コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「モジュール:削除依頼ログ/sandbox」の版間の差分

削除された内容 追加された内容
編集の要約なし
 
(同じ利用者による、間の40版が非表示)
1行目: 1行目:
local yesno = require('Module:Yesno')
local yesno = require('Module:Yesno')

local mMessageBox = require('Module:Message box')
---Check whether an array includes a certain value
local TEMPLATE_PAGE = 'Template:Old XfD multi'
---@param array table
---@param value any
---@return boolean
local function includes(array, value)
for _, v in ipairs(array) do
if v == value then
return true
end
end
return false
end

---Push a value into an array if the array doesn't already have the value
---@param array table
---@param value any
local function push(array, value)
if not includes(array, value) then
table.insert(array, value)
end
end


-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Helper functions
-- AfDLog class
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------


local function exists(page)
local AfDLog = {}
AfDLog.__index = AfDLog
local success, exists = pcall(function ()
local title = mw.title.new(page)
return title.exists
end)
return success and exists
end


---Create a new AfDLog object instance.
local function getAfdPage(page)
---@param args table
if page and mw.title.new(page) then
---@return AfDLog
if mw.title.new(page).namespace ~= 0 then
function AfDLog.new(args)
return page
else
return 'Wikipedia:Articles for deletion/' .. page
end
else return nil
end
end


local function getVfdPage(page)
local self = setmetatable({}, AfDLog)
if page and mw.title.new(page) then
if mw.title.new(page).rootPageTitle.fullText == 'Wikipedia:Votes for deletion' then
return page
else
return 'Wikipedia:Votes for deletion/' .. page
end
else return nil
end
end


-- Set class properties
local function makeWikilink(page, display)
display = display or 'discussion'
if page then
return string.format('[[%s|%s]]', page, display)
else
return display --probably a bad title
end
end


self.talk = not not yesno(args.talk) -- Whether the log is for the talk page
local function makeUrlLink(page, display)
self.collapse = tonumber(args.collapse) or not not yesno(args.collapse) -- Whether to collapse some data rows
display = display or 'discussion'
self.numbered = true --yesno(args.numbered) == nil or not not yesno(args.numbered) -- Disabled parameter
return string.format('[%s %s]', page, display)
self.reversed = yesno(args.reversed) == nil or not not yesno(args.reversed) -- Whether to reverse the order of data rows
end
self.currentTitle = mw.title.getCurrentTitle() -- The title object for the current page
self.omitCat = (function() -- Whether to omit error categories
local patterns = {
'^Template:(削除依頼ログ)$',
'^Template:(削除依頼ログ/.+)$',
'^Template:(削除済みノート[23]?)$',
'^Template:(削除済みノート[23]?/.+)$',
'^Template:(不削除ノート)$',
'^Template:(不削除ノート/.+)$',
'^Template:(特定版削除済みノート)$',
'^Template:(特定版削除済みノート/.+)$',
'^Template:(削除済みノートページ2?)$',
'^Template:(削除済みノートページ2?/.+)$'
}
for _, pat in ipairs(patterns) do
if string.find(self.currentTitle.prefixedText, pat) then
return true
end
end
return false
end)()
self.pageList = {} -- Keyed by pagetitles and valued by boolean values (true for existing pages)
self.errors = { -- Used to add error categories
invalidPage = false, -- True if some page doesn't exist
invalidDate = false, -- True if some date parameter has an unsupported format
badParameters = {}, -- An array of names of undefined parameter keys
cats = {} -- An array of error categories (`%s` in `Category:削除依頼ログエラー/%s`)
}
self._suppressPageError = args._suppressPageError == 'true' -- Private parameter
self.pageType = 'ページ' -- 「この`pageType`は過去に削除依頼の...」
self.rowData = {} -- An array of objects to render row data


-- Get page type
local function pageTypeName(title)
if self.talk then
local display = mw.ustring.lower(title.subjectNsText)
self.pageType = 'ノート'
local pageTypes = {
else
[''] = 'article',
local subjTitle = self.currentTitle.subjectPageTitle
['user'] = 'user page',
local pageTypes = {
['wikipedia'] = 'project page',
[0] = '記事',
['mediawiki'] = 'interface page',
[2] = '利用者ページ',
['help'] = 'help page'
[4] = 'プロジェクトページ',
}
[6] = 'ファイル',
if pageTypes[display] then display = pageTypes[display] end
[8] = 'インターフェースページ',
return display
[10] = 'テンプレート',
end
[12] = 'ヘルプページ',
[14] = 'カテゴリ',
[100] = 'ポータル',
[102] = 'プロジェクト',
[828] = 'モジュール'
}
if pageTypes[subjTitle.namespace] then
self.pageType = pageTypes[subjTitle.namespace]
end
end


-- Process data paremeters (result, page, date)
local function cleanupTitle(title)
if not title then return title end
title = mw.uri.decode(title, 'PATH')
title = string.gsub(title, '|.*', '')
title = string.gsub(title, '[%[%]{}]', '')
return title
end


