模組:沙盒/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
return {}
end
local pageContent = pageTitleObj:getContent()
if not pageContent then
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)
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
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
local talkPageTitleObj = mw.title.new(article_title)
if talkPageTitleObj then -- 仅当条目对象有效时继续
local talkPageTitle = talkPageTitleObj:getTalkPage()
if talkPageTitle then -- 仅当讨论页对象有效时继续
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)
end
end
-- 如果 talkPageTitleObj 或 talkPageTitle 无效,则自然跳到下一次循环
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
local talkPageTitleObj = mw.title.new(article_title)
if talkPageTitleObj then -- 仅当条目对象有效时继续
local talkPageTitle = talkPageTitleObj:getTalkPage()
if 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
-- 使用 pcall 防范无效正则表达式引发错误
local success_match, _ = pcall(mw.ustring.match, actual_proj_name, proj_regex)
if success_match and mw.ustring.match(actual_proj_name, proj_regex) then
matched_project = true
break -- 当前条目的一个专题已匹配,无需再检查此条目的其他专题
end
end
if matched_project then
break -- 当前条目已匹配,无需再检查此条目的其他专题针对其他 projectN 正则
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
end
end
-- 如果 talkPageTitleObj 或 talkPageTitle 无效,则自然跳到下一次循环
end
if #matching_articles_formatted == 0 then
return args.projectifnone or "没有找到符合指定专题条件的候选项目。"
end
-- 输出为 # 号有序列表
return '# ' .. table.concat(matching_articles_formatted, '\n# ')
end
return p