模組:沙盒/PexEric/1
外观
local p = {}
-- 引入模块
local argsModule = require('Module:Arguments')
local tparamValue -- 延迟加载: require('Module:Template parameter value')
-- WikiProject banner shell 及其重定向的正则表达式列表
local WIKI_PROJECT_BANNER_SHELL_REDIRECTS = {
"wiki%s*project%s*banner%s*shell",
"w?pj?%s*banner%s*shell",
"wiki%s*project%s*banners",
"multiple%s*wikiprojects?",
"wiki%s*project%s*shell",
"pjbs",
"[維维]基[专專][题題][橫横]幅",
"多?[個个]?[維维]?基?[专專][题題][橫横]幅",
"[維维]基[专專][题題]",
"多?[個个]?[維维]?基?[专專][题題]",
"[专專][题題][橫横]幅",
"通用[評评][級级]",
}
---
-- 内部函数:从指定页面获取候选项目标题
-- @param cfg table 配置表,包含: title(页面标题), pattern(提取正则), black(黑名单), blackregex(黑名单正则)
-- @return table 候选项目标题数组
local function getCandidatesFromPage(cfg)
local pageTitleObj = mw.title.new(cfg.title)
if not pageTitleObj or not pageTitleObj.exists then
mw.log("Module:CandidateProjectInfo - getCandidatesFromPage: Page not found or invalid: " .. cfg.title)
return {}
end
local pageContent = pageTitleObj:getContent()
if not pageContent then
mw.log("Module:CandidateProjectInfo - getCandidatesFromPage: No content for page: " .. cfg.title)
return {}
end
local matches = {}
local blackList = {}
if cfg.black and cfg.black ~= '' then
for b in mw.text.gsplit(cfg.black, '|', true) do
blackList[mw.text.trim(b)] = true
end
end
for m in mw.ustring.gmatch(pageContent, cfg.pattern) do
m = mw.text.trim(m)
if m ~= '' and not blackList[m] and not (cfg.blackregex and cfg.blackregex ~= '' and mw.ustring.match(m, cfg.blackregex)) then
table.insert(matches, m)
else
-- mw.log("Module:CandidateProjectInfo - getCandidatesFromPage: Skipped candidate by black/blackregex: " .. m)
end
end
return matches
end
---
-- 内部函数:从讨论页获取维基专题名称
-- @param talkPageName string 讨论页完整标题
-- @return table 专题模板名称数组
local function getProjectsFromTalkPage(talkPageName)
if not tparamValue then -- 延迟加载
tparamValue = require('Module:Template parameter value')
end
local success, bannerContent = tparamValue.getParameter(
talkPageName,
WIKI_PROJECT_BANNER_SHELL_REDIRECTS,
'1', -- {{WikiProject banner shell}} 的第一个匿名参数
{
treat_as_regex = true, -- WIKI_PROJECT_BANNER_SHELL_REDIRECTS 使用正则
template_index = 1 -- 获取第一个匹配的 banner shell
}
)
if not success or not bannerContent or bannerContent:match("^%s*$") then
-- mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: No banner shell content for: " .. talkPageName)
return {} -- 未成功获取或内容为空
end
local projects = {}
-- 从 banner shell 的参数1内容中提取模板名
for name_part in mw.ustring.gmatch(bannerContent, "{{%s*([^}|]+)") do
local projectName = mw.text.trim(name_part)
if projectName ~= "" then
local found = false -- 避免重复添加同一专题
for _, existing_proj in ipairs(projects) do
if existing_proj == projectName then
found = true
break
end
end
if not found then
table.insert(projects, projectName)
end
end
end
return projects
end
---
-- 内部函数:格式化条目项(链接 + 前后缀)
-- @param article_title string 条目标题
-- @param linkprefix_val string|nil 链接目标前缀
-- @param item_prefix_val string 条目项前缀
-- @param item_suffix_val string 条目项后缀
-- @return string 格式化后的条目项文本
local function formatArticleItem(article_title, linkprefix_val, item_prefix_val, item_suffix_val)
local display_title = article_title:gsub('_', ' ') -- 链接显示文本替换下划线
local core_link
if linkprefix_val and linkprefix_val ~= "" then
core_link = string.format('[[:%s%s|%s]]', linkprefix_val, article_title, display_title)
else
core_link = string.format('[[:%s|%s]]', article_title, display_title)
end
return item_prefix_val .. core_link .. item_suffix_val
end
---
-- 函数1: 以表格形式列出候选项目及其所属专题
-- @param frame table Scribunto frame 对象
function p.listProjectsForCandidates(frame)
local args = argsModule.getArgs(frame, {removeBlanks = false}) -- 保留空参数字符串
local review_page_title = args.title
local candidate_pattern = args.pattern
if not review_page_title or review_page_title == '' or not candidate_pattern or candidate_pattern == '' then
return '<span class="error">错误:参数 "title" 和 "pattern" 不能为空。</span>'
end
local candidate_cfg = {
title = review_page_title,
pattern = candidate_pattern,
black = args.black,
blackregex = args.blackregex
}
local candidates = getCandidatesFromPage(candidate_cfg)
if #candidates == 0 then
return args.ifnone or "暂无候选项目"
end
-- 参数获取,带默认值
local item_prefix = args.item_prefix or ""
local item_suffix = args.item_suffix or ""
local tpl_prefix = args.tpl_prefix or "{{tl|"
local tpl_suffix = args.tpl_suffix or "}}"
local linkprefix = args.linkprefix
local output_table = {}
table.insert(output_table, '{| class="wikitable sortable"')
table.insert(output_table, '! 条目 !! 所属专题') -- 表头
for _, article_title in ipairs(candidates) do
if type(article_title) == "string" and article_title ~= "" then
local talkPageTitleObj = mw.title.new(article_title)
if talkPageTitleObj then
local talkPageTitle = nil
local pcall_success, pcall_result_or_error = pcall(function()
-- 再次检查 talkPageTitleObj 以防万一,并确保 getTalkPage 是函数
if talkPageTitleObj and type(talkPageTitleObj.getTalkPage) == 'function' then
talkPageTitle = talkPageTitleObj:getTalkPage()
else
-- 如果 talkPageTitleObj 无效或缺少方法,则抛出错误以被 pcall 捕获
error("talkPageTitleObj is invalid or missing getTalkPage method.")
end
end)
if pcall_success then
if talkPageTitle then -- 确保 getTalkPage 返回了一个对象
local projects = getProjectsFromTalkPage(talkPageTitle:getFullText())
local projects_formatted_list = {}
if #projects > 0 then
for _, proj_name in ipairs(projects) do
table.insert(projects_formatted_list, tpl_prefix .. proj_name .. tpl_suffix)
end
end
local projects_string
if #projects_formatted_list > 0 then
projects_string = table.concat(projects_formatted_list, "、")
else
projects_string = args.projectifnone or "<em>未找到所属专题信息</em>"
end
local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix)
table.insert(output_table, '|-')
table.insert(output_table, '| ' .. formatted_article .. '\n| ' .. projects_string)
else
-- getTalkPage 成功调用但返回 nil (例如,特殊页面没有讨论页)
-- 这种情况下,我们认为没有专题信息
local projects_string = args.projectifnone or "<em>未找到所属专题信息</em>"
local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix)
table.insert(output_table, '|-')
table.insert(output_table, '| ' .. formatted_article .. '\n| ' .. projects_string)
mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: getTalkPage returned nil for article: " .. article_title)
end
else
-- pcall 失败
mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: Error getting talk page for article '" .. article_title .. "': " .. tostring(pcall_result_or_error))
-- 仍然为该条目输出一行,但标记专题获取失败
local projects_string = "<em>获取专题信息失败</em>"
local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix)
table.insert(output_table, '|-')
table.insert(output_table, '| ' .. formatted_article .. '\n| ' .. projects_string)
end
else
mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: Could not create title object for article: " .. article_title)
-- 为无法创建标题对象的条目也输出一行
local projects_string = "<em>无效条目名</em>"
local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix) -- 尝试格式化,可能仅显示 article_title
table.insert(output_table, '|-')
table.insert(output_table, '| ' .. formatted_article .. '\n| ' .. projects_string)
end
else
mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: Invalid (nil or empty) article_title found in candidates list: " .. tostring(article_title))
end
end
table.insert(output_table, '|}')
return table.concat(output_table, '\n')
end
---
-- 函数2: 按指定专题筛选候选项目
-- @param frame table Scribunto frame 对象
function p.filterCandidatesByProjects(frame)
local args = argsModule.getArgs(frame, {removeBlanks = false})
local review_page_title = args.title
local candidate_pattern = args.pattern
if not review_page_title or review_page_title == '' or not candidate_pattern or candidate_pattern == '' then
return '<span class="error">错误:参数 "title" 和 "pattern" 不能为空。</span>'
end
local project_regexes = {} -- 存储 projectN 参数的正则表达式
for k, v in pairs(args) do
if type(k) == 'string' and k:match("^project%d+$") and v and v ~= '' then
table.insert(project_regexes, v)
end
end
if #project_regexes == 0 then
return '<span class="error">错误:必须提供至少一个 "projectN" 参数用于筛选。</span>'
end
local candidate_cfg = {
title = review_page_title,
pattern = candidate_pattern,
black = args.black,
blackregex = args.blackregex
}
local candidates = getCandidatesFromPage(candidate_cfg)
if #candidates == 0 then
return args.ifnone or "暂无候选项目"
end
local item_prefix = args.item_prefix or ""
local item_suffix = args.item_suffix or ""
local linkprefix = args.linkprefix
local matching_articles_formatted = {}
for _, article_title in ipairs(candidates) do
if type(article_title) == "string" and article_title ~= "" then
local talkPageTitleObj = mw.title.new(article_title)
if talkPageTitleObj then
local talkPageTitle = nil
local pcall_success, pcall_result_or_error = pcall(function()
if talkPageTitleObj and type(talkPageTitleObj.getTalkPage) == 'function' then
talkPageTitle = talkPageTitleObj:getTalkPage()
else
error("talkPageTitleObj is invalid or missing getTalkPage method.")
end
end)
if pcall_success and talkPageTitle then
local article_projects = getProjectsFromTalkPage(talkPageTitle:getFullText())
if #article_projects > 0 then
local matched_project = false
for _, actual_proj_name in ipairs(article_projects) do
for _, proj_regex in ipairs(project_regexes) do
local regex_match_success, _ = pcall(mw.ustring.match, actual_proj_name, proj_regex)
if regex_match_success and mw.ustring.match(actual_proj_name, proj_regex) then
matched_project = true
break
end
end
if matched_project then break end
end
if matched_project then
local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix)
table.insert(matching_articles_formatted, formatted_article)
end
end
elseif not pcall_success then
mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: Error getting talk page for article '" .. article_title .. "': " .. tostring(pcall_result_or_error))
-- else: getTalkPage was successful but returned nil, or talkPageTitleObj was invalid from the start for this path.
-- No special handling needed here, as it won't match projects.
-- else if talkPageTitle is nil (after successful pcall), it will correctly result in no projects.
end
else
mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: Could not create title object for article: " .. article_title)
end
else
mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: Invalid (nil or empty) article_title found in candidates list: " .. tostring(article_title))
end
end
if #matching_articles_formatted == 0 then
return args.projectifnone or "没有找到符合指定专题条件的候选项目。"
end
return '# ' .. table.concat(matching_articles_formatted, '\n# ')
end
return p