---@param date string
-------------------------------------------------------------------------------
---@return string|nil YYYY年MM月DD日 Nil if the input date has an unsupported format
-- OldAfdMulti class
local function formatDate(date)
-------------------------------------------------------------------------------
local patterns = {
'^(%d%d%d%d)年(%d%d?)月(%d%d?)日$',
'^(%d%d%d%d)年(%d%d?)月$',
'^(%d%d%d%d)%-(%d%d)%-(%d%d)T%d%d:%d%d:%d%dZ?$',
'^(%d%d%d%d)%-(%d%d)%-(%d%d)$',
'^(%d%d%d%d)%-(%d%d)$'
}
for _, pat in ipairs(patterns) do
local y, m, d = string.match(date, pat)
if y and m then
return string.format('%s%s%s',
y:gsub('^0', '') .. '年',
m:gsub('^0', '') .. '月',
d and d:gsub('^0', '') .. '日' or ''
)
end
end
return nil
end


---@param prefix "result" | "page" | "fullpage" | "display" | "date" | "note"
local OldAfdMulti = {}
---@param num number
OldAfdMulti.__index = OldAfdMulti
---@param val string
local function setRowData(prefix, num, val)
if not self.rowData[num] then -- Set default data
self.rowData[num] = {
result = '削除',
page = 'Wikipedia:削除依頼/', -- No automatic substitution: Causes problems if the page is moved afterwards
fullpage = nil,
display = nil,
date = nil,
note = nil
}
end
if prefix == 'page' then
-- Remove leading/trailing brackets and spaces
-- Note: 'page' is a subpage name and leading spaces shouldn't be removed (XXX/A and XXX/_A are different)
local m = string.match(val, '^%[*(.-)[%s_%]]*$')
if m then
val = 'Wikipedia:削除依頼/' .. m
end
elseif prefix == 'fullpage' then
-- Remove leading/trailing brackets and spaces
local m = string.match(val, '^[%s_%[]*(.-)[%s_%]]*$')
if m then
val = m
end
elseif prefix == 'date' then
local date = formatDate(val)
if date then
val = date
else
self.errors.invalidDate = true
return
end
end
self.rowData[num][prefix] = val -- Set the specified data
end


-- List of accepted parameter names; true for ordered parameters and false for unordered ones
function OldAfdMulti.new(args)
local acceptedPrefixes = {
local self = setmetatable({}, OldAfdMulti)
result = true,
self.currentTitle = mw.title.getCurrentTitle()
page = true,
fullpage = true,
display = true,
date = true,
note = true,
talk = false,
collapse = false,
reversed = false,
_suppresspageerror = false, -- Private parameter
-- numbered = false -- Disabled parameter
}


-- Preprocess the row args for easier looping.
self.rowData = {}
for k, v in pairs(args) do
for k, v in pairs(args) do
local key = mw.ustring.lower(k)
if type(k) == 'string' then
local prefix, num = k:match('^(.-)([1-9][0-9]*)$')
local prefix, num = key:match('^(.-)(%d*)$')
if prefix and num then
if prefix and acceptedPrefixes[prefix] ~= nil and num ~= '0' then
num = tonumber(num)
num = tonumber(num)
if prefix == 'result' or
if acceptedPrefixes[prefix] == true and num then
setRowData(prefix, num, v)
prefix == 'date' or
elseif acceptedPrefixes[prefix] == false and not num then
prefix == 'page' or
-- Do nothing
prefix == 'link' or
else
prefix == 'caption' or
push(self.errors.badParameters, k)
prefix == 'votepage' or
end
prefix == 'merge'
else
then
push(self.errors.badParameters, k)
self.rowData[num] = self.rowData[num] or {}
end
self.rowData[num][prefix] = v
if v and v ~= '' and prefix=='merge' then
self.isMerge = true
end
end
end
end
end
end

