模組:沙盒/PexEric/1
外观
local p = {}
-- 配置各种评选类型的参数
local assessment_configs = {
DYKC = {
title = "Wikipedia:新条目推荐/候选",
pattern = "%{{!}}%s*article%s*=%s*(.-)%s*[%}%{{!}}]|",
black = nil,
blackregex = nil
},
PR = {
title = "Wikipedia:同行评审/提案区",
pattern = "====%s*(.-)%s*===",
black = nil,
blackregex = nil
},
GAC = {
title = "Wikipedia:優良條目評選/提名區",
pattern = "====%s*(.-)%s*===%f[^=]",
black = nil,
blackregex = "==%s*(.-)%s*="
},
FAC = {
title = "Wikipedia:典范条目评选/提名区",
pattern = "====%s*(.-)%s*===%f[^=]",
black = nil,
blackregex = "==%s*(.-)%s*="
},
FLC = {
title = "Wikipedia:特色列表评选/提名区",
pattern = "====%s*(.-)%s*===%f[^=]",
black = nil,
blackregex = "==%s*(.-)%s*="
},
ITNC = {
title = "Wikipedia:新闻动态候选",
pattern = "%{{!}}%s*article%s*=%s*(.-)%s%{{!}}",
black = nil,
blackregex = nil
}
}
-- 辅助函数:获取表格的键名,用于错误提示
local function get_config_keys(tbl)
local keys = {}
if type(tbl) ~= "table" then return {} end
for k, _ in pairs(tbl) do
table.insert(keys, k)
end
table.sort(keys)
return keys
end
-- 辅助函数:模拟 PatternedCandidateUtils 的核心 getCandidates 逻辑
-- 因为 PatternedCandidateUtils 未导出 getCandidates,我们在此重现其功能
local function get_candidates_for_assessment(assessment_key)
local config = assessment_configs[assessment_key]
if not config then return {} end
local page_content = mw.title.new(config.title):getContent()
if not page_content then return {} end
local matches = {}
local black_map = {}
if config.black then
for b_item in mw.text.gsplit(config.black, '|', true) do
black_map[b_item] = true
end
end
for m in mw.ustring.gmatch(page_content, config.pattern) do
if not black_map[m] and not (config.blackregex and mw.ustring.match(m, config.blackregex)) then
-- 候选条目标题中的下划线转换为空格,以获得规范的页面标题
table.insert(matches, mw.text.trim(m:gsub("_", " ")))
end
end
return matches -- 返回页面标题字符串数组
end
-- 辅助函数:从模板调用字符串中提取模板名称
-- 例如:从 "{{Template:Foo|bar}}" 或 "{{Foo}}" 提取 "Foo"
local function extract_template_name(invocation_str)
if not invocation_str or type(invocation_str) ~= "string" then return nil end
-- 匹配 {{ 开头,然后是非 |{}<>[] 的字符作为名称部分
local name = mw.ustring.match(invocation_str, "^%s*%{%{%s*([^|{}<>%[%]]+)")
name = name and mw.text.trim(name)
if name then
-- 移除 "Template:" 前缀(不区分大小写)
local t_prefix_lower = "template:"
local name_lower_part = mw.ustring.lower(mw.ustring.sub(name, 1, #t_prefix_lower))
if name_lower_part == t_prefix_lower then
name = mw.text.trim(mw.ustring.sub(name, #t_prefix_lower + 1))
end
end
return name
end
-- WPBS (WikiProject banner shell) 及其重定向的匹配模式
-- (部分借鉴自 Module:PJBSClass/main)
local wpbs_redirect_patterns = {
"wiki%s*project%s*banner%s*shell", "wpj?%s*banner%s*shell", "wiki%s*project%s*banners",
"multiple%s*wikiprojects?", "wiki%s*project%s*shell", "pjbs",
"[維维]基[专專][题題][橫横]幅", "多?[個个]?[維维]?基?[专專][题題][橫横]幅",
"[維维]基[专專][题題]", "多?[個个]?[維维]?基?[专專][题題]",
"[专專][题題][橫横]幅", "通用[評评][級级]"
}
-- 检查模板名称是否为 WPBS 或其重定向
local function is_wpbs_template(template_name_input)
if not template_name_input then return false end
-- 规范化名称以便比较:小写并替换下划线为空格
local template_name = mw.ustring.lower(template_name_input:gsub("_", " "))
for _, pat in ipairs(wpbs_redirect_patterns) do
if mw.ustring.find(template_name, pat) then
return true
end
end
return false
end
-- 专题横幅模板名称的常见前缀/模式 (小写, 无空格/下划线)
local project_banner_name_patterns = {
"wikiproject", "wp", -- 英文
"維基專題", "維基专题", "专题", "專題" -- 中文
}
-- 检查模板名称是否为一个专题横幅
local function is_project_banner(template_name_input)
if not template_name_input then return false end
-- 规范化名称:小写,移除所有空格、下划线、连字符
local normalized_name = mw.ustring.lower(template_name_input:gsub("[_%s-]", ""))
for _, pat_prefix in ipairs(project_banner_name_patterns) do
if mw.ustring.sub(normalized_name, 1, #pat_prefix) == pat_prefix then
return true
end
end
return false
end
-- 辅助函数:获取指定页面的所有相关专题名称列表
local function get_projects_for_page(page_title_str)
local projects_set = {} -- 使用集合确保唯一性
local main_page_title_obj = mw.title.new(page_title_str)
if not main_page_title_obj then return {} end
local talk_page_title_obj = main_page_title_obj.talkPageTitle
if not talk_page_title_obj or not talk_page_title_obj.exists then return {} end
local talk_content = talk_page_title_obj:getContent()
if not talk_content or talk_content == "" then return {} end
local TPV = require("Module:Template parameter value")
local PJBSClass -- PJBSClass/main 模块
-- 尝试加载 Module:PJBSClass/main
local success_pjbs, pjbs_module_or_error = pcall(require, "Module:PJBSClass/main")
if success_pjbs then
PJBSClass = pjbs_module_or_error
else
-- 如果无法加载 PJBSClass,至少可以继续尝试通用模板匹配
mw.log("Warning: Module:PJBSClass/main could not be loaded. Falling back to general template scan for projects. Error: " .. tostring(pjbs_module_or_error))
end
-- 1. 如果 PJBSClass 加载成功,则优先处理 WikiProject banner shell (WPBS)
if PJBSClass then
local wpbs_template_wikitext = PJBSClass.getWPBSTemplateContent(talk_content)
if wpbs_template_wikitext and mw.text.trim(wpbs_template_wikitext) ~= "" then
local wpbs_params, wpbs_param_order = TPV.getParameters(wpbs_template_wikitext)
if wpbs_params and wpbs_param_order then
for _, key_str in ipairs(wpbs_param_order) do
-- WPBS 中的专题横幅通常作为匿名参数(即数字索引)
if tonumber(key_str) then
local project_invocation = wpbs_params[key_str]
local raw_project_name = extract_template_name(project_invocation)
if raw_project_name then
-- 通过 mw.title.new 获取规范化的模板名 (无 "Template:" 前缀)
local project_title_obj = mw.title.new(raw_project_name, "Template")
local canonical_project_name = project_title_obj and project_title_obj.text or raw_project_name
-- 确认这确实是一个专题横幅
if is_project_banner(canonical_project_name) then
projects_set[canonical_project_name] = true
end
end
end
end
end
end
end
-- 2. 扫描整个讨论页,查找所有独立的专题横幅 (可能不在 WPBS 内,或 WPBS 解析失败时)
local all_template_invocations = TPV.matchAllTemplates(talk_content)
for _, invocation_str in ipairs(all_template_invocations) do
local raw_tpl_name = extract_template_name(invocation_str)
if raw_tpl_name then
local tpl_title_obj = mw.title.new(raw_tpl_name, "Template")
local template_name = tpl_title_obj and tpl_title_obj.text or raw_tpl_name
-- 添加条件:是专题横幅,且不是 WPBS 本身
if is_project_banner(template_name) and not is_wpbs_template(template_name) then
projects_set[template_name] = true
end
end
end
-- 将集合转换为排序列表
local projects_list = {}
for proj_name, _ in pairs(projects_set) do
table.insert(projects_list, proj_name)
end
table.sort(projects_list)
return projects_list
end
--- PUBLIC FUNCTIONS ---
-- 函数1: 列出某个特定内容评选中所有页面对应的专题 (表格形式)
-- 参数: frame.args[1] 或 frame.args.assessment - 评选类型 (DYKC, PR, GAC, FAC, FLC, ITNC)
function p.listProjectsForCandidates(frame)
local args = frame.args
local assessment_key_input = args[1] or args.assessment
if not assessment_key_input then
return mw.html.create('span')
:addClass('error')
:wikitext('错误:未提供评选类型。可用类型:' .. table.concat(get_config_keys(assessment_configs), ", "))
:allDone()
end
local assessment_key = mw.ustring.upper(assessment_key_input)
if not assessment_configs[assessment_key] then
return mw.html.create('span')
:addClass('error')
:wikitext('错误:无效的评选类型 "' ..assessment_key_input .. '"。可用类型:' .. table.concat(get_config_keys(assessment_configs), ", "))
:allDone()
end
local candidates = get_candidates_for_assessment(assessment_key)
if #candidates == 0 then
return "该评选类型 (" .. assessment_key .. ") 目前没有候选条目。"
end
local root = mw.html.create('table')
root:addClass('wikitable sortable')
local header_row = root:tag('tr')
header_row:tag('th'):css('width', '30%'):wikitext('候选页面')
header_row:tag('th'):wikitext('所属专题')
for i, candidate_title_str in ipairs(candidates) do
local projects = get_projects_for_page(candidate_title_str)
local row = root:tag('tr')
row:tag('td'):wikitext('[[' .. candidate_title_str .. ']]')
local projects_display_list = {}
if #projects > 0 then
for _, proj_name in ipairs(projects) do
-- 为专题名称创建链接,通常链接到模板页
table.insert(projects_display_list, '[[Template:' .. proj_name .. '|' .. proj_name .. ']]')
end
row:tag('td'):wikitext(table.concat(projects_display_list, '、'))
else
row:tag('td'):css('font-style', 'italic'):wikitext('未找到专题')
end
end
return tostring(root)
end
-- 函数2: 获取指定多个专题对应的特定内容评选项目
-- 参数: frame.args[1] 或 frame.args.assessment - 评选类型
-- 参数: frame.args[2] 或 frame.args.projects - 逗号分隔的专题名称列表
function p.listCandidatesForProjects(frame)
local args = frame.args
local assessment_key_input = args[1] or args.assessment
local projects_input_str = args[2] or args.projects
if not assessment_key_input then
return mw.html.create('span')
:addClass('error')
:wikitext('错误:未提供评选类型。可用类型:' .. table.concat(get_config_keys(assessment_configs), ", "))
:allDone()
end
local assessment_key = mw.ustring.upper(assessment_key_input)
if not assessment_configs[assessment_key] then
return mw.html.create('span')
:addClass('error')
:wikitext('错误:无效的评选类型 "' .. assessment_key_input .. '"。可用类型:' .. table.concat(get_config_keys(assessment_configs), ", "))
:allDone()
end
if not projects_input_str or mw.text.trim(projects_input_str) == "" then
return mw.html.create('span')
:addClass('error')
:wikitext('错误:未指定专题列表。请使用逗号分隔专题名称。')
:allDone()
end
local target_projects_set = {}
local target_projects_display_list = {} -- 用于反馈给用户他们搜索的专题
for proj_name_raw in mw.text.gsplit(projects_input_str, ',') do
local proj_name = mw.text.trim(proj_name_raw)
if proj_name ~= "" then
-- 规范化用户输入的专题名,以便与提取的专题名比较
-- 假设 extract_template_name 和 mw.title.new(...).text 返回的是最常见的形式
-- 用户输入的也应该是这个形式(例如 "WikiProject Medicine", "中国专题")
target_projects_set[proj_name] = true
table.insert(target_projects_display_list, proj_name)
end
end
if #target_projects_display_list == 0 then
return mw.html.create('span')
:addClass('error')
:wikitext('错误:指定的专题列表为空或格式不正确。')
:allDone()
end
local candidates = get_candidates_for_assessment(assessment_key)
if #candidates == 0 then
return "该评选类型 (" .. assessment_key .. ") 目前没有候选条目。"
end
local matching_candidates_titles = {}
for _, candidate_title_str in ipairs(candidates) do
local page_projects = get_projects_for_page(candidate_title_str)
local found_match_for_this_candidate = false
for _, p_proj_name in ipairs(page_projects) do
if target_projects_set[p_proj_name] then
found_match_for_this_candidate = true
break
end
end
if found_match_for_this_candidate then
table.insert(matching_candidates_titles, candidate_title_str)
end
end
local feedback_projects_str = table.concat(target_projects_display_list, "、")
if #matching_candidates_titles == 0 then
return "在评选类型 " .. assessment_key .. " 中,未找到属于专题“" .. feedback_projects_str .. "”中任何一个的候选条目。"
else
local root = mw.html.create()
root:wikitext("以下是在评选类型 " .. assessment_key .. " 中,属于专题“" .. feedback_projects_str .. "”中任何一个的候选条目:")
local result_list = root:tag('ul')
for _, mc_title in ipairs(matching_candidates_titles) do
result_list:tag('li'):wikitext('[[' .. mc_title .. ']]')
end
return tostring(root)
end
end
return p