-- Set aliases for parameters ending in "1".
---Remove any gaps in the array we made.
if self.rowData[1] then
---@param t table
self.rowData[1].result = self.rowData[1].result or args.result
---@return table
self.rowData[1].date = self.rowData[1].date or args.date
self.rowData[1].page = self.rowData[1].page or args.page
self.rowData[1].votepage = self.rowData[1].votepage or args.votepage
self.rowData[1].link = self.rowData[1].link or args.link
self.rowData[1].caption = self.rowData[1].caption or args.caption
self.rowData[1].merge = self.rowData[1].merge or args.merge
elseif args.result or
args.date or
args.page or
args.votepage or
args.link or
args.caption or
args.merge
then
self.rowData[1] = {
result = args.result,
date = args.date,
page = args.page,
votepage = args.votepage,
link = args.link,
caption = args.caption,
merge = args.merge
}
end
-- Remove any gaps in the array we made.
local function compressSparseArray(t)
local function compressSparseArray(t)
local ret, nums = {}, {}
local ret, nums = {}, {}
for num, data in pairs(t) do
for num, _ in pairs(t) do
nums[#nums + 1] = num
nums[#nums + 1] = num
end
end
147行目: 209行目:
end
end
self.rowData = compressSparseArray(self.rowData)
self.rowData = compressSparseArray(self.rowData)
-- Set aliases that apply to all of the data tables.
for i, data in ipairs(self.rowData) do
data.page = data.page or data.votepage
data.page = cleanupTitle(data.page)
data.votepage = nil
end


-- Set collapsedness
self.collapse = tonumber(args.collapse)
if not self.collapse then
self.collapse = yesno(args.collapse)
end

-- Set other properties
self.isNumbered = yesno(args.numbered)
self.isSmall = yesno(args.small)
self.pageType = args.type or pageTypeName(self.currentTitle)
if args.merge and args.merge ~= '' then
self.isMerge = true
end
self.deletion = args.deletion
return self
return self
end


function OldAfdMulti:renderResult(result)
return result or "'''Keep'''"
end
end


---Render the AfDLog instance as a message box.
function OldAfdMulti:renderDate(date)
---@return string
if date then
function AfDLog:renderBox()
return date
return require('Module:Message box').main('tmbox', {
else
type = 'notice',
self.hasMissingDate = true
image = '[[File:Clipboard.svg|35px|削除依頼]]',
return string.format(
text = self:renderBoxText()
'<sup>%s[[%s|date missing]]%s</sup>',
})
mw.text.nowiki('['),
TEMPLATE_PAGE,
mw.text.nowiki(']')
)
end
end
end


function AfDLog:renderBoxText()
function OldAfdMulti:renderPageText(linkFunc, page, caption)
return string.format(', see %s.', linkFunc(page, caption))
end


local nRows = #self.rowData
function OldAfdMulti:renderRow(result, date, link, merge)
local result = self:renderResult(result)
local ret = {}
local date = self:renderDate(date)
local mergeText = ''
if merge and merge ~= '' then
mergeText = string.format('Merge with [[:%s]]: ', merge)
end
if link then
return string.format('%s%s, %s, see %s.', mergeText, result, date, link)
else
return string.format('%s%s, %s', mergeText, result, date)
end
end


if nRows == 1 and self.rowData[1].date then
function OldAfdMulti:renderFirstRow(data)
ret[#ret + 1] = string.format(
local link
'この%sは%sに[[Wikipedia:削除依頼|削除依頼]]の審議対象になりました。',
if data.link then
self.pageType,
link = makeUrlLink(data.link, data.caption)
self.rowData[1].date
else
)
local page = data.page or self.currentTitle.text
else
link = makeWikilink(getAfdPage(page), data.caption)
ret[#ret + 1] = string.format(
'この%sは過去に[[Wikipedia:削除依頼|削除依頼]]の審議対象になりました。',
self.pageType
)
end
if nRows > 1 then
ret[#ret + 1] = '新しく依頼を提出する場合、以下を参考にしてください。\n'
ret[#ret + 1] = self:renderMultipleRows()
elseif nRows == 1 then
ret[#ret + 1] = self:renderFirstRow()
else -- There's no rowData
-- Do nothing
end
end
return self:renderRow(data.result, data.date, link, data.merge)
end


ret[#ret + 1] = self:renderErrors()
function OldAfdMulti:renderSubsequentRow(data)

local link
return table.concat(ret)
if data.page then

link = makeWikilink(getAfdPage(data.page), data.caption)
elseif data.link then
link = makeUrlLink(data.link, data.caption)
end
return self:renderRow(data.result, data.date, link, data.merge)
end
end


function OldAfdMulti:renderRows()
function AfDLog:renderMultipleRows()

local root = mw.html.create()
local root = mw.html.create()
local nRows = #self.rowData
local nRows = #self.rowData
local i = nRows
local i = self.reversed and nRows or 1


local nCollapsedRows
local nCollapsedRows
259行目: 287行目:
:tag('tr')
:tag('tr')
:tag('td')
:tag('td')
:tag(self.isNumbered and 'ol' or 'ul')
:tag(self.numbered and 'ol' or 'ul')
end

local function renderRow(html, method, data)
html
:tag('li')
:attr('value', self.isNumbered and i or nil)
:wikitext(self[method](self, data))
end
end


272行目: 293行目:
if hasNormalRows then
if hasNormalRows then
local normalList = makeList(false)
local normalList = makeList(false)
if self.reversed then
while i > 1 and i > nCollapsedRows do
while i >= 1 and i > nCollapsedRows do
renderRow(normalList, 'renderSubsequentRow', self.rowData[i])
self:renderRow(i, normalList)
i = i - 1
i = i - 1
end
end
if i == 1 and i > nCollapsedRows then
else
renderRow(normalList, 'renderFirstRow', self.rowData[i])
while i <= nRows - nCollapsedRows do
i = i - 1
self:renderRow(i, normalList)
end
i = i + 1
end
end
end
end


286行目: 310行目:
local header
local header
if hasNormalRows then
if hasNormalRows then
header = 'Older deletion discussions:'
header = '過去の削除依頼:'
elseif nRows > 1 then
header = 'Deletion discussions:'
else
else
header = 'Deletion discussion:'
header = '削除依頼:'
end
end
local collapsedList = makeList(true, header)
local collapsedList = makeList(true, header)
if self.reversed then
while i > 1 do
while i >= 1 do
renderRow(collapsedList, 'renderSubsequentRow', self.rowData[i])
self:renderRow(i, collapsedList)
i = i - 1
i = i - 1
end
end
renderRow(collapsedList, 'renderFirstRow', self.rowData[i])
else
while i <= nRows do
self:renderRow(i, collapsedList)
i = i + 1
end
end
end
end


return tostring(root)
return tostring(root)

end
end


function OldAfdMulti:renderFirstRowOnly()
function AfDLog:renderRow(rowNum, html)
local data = self.rowData[1] or {}
local data = self.rowData[rowNum]
local link = self:addPage(data)
local caption = data.caption or 'the discussion'
local link
local wkt
if data.link then
if data.date and data.note then
wkt = string.format('<b>%s</b> %s (%s; %s)', data.result, link, data.date, data.note)
link = makeUrlLink(data.link, caption)
elseif data.date or data.note then
else
wkt = string.format('<b>%s</b> %s (%s)', data.result, link, data.date or data.note)
local page = data.page or self.currentTitle.text
else
if exists(getAfdPage(page)) then
wkt = string.format('<b>%s</b> %s', data.result, link)
link = makeWikilink(getAfdPage(page), caption)
end
elseif exists(getVfdPage(page)) then
html
link = makeWikilink(getVfdPage(page), caption)
:tag('li')
else
:attr('value', self.numbered and rowNum or nil)
link = caption -- Make this an error?
:wikitext(wkt)
end
end
local result = self:renderResult(data.result or "'''keep'''")
return string.format(
'The result of %s was %s.',
link, result
)
end
end


function OldAfdMulti:renderBannerText()
function AfDLog:renderFirstRow()
local nRows = #self.rowData
local data = self.rowData[1]
local ret = {}
local ret = {}
if self.deletion then
if data.fullpage then
ret[#ret + 1] = string.format(
if nRows < 1 or not self.rowData[1].date then
'詳細は%sをご覧ください。',
ret[#ret + 1] = string.format(
self:addPage(data)
'This %s was previously nominated for %s.',
)
self.pageType,
else
self.deletion
ret[#ret + 1] = string.format(
)
'%sの結果、<b>%s</b>となりました。',
elseif nRows == 1 and self.rowData[1].date then
self:addPage(data, '議論'),
ret[#ret + 1] = string.format(
data.result
'This %s was nominated for %s on %s.',
)
self.pageType,
end
self.deletion,
if data.note then
self.rowData[1].date
ret[#ret + 1] = string.format('(%s)', data.note)
)
end
else
return table.concat(ret)
ret[#ret + 1] = string.format(
'This %s was nominated for %s.',
self.pageType,
self.deletion
)
end
elseif self.isMerge then
if nRows < 1 or not self.rowData[1].date then
ret[#ret + 1] = string.format(
'This %s was considered for [[Wikipedia:Deletion policy#Merging|merging]] with %s.',
self.pageType,
self.rowData[1].merge
)
elseif nRows == 1 and self.rowData[1].date then
ret[#ret + 1] = string.format(
'This %s was considered for [[Wikipedia:Deletion policy#Merging|merging]] with [[:%s]] on %s.',
self.pageType,
self.rowData[1].merge,
self.rowData[1].date
)
else
ret[#ret + 1] = string.format(
'This %s was nominated for [[Wikipedia:Deletion policy|deletion]] or considered for [[Wikipedia:Deletion policy#Merging|merging]].',
self.pageType
)
end
else
if nRows < 1 or not self.rowData[1].date then
ret[#ret + 1] = string.format(
'This %s was previously nominated for [[Wikipedia:Deletion policy|deletion]].',
self.pageType
)
elseif nRows == 1 and self.rowData[1].date then
ret[#ret + 1] = string.format(
'This %s was nominated for [[Wikipedia:Deletion policy|deletion]] on %s.',
self.pageType,
self.rowData[1].date
)
else
ret[#ret + 1] = string.format(
'This %s was nominated for [[Wikipedia:Deletion policy|deletion]].',
self.pageType
)
end
end
if nRows > 1 then
ret[#ret + 1] = ' '
if self.isSmall then
ret[#ret + 1] = 'Review prior discussions if considering re-nomination:'
else
ret[#ret + 1] = 'Please review the prior discussions if you are considering re-nomination:'
end
ret[#ret + 1] = '\n'
ret[#ret + 1] = self:renderRows()
else
ret[#ret + 1] = ' '
ret[#ret + 1] = self:renderFirstRowOnly()
end
return table.concat(ret)
end
end


---Clean up pagetitle, check its existence, and set the 'invalidPage' parameter to true if there's a problem.
function OldAfdMulti:renderBanner()
---@param data table rowData object
return mMessageBox.main('tmbox', {
---@param caption? string Priority: caption > data.display > data.fullpage > data.page (default)
small = self.isSmall,
---@return string wikilink
type = 'notice',
function AfDLog:addPage(data, caption)
image = '[[File:Clipboard.svg|35px|Articles for deletion]]',
caption = string.gsub(caption or data.display or data.fullpage or data.page, '_', ' ')
smallimage = 'none',
local key = data.fullpage and 'fullpage' or 'page' -- Override 'page' by 'fullpage' if the latter is specified
text = self:renderBannerText()
local title = mw.title.new(data[key])
})
local isValidPage = title and data[key] ~= 'Wikipedia:削除依頼/'
if isValidPage then
data[key] = title.prefixedText
if self.pageList[data[key]] == nil and not self._suppressPageError then
self.pageList[data[key]] = title.exists
else
self.pageList[data[key]] = true
end
else
self.pageList[data[key]] = false
end
if not self.errors.invalidPage and not self.pageList[data[key]] then
self.errors.invalidPage = true
end
if isValidPage then
return string.format(
'[[:%s|%s]]',
title.prefixedText .. (title.fragment ~= '' and ('#' .. title.fragment) or ''),
caption
)
else
return string.format(
'%s%s%s',
mw.text.nowiki('[['),
data[key],
mw.text.nowiki(']]')
)
end
end
end


function OldAfdMulti:renderTrackingCategories()
function AfDLog:renderErrors()

local ret = {}
local function errorMessage(code)
if self.hasMissingDate and self.currentTitle.isTalkPage then
return string.format('<strong class="error" style="display:block;">エラー: %s</strong>', code)
ret[#ret + 1] = '[[Category:Old XfD multi templates with errors]]'
end
end

local ret = {}
if not self.rowData[1] then
ret[#ret + 1] = errorMessage('必須引数が指定されていません')
self:addCategory('引数未指定')
end
if #self.errors.badParameters > 0 then
ret[#ret + 1] = errorMessage(string.format(
'不正な引数が指定されています (%s)',
table.concat(self.errors.badParameters, ', ')
))
self:addCategory('不正な引数')
end
if self.errors.invalidPage then
ret[#ret + 1] = errorMessage('不正なページ名が指定されています')
self:addCategory('不正なページ名')
end
if self.errors.invalidDate then
self:addCategory('不正なdate引数')
end

ret[#ret + 1] = self:renderCategories()

return table.concat(ret)

end

function AfDLog:addCategory(subcat)
push(self.errors.cats, subcat)
end

function AfDLog:renderCategories()
if self.omitCat then
return ''
end
local ret = {}
for _, subcat in ipairs(self.errors.cats) do
ret[#ret + 1] = string.format('[[Category:削除依頼ログエラー/%s]]', subcat)
end
if #ret > 0 then
ret[#ret + 1] = '[[Category:削除依頼ログエラー]]'
end
return table.concat(ret)
return table.concat(ret)
end
end


---@param rowNum? number If given, return a row, not a template
function OldAfdMulti:__tostring()
---@return string
return self:renderBanner() .. self:renderTrackingCategories()
function AfDLog:renderRawTemplate(rowNum)

local ret = {}
local function pushIfRowNumIsNil(str)
if not rowNum then
table.insert(ret, str)
end
end

pushIfRowNumIsNil('{{削除依頼ログ/sandbox')
for i, data in ipairs(self.rowData) do
pushIfRowNumIsNil('\n')
ret[#ret + 1] = string.format('|result%s=%s', rowNum or i, data.result)
if data.fullpage then
ret[#ret + 1] = string.format('|fullpage%s=%s', rowNum or i, data.fullpage)
else
ret[#ret + 1] = string.format('|page%s=%s', rowNum or i, string.gsub(data.page, '^Wikipedia:削除依頼/', ''))
end
if data.display then
ret[#ret + 1] = string.format('|display%s=%s', rowNum or i, data.display)
end
ret[#ret + 1] = string.format('|date%s=%s', rowNum or i, data.date or '')
if data.note then
ret[#ret + 1] = string.format('|note%s=%s', rowNum or i, data.note)
end
end
pushIfRowNumIsNil('\n|talk=' .. tostring(self.talk))
-- pushIfRowNumIsNil('\n|collapse=' .. tostring(self.collapse))
-- pushIfRowNumIsNil('\n|numbered=' .. tostring(self.numbered))
pushIfRowNumIsNil('\n}}')

return table.concat(ret)

end
end


435行目: 502行目:


function p._main(args)
function p._main(args)

local afd = OldAfdMulti.new(args)
local afd = AfDLog.new(args)
return tostring(afd)

local caller = mw.getCurrentFrame():getParent():getTitle()
local substPatterns = { -- Templates that must be transcluded with `subst:`
'^Template:(削除済みノート[23]?)$',
'^Template:(削除済みノート[23]?/.+)$',
'^Template:(不削除ノート)$',
'^Template:(不削除ノート/.+)$',
'^Template:(特定版削除済みノート)$',
'^Template:(特定版削除済みノート/.+)$',
'^Template:(削除済みノートページ2?)$',
'^Template:(削除済みノートページ2?/.+)$'
}
local callerTitle
for _, pat in ipairs(substPatterns) do
callerTitle = caller:match(pat)
if callerTitle then break end
end

if mw.isSubsting() then
local rowNum
if args.n then
rowNum = string.match(args.n, '^(%d+)$') or 2
end
return afd:renderRawTemplate(rowNum)
elseif callerTitle then
local ret = {}
ret[#ret + 1] = '<strong class="error">エラー: '
ret[#ret + 1] = '[[Help:テンプレート#テンプレートの内容で置き換える|subst:]] がありません。'
ret[#ret + 1] = string.format('「%s」ではなく「subst:%s」としてください。', callerTitle, callerTitle)
ret[#ret + 1] = '</strong>'
afd:addCategory('subst違反')
ret[#ret + 1] = afd:renderCategories()
return table.concat(ret)
else
return afd:renderBox()
end

end
end


function p.main(frame)
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame, {
local args = require('Module:Arguments').getArgs(frame, {
wrappers = TEMPLATE_PAGE
wrappers = {
'Template:削除依頼ログ'
}
})
})
return p._main(args)
return p._main(args)

2023年8月16日 (水) 20:32時点における最新版

モジュールの解説[作成]
local yesno = require('Module:Yesno')

---Check whether an array includes a certain value
---@param array table
---@param value any
---@return boolean
local function includes(array, value)
    for _, v in ipairs(array) do
        if v == value then
            return true
        end
    end
    return false
end

---Push a value into an array if the array doesn't already have the value
---@param array table
---@param value any
local function push(array, value)
    if not includes(array, value) then
        table.insert(array, value)
    end
end

-------------------------------------------------------------------------------
-- AfDLog class
-------------------------------------------------------------------------------

local AfDLog = {}
AfDLog.__index = AfDLog

---Create a new AfDLog object instance.
---@param args table
---@return AfDLog
function AfDLog.new(args)

	local self = setmetatable({}, AfDLog)

    -- Set class properties

    self.talk = not not yesno(args.talk) -- Whether the log is for the talk page
    self.collapse = tonumber(args.collapse) or not not yesno(args.collapse) -- Whether to collapse some data rows
    self.numbered = true --yesno(args.numbered) == nil or not not yesno(args.numbered) -- Disabled parameter
    self.reversed = yesno(args.reversed) == nil or not not yesno(args.reversed) -- Whether to reverse the order of data rows
    self.currentTitle = mw.title.getCurrentTitle() -- The title object for the current page
    self.omitCat = (function() -- Whether to omit error categories
        local patterns = {
            '^Template:(削除依頼ログ)$',
            '^Template:(削除依頼ログ/.+)$',
            '^Template:(削除済みノート[23]?)$',
            '^Template:(削除済みノート[23]?/.+)$',
            '^Template:(不削除ノート)$',
            '^Template:(不削除ノート/.+)$',
            '^Template:(特定版削除済みノート)$',
            '^Template:(特定版削除済みノート/.+)$',
            '^Template:(削除済みノートページ2?)$',
            '^Template:(削除済みノートページ2?/.+)$'
        }
        for _, pat in ipairs(patterns) do
            if string.find(self.currentTitle.prefixedText, pat) then
                return true
            end
        end
        return false
    end)()
    self.pageList = {} -- Keyed by pagetitles and valued by boolean values (true for existing pages)
    self.errors = { -- Used to add error categories
        invalidPage = false, -- True if some page doesn't exist
        invalidDate = false, -- True if some date parameter has an unsupported format
        badParameters = {}, -- An array of names of undefined parameter keys
        cats = {} -- An array of error categories (`%s` in `Category:削除依頼ログエラー/%s`)
    }
    self._suppressPageError = args._suppressPageError == 'true' -- Private parameter
    self.pageType = 'ページ' -- 「この`pageType`は過去に削除依頼の...」
    self.rowData = {} -- An array of objects to render row data

    -- Get page type
    if self.talk then
        self.pageType = 'ノート'
    else
        local subjTitle = self.currentTitle.subjectPageTitle
        local pageTypes = {
            [0] = '記事',
            [2] = '利用者ページ',
            [4] = 'プロジェクトページ',
            [6] = 'ファイル',
            [8] = 'インターフェースページ',
            [10] = 'テンプレート',
            [12] = 'ヘルプページ',
            [14] = 'カテゴリ',
            [100] = 'ポータル',
            [102] = 'プロジェクト',
            [828] = 'モジュール'
        }
        if pageTypes[subjTitle.namespace] then
            self.pageType = pageTypes[subjTitle.namespace]
        end
    end

	-- Process data paremeters (result, page, date)

    ---@param date string
    ---@return string|nil YYYY年MM月DD日 Nil if the input date has an unsupported format
    local function formatDate(date)
        local patterns = {
            '^(%d%d%d%d)年(%d%d?)月(%d%d?)日$',
            '^(%d%d%d%d)年(%d%d?)月$',
            '^(%d%d%d%d)%-(%d%d)%-(%d%d)T%d%d:%d%d:%d%dZ?$',
            '^(%d%d%d%d)%-(%d%d)%-(%d%d)$',
            '^(%d%d%d%d)%-(%d%d)$'
        }
        for _, pat in ipairs(patterns) do
            local y, m, d = string.match(date, pat)
            if y and m then
                return string.format('%s%s%s', 
                    y:gsub('^0', '') .. '年',
                    m:gsub('^0', '') .. '月',
                    d and d:gsub('^0', '') .. '日' or ''
                )
            end
        end
        return nil
    end

    ---@param prefix "result" | "page" | "fullpage" | "display" | "date" | "note"
    ---@param num number
    ---@param val string
    local function setRowData(prefix, num, val)
        if not self.rowData[num] then -- Set default data
            self.rowData[num] = {
                result = '削除',
                page = 'Wikipedia:削除依頼/', -- No automatic substitution: Causes problems if the page is moved afterwards
                fullpage = nil,
                display = nil,
                date = nil,
                note = nil
            }
        end
        if prefix == 'page' then
            -- Remove leading/trailing brackets and spaces
            -- Note: 'page' is a subpage name and leading spaces shouldn't be removed (XXX/A and XXX/_A are different)
            local m = string.match(val, '^%[*(.-)[%s_%]]*$')
            if m then
                val = 'Wikipedia:削除依頼/' .. m
            end
        elseif prefix == 'fullpage' then
            -- Remove leading/trailing brackets and spaces
            local m = string.match(val, '^[%s_%[]*(.-)[%s_%]]*$')
            if m then
                val = m
            end
        elseif prefix == 'date' then
            local date = formatDate(val)
            if date then
                val = date
            else
                self.errors.invalidDate = true
                return
            end
        end
        self.rowData[num][prefix] = val -- Set the specified data
    end

    -- List of accepted parameter names; true for ordered parameters and false for unordered ones
    local acceptedPrefixes = {
        result = true,
        page = true,
        fullpage = true,
        display = true,
        date = true,
        note = true,
        talk = false,
        collapse = false,
        reversed = false,
        _suppresspageerror = false, -- Private parameter
        -- numbered = false -- Disabled parameter
    }

	for k, v in pairs(args) do
        local key = mw.ustring.lower(k)
        local prefix, num = key:match('^(.-)(%d*)$')
        if prefix and acceptedPrefixes[prefix] ~= nil and num ~= '0' then
            num = tonumber(num)
            if acceptedPrefixes[prefix] == true and num then
                setRowData(prefix, num, v)
            elseif acceptedPrefixes[prefix] == false and not num then
                -- Do nothing
            else
                push(self.errors.badParameters, k)
            end
        else
            push(self.errors.badParameters, k)
        end
	end

	---Remove any gaps in the array we made.
    ---@param t table
    ---@return table
	local function compressSparseArray(t)
		local ret, nums = {}, {}
		for num, _ in pairs(t) do
			nums[#nums + 1] = num
		end
		table.sort(nums)
		for i, num in ipairs(nums) do
			ret[i] = t[num]
		end
		return ret
	end
	self.rowData = compressSparseArray(self.rowData)

	return self

end

---Render the AfDLog instance as a message box.
---@return string
function AfDLog:renderBox()
	return require('Module:Message box').main('tmbox', {
		type = 'notice',
		image = '[[File:Clipboard.svg|35px|削除依頼]]',
		text = self:renderBoxText()
	})
end

function AfDLog:renderBoxText()

	local nRows = #self.rowData
	local ret = {}

    if nRows == 1 and self.rowData[1].date then
        ret[#ret + 1] = string.format(
            'この%sは%sに[[Wikipedia:削除依頼|削除依頼]]の審議対象になりました。',
            self.pageType,
            self.rowData[1].date
        )
    else
        ret[#ret + 1] = string.format(
            'この%sは過去に[[Wikipedia:削除依頼|削除依頼]]の審議対象になりました。',
            self.pageType
        )
    end
	if nRows > 1 then
        ret[#ret + 1] = '新しく依頼を提出する場合、以下を参考にしてください。\n'
        ret[#ret + 1] = self:renderMultipleRows()
    elseif nRows == 1 then
		ret[#ret + 1] = self:renderFirstRow()
    else -- There's no rowData
        -- Do nothing
	end

    ret[#ret + 1] = self:renderErrors()

	return table.concat(ret)

end

function AfDLog:renderMultipleRows()

	local root = mw.html.create()
	local nRows = #self.rowData
	local i = self.reversed and nRows or 1

	local nCollapsedRows
	if type(self.collapse) == 'number' then
		nCollapsedRows = self.collapse
	elseif self.collapse then
		nCollapsedRows = nRows
	else
		nCollapsedRows = 0
	end
	local hasNormalRows = nRows - nCollapsedRows > 0

	local function makeList(isCollapsed, header)
		local tableRoot = root:tag('table')
		tableRoot
			:addClass(isCollapsed and 'mw-collapsible mw-collapsed' or nil)
			:css('width', '100%')
			:css('background-color', '#f8eaba')
		if header then
			tableRoot
				:tag('tr')
					:tag('th')
						:wikitext(header)
		end
		return tableRoot
			:tag('tr')
				:tag('td')
					:tag(self.numbered and 'ol' or 'ul')
	end

	-- Render normal rows
	if hasNormalRows then
		local normalList = makeList(false)
        if self.reversed then
            while i >= 1 and i > nCollapsedRows do
                self:renderRow(i, normalList)
                i = i - 1
            end
        else
            while i <= nRows - nCollapsedRows do
                self:renderRow(i, normalList)
                i = i + 1
            end
        end
	end

	-- Render collapsed rows
	if nCollapsedRows > 0 then
		local header
		if hasNormalRows then
			header = '過去の削除依頼:'
		else
			header = '削除依頼:'
		end
		local collapsedList = makeList(true, header)
        if self.reversed then
            while i >= 1 do
                self:renderRow(i, collapsedList)
                i = i - 1
            end
        else
            while i <= nRows do
                self:renderRow(i, collapsedList)
                i = i + 1
            end
        end
	end

	return tostring(root)

end

function AfDLog:renderRow(rowNum, html)
    local data = self.rowData[rowNum]
    local link = self:addPage(data)
    local wkt
    if data.date and data.note then
        wkt = string.format('<b>%s</b> %s (%s; %s)', data.result, link, data.date, data.note)
    elseif data.date or data.note then
        wkt = string.format('<b>%s</b> %s (%s)', data.result, link, data.date or data.note)
    else
        wkt = string.format('<b>%s</b> %s', data.result, link)
    end
    html
        :tag('li')
            :attr('value', self.numbered and rowNum or nil)
            :wikitext(wkt)
end

function AfDLog:renderFirstRow()
	local data = self.rowData[1]
    local ret = {}
    if data.fullpage then
        ret[#ret + 1] = string.format(
            '詳細は%sをご覧ください。',
            self:addPage(data)
        )
    else
        ret[#ret + 1] = string.format(
            '%sの結果、<b>%s</b>となりました。',
            self:addPage(data, '議論'),
            data.result
        )
    end
    if data.note then
        ret[#ret + 1] = string.format('(%s)', data.note)
    end
    return table.concat(ret)
end

---Clean up pagetitle, check its existence, and set the 'invalidPage' parameter to true if there's a problem.
---@param data table rowData object
---@param caption? string Priority: caption > data.display > data.fullpage > data.page (default)
---@return string wikilink
function AfDLog:addPage(data, caption)
    caption = string.gsub(caption or data.display or data.fullpage or data.page, '_', ' ')
    local key = data.fullpage and 'fullpage' or 'page' -- Override 'page' by 'fullpage' if the latter is specified
    local title = mw.title.new(data[key])
    local isValidPage = title and data[key] ~= 'Wikipedia:削除依頼/'
    if isValidPage then
        data[key] = title.prefixedText
        if self.pageList[data[key]] == nil and not self._suppressPageError then
            self.pageList[data[key]] = title.exists
        else
            self.pageList[data[key]] = true
        end
    else
        self.pageList[data[key]] = false
    end
    if not self.errors.invalidPage and not self.pageList[data[key]] then
        self.errors.invalidPage = true
    end
    if isValidPage then
        return string.format(
            '[[:%s|%s]]',
            title.prefixedText .. (title.fragment ~= '' and ('#' .. title.fragment) or ''),
            caption
        )
    else
        return string.format(
            '%s%s%s',
            mw.text.nowiki('[['),
            data[key],
            mw.text.nowiki(']]')
        )
    end
end

function AfDLog:renderErrors()

    local function errorMessage(code)
        return string.format('<strong class="error" style="display:block;">エラー: %s</strong>', code)
    end

    local ret = {}
    if not self.rowData[1] then
        ret[#ret + 1] = errorMessage('必須引数が指定されていません')
        self:addCategory('引数未指定')
    end
    if #self.errors.badParameters > 0 then
        ret[#ret + 1] = errorMessage(string.format(
            '不正な引数が指定されています (%s)',
            table.concat(self.errors.badParameters, ', ')
        ))
        self:addCategory('不正な引数')
    end
    if self.errors.invalidPage then
        ret[#ret + 1] = errorMessage('不正なページ名が指定されています')
        self:addCategory('不正なページ名')
    end
    if self.errors.invalidDate then
        self:addCategory('不正なdate引数')
    end

    ret[#ret + 1] = self:renderCategories()

    return table.concat(ret)

end

function AfDLog:addCategory(subcat)
    push(self.errors.cats, subcat)
end

function AfDLog:renderCategories()
    if self.omitCat then
        return ''
    end
    local ret = {}
    for _, subcat in ipairs(self.errors.cats) do
        ret[#ret + 1] = string.format('[[Category:削除依頼ログエラー/%s]]', subcat)
    end
    if #ret > 0 then
        ret[#ret + 1] = '[[Category:削除依頼ログエラー]]'
    end
	return table.concat(ret)
end

---@param rowNum? number If given, return a row, not a template
---@return string
function AfDLog:renderRawTemplate(rowNum)

    local ret = {}
    local function pushIfRowNumIsNil(str)
        if not rowNum then
            table.insert(ret, str)
        end
    end

    pushIfRowNumIsNil('{{削除依頼ログ/sandbox')
    for i, data in ipairs(self.rowData) do
        pushIfRowNumIsNil('\n')
        ret[#ret + 1] = string.format('|result%s=%s', rowNum or i, data.result)
        if data.fullpage then
            ret[#ret + 1] = string.format('|fullpage%s=%s', rowNum or i, data.fullpage)
        else
            ret[#ret + 1] = string.format('|page%s=%s', rowNum or i, string.gsub(data.page, '^Wikipedia:削除依頼/', ''))
        end
        if data.display then
            ret[#ret + 1] = string.format('|display%s=%s', rowNum or i, data.display)
        end
        ret[#ret + 1] = string.format('|date%s=%s', rowNum or i, data.date or '')
        if data.note then
            ret[#ret + 1] = string.format('|note%s=%s', rowNum or i, data.note)
        end
    end
    pushIfRowNumIsNil('\n|talk=' .. tostring(self.talk))
    -- pushIfRowNumIsNil('\n|collapse=' .. tostring(self.collapse))
    -- pushIfRowNumIsNil('\n|numbered=' .. tostring(self.numbered))
    pushIfRowNumIsNil('\n}}')

    return table.concat(ret)

end

-------------------------------------------------------------------------------
-- Exports
-------------------------------------------------------------------------------

local p = {}

function p._main(args)

	local afd = AfDLog.new(args)

    local caller = mw.getCurrentFrame():getParent():getTitle()
    local substPatterns = { -- Templates that must be transcluded with `subst:`
        '^Template:(削除済みノート[23]?)$',
        '^Template:(削除済みノート[23]?/.+)$',
        '^Template:(不削除ノート)$',
        '^Template:(不削除ノート/.+)$',
        '^Template:(特定版削除済みノート)$',
        '^Template:(特定版削除済みノート/.+)$',
        '^Template:(削除済みノートページ2?)$',
        '^Template:(削除済みノートページ2?/.+)$'
    }
    local callerTitle
    for _, pat in ipairs(substPatterns) do
        callerTitle = caller:match(pat)
        if callerTitle then break end
    end

    if mw.isSubsting() then
        local rowNum
        if args.n then
            rowNum = string.match(args.n, '^(%d+)$') or 2
        end
        return afd:renderRawTemplate(rowNum)
    elseif callerTitle then
        local ret = {}
        ret[#ret + 1] = '<strong class="error">エラー: '
        ret[#ret + 1] = '[[Help:テンプレート#テンプレートの内容で置き換える|subst:]] がありません。'
        ret[#ret + 1] = string.format('「%s」ではなく「subst:%s」としてください。', callerTitle, callerTitle)
        ret[#ret + 1] = '</strong>'
        afd:addCategory('subst違反')
        ret[#ret + 1] = afd:renderCategories()
        return table.concat(ret)
    else
        return afd:renderBox()
    end

end

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = {
            'Template:削除依頼ログ'
        }
	})
	return p._main(args)
end

return p