模組:沙盒/PexEric/1
外观
local p = {}
-- Lazy load necessary modules
local Arguments -- Will be loaded by require
local TPV -- Module:Template parameter value
-- Helper function to initialize modules if not already done
local function init_modules()
if not Arguments then
-- Adjust the path if your Arguments module is elsewhere
Arguments = require('Module:Arguments')
end
if not TPV then
-- Adjust the path if your Template parameter value module is elsewhere
-- For simplicity, assuming it's at "Module:Template parameter value"
local success_tpv, tpv_module = pcall(require, "Module:Template parameter value")
if success_tpv then
TPV = tpv_module
else
-- Fallback or error handling if TPV module cannot be loaded
mw.log("Error loading Module:Template parameter value: " .. tostring(tpv_module))
TPV = nil -- Ensure it's nil so checks below will fail gracefully
end
end
end
-- Copied from previous version, for extracting templates from a string
local function _matchAllTemplates(str)
if not str or str == "" then
return {}
end
local matches = {}
str = tostring(str)
for template in mw.ustring.gmatch(str, "{%b{}}") do
table.insert(matches, template)
local innerContent = mw.ustring.sub(template, 3, -3)
if innerContent and #innerContent > 0 then
local subtemplates = _matchAllTemplates(innerContent)
for _, subtemplate in ipairs(subtemplates) do
table.insert(matches, subtemplate)
end
end
end
return matches
end
-- List of Lua string patterns for WikiProject banner shell and its redirects.
-- These patterns are designed to be matched against a lowercase, space-normalized template name.
-- IMPORTANT: These are now used as the 'templates' argument for TPV.getTemplate
local WPBS_TEMPLATE_NAMES_FOR_TPV = {
"WikiProject banner shell",
"WPbannershell", "WPBS", "Wpbs",
"Pjbannershell", "Pjbs",
"WikiProject banners",
"Multiple Wikiprojects", "Multiple WikiProjects",
"WikiProject shell",
"维基专题横幅", "維基專題橫幅",
"多个维基专题横幅", "多個維基專題橫幅", "多個維基专题横幅", "多个維基專題橫幅",
"维基专题", "維基專題", -- These were ambiguous before, but as TPV template targets, they are fine.
"多个维基专题", "多個維基專題",
"专题横幅", "專題橫幅",
"通用评级", "通用評級"
-- Ensure this list contains the *canonical names* or common redirects TPV should look for.
-- TPV itself handles case-insensitivity for the first letter.
-- We can also pass treat_as_regex = true to TPV if needed, but for now, direct names are better.
}
-- Internal function to get candidate article titles
local function get_candidates_internal(page_title_str, pattern_str, black_list_str, black_regex_str)
if not page_title_str or not pattern_str then
return {}
end
local page_title_obj = mw.title.new(page_title_str)
if not page_title_obj or not page_title_obj.exists then return {} end
local content = page_title_obj:getContent()
if not content then return {} end
local matches = {}
local black_map = {}
if black_list_str and black_list_str ~= "" then
for b in mw.text.gsplit(black_list_str, '|', true) do
if b ~= "" then black_map[mw.text.trim(b)] = true end
end
end
for m in mw.ustring.gmatch(content, pattern_str) do
local trimmed_m = mw.text.trim(m)
if not black_map[trimmed_m] and
not (black_regex_str and black_regex_str ~= "" and mw.ustring.match(trimmed_m, black_regex_str)) then
table.insert(matches, trimmed_m)
end
end
return matches
end
-- Internal function to get WikiProject banners for a given article
-- MODIFIED to use Module:Template parameter value
local function get_article_wikiprojects(article_title_str)
init_modules() -- Ensure TPV is loaded
if not article_title_str or article_title_str == "" or not TPV then
if not TPV then mw.log("get_article_wikiprojects: TPV module not available for " .. article_title_str) end
return {}
end
local talk_page_title_str = "Talk:" .. article_title_str
local base_title_obj = mw.title.new(article_title_str)
if base_title_obj and base_title_obj.namespace ~= 0 and base_title_obj.namespace ~= 10 and base_title_obj.namespace ~= 118 then
if base_title_obj.isTalkPage then
talk_page_title_str = article_title_str
elseif base_title_obj.canHaveTalkPage then
talk_page_title_str = base_title_obj.talkPageTitle.fullText
else
return {}
end
end
-- TPV options
local tpv_options = {
template_index = 1, -- We want the first WPBS found
-- treat_as_regex = false, -- WPBS_TEMPLATE_NAMES_FOR_TPV are direct names
ignore_blank = true, -- For parameter value
}
local success, wpbs_param1_value
local tpv_page_target = talk_page_title_str
-- Check if talk page exists. If not, and base is a project page, try base.
local talk_title_obj = mw.title.new(talk_page_title_str)
if not talk_title_obj or not talk_title_obj.exists then
if base_title_obj and base_title_obj.exists and base_title_obj.namespace ~= 0 then
tpv_page_target = base_title_obj.fullText
else
-- mw.log("get_article_wikiprojects: No talk page or suitable base page for " .. article_title_str)
return {} -- No suitable page to check
end
end
-- Try to get parameter "1" from any of the WPBS templates
success, wpbs_param1_value = TPV.getParameter(
tpv_page_target,
WPBS_TEMPLATE_NAMES_FOR_TPV, -- list of WPBS template names
"1", -- The first unnamed parameter
tpv_options
)
local projects_found = {}
local projects_map = {}
if success and wpbs_param1_value and wpbs_param1_value ~= "" then
-- wpbs_param1_value now contains the wikitext of all project banners inside the shell
-- We need to extract template names from this wikitext
local templates_in_param1 = _matchAllTemplates(wpbs_param1_value)
for _, tpl_code in ipairs(templates_in_param1) do
local tpl_name_match = mw.ustring.match(tpl_code, "{{%s*([^}|<\n#]+)") -- Avoid matching parameters starting with #
if tpl_name_match then
local raw_tpl_name = mw.text.trim(tpl_name_match)
if raw_tpl_name ~= "" then
-- Normalize the template name (resolve redirects, canonical case)
local template_title_obj = mw.title.new(raw_tpl_name, "Template")
local canonical_tpl_name = raw_tpl_name -- fallback
if template_title_obj then
local p_success, p_result = pcall(function() return template_title_obj:getText() end)
if p_success and type(p_result) == "string" then
canonical_tpl_name = p_result
end
end
if not projects_map[canonical_tpl_name] then
table.insert(projects_found, canonical_tpl_name)
projects_map[canonical_tpl_name] = true
end
end
end
end
else
-- If WPBS not found or param 1 is empty, try to find project templates directly on the page
-- This handles cases where projects are not inside a WPBS
local talk_content_obj = mw.title.new(tpv_page_target)
if talk_content_obj and talk_content_obj.exists then
local talk_content = talk_content_obj:getContent()
if talk_content then
local all_page_templates = _matchAllTemplates(talk_content)
for _, tpl_code in ipairs(all_page_templates) do
local tpl_name_match = mw.ustring.match(tpl_code, "{{%s*([^}|<\n#]+)")
if tpl_name_match then
local raw_tpl_name = mw.text.trim(tpl_name_match)
if raw_tpl_name ~= "" then
local template_title_obj = mw.title.new(raw_tpl_name, "Template")
local canonical_tpl_name = raw_tpl_name
if template_title_obj then
local p_success, p_result = pcall(function() return template_title_obj:getText() end)
if p_success and type(p_result) == "string" then
canonical_tpl_name = p_result
end
end
-- Rough check if it's a banner shell itself to avoid re-adding it
local is_shell = false
for _, shell_name_pattern_base in ipairs(WPBS_TEMPLATE_NAMES_FOR_TPV) do
-- This simple check might need refinement if shell names are complex patterns
if mw.ustring.lower(canonical_tpl_name):find(mw.ustring.lower(shell_name_pattern_base):gsub("%%s%*", " *")) then
is_shell = true
break
end
end
if not is_shell and not projects_map[canonical_tpl_name] then
table.insert(projects_found, canonical_tpl_name)
projects_map[canonical_tpl_name] = true
end
end
end
end
end
end
end
return projects_found
end
local function format_candidate_item(raw_candidate_name, item_link_prefix, item_name_prefix, item_name_suffix)
local display_name = raw_candidate_name:gsub("_", " ")
local link_target
if item_link_prefix and item_link_prefix ~= "" then
if not mw.ustring.find(item_link_prefix, "#$") and not mw.ustring.find(item_link_prefix, "^%[%[") then
item_link_prefix = item_link_prefix .. "#"
end
link_target = item_link_prefix .. raw_candidate_name
else
link_target = raw_candidate_name
end
local item_markup = string.format("[[:%s|%s]]", link_target, display_name)
return (item_name_prefix or "") .. item_markup .. (item_name_suffix or "")
end
function p.listProjects(frame)
init_modules()
if not Arguments then return '<span class="error">错误:Module:Arguments 未能加载。</span>' end
local args = Arguments.getArgs(frame)
local review_page_title = args.title
local pattern = args.pattern
if not review_page_title or not pattern then
return '<span class="error">错误:必须提供 "title" 和 "pattern" 参数。</span>'
end
local black_list = args.black
local black_regex = args.blackregex
local item_link_prefix = args.item_link_prefix or ""
local item_name_prefix = args.item_name_prefix or ""
local item_name_suffix = args.item_name_suffix or ""
local tpl_name_prefix = args.tpl_prefix or "{{tl|"
local tpl_name_suffix = args.tpl_suffix or "}}"
local candidates = get_candidates_internal(review_page_title, pattern, black_list, black_regex)
if #candidates == 0 then return args.ifnone or "暂无候选项目" end
local output_table = {'{| class="wikitable sortable plainlinks"', '! 候选项 !! 所属专题'}
for _, candidate_raw_name in ipairs(candidates) do
local formatted_candidate_item = format_candidate_item(candidate_raw_name, item_link_prefix, item_name_prefix, item_name_suffix)
local projects = get_article_wikiprojects(candidate_raw_name)
local projects_str_parts = {}
if #projects > 0 then
for _, proj_name in ipairs(projects) do
table.insert(projects_str_parts, tpl_name_prefix .. proj_name .. tpl_name_suffix)
end
end
local projects_display_str = #projects_str_parts > 0 and table.concat(projects_str_parts, "、") or "(暂无专题信息)"
table.insert(output_table, string.format("|-\n| %s \n| %s", formatted_candidate_item, projects_display_str))
end
table.insert(output_table, "|}")
return table.concat(output_table, "\n")
end
-- Normalization for project name (used as text to match regex against)
local function normalize_project_for_matching(name)
if not name or name == "" then return "" end
-- For regex matching, usually the canonical name is best
local titleObj = mw.title.new(name, "Template")
if titleObj then
local success, text = pcall(function() return titleObj:getText() end)
if success and type(text) == "string" then
return text -- Returns "Foo bar" for "Template:Foo bar"
end
end
-- Fallback: simple normalization
local temp_name = mw.text.trim(name)
temp_name = mw.ustring.gsub(temp_name, "_", " ")
if #temp_name > 0 then
temp_name = mw.ustring.upper(mw.ustring.sub(temp_name, 1, 1)) .. mw.ustring.sub(temp_name, 2)
end
return temp_name
end
-- Main function 2: Filter candidates by one or more project regexes
function p.filterByProjects(frame)
init_modules()
if not Arguments then return '<span class="error">错误:Module:Arguments 未能加载。</span>' end
local args = Arguments.getArgs(frame)
local review_page_title = args.title
local pattern = args.pattern
if not review_page_title or not pattern then
return '<span class="error">错误:必须提供 "title" 和 "pattern" 参数。</span>'
end
local project_regexes = {}
-- Collect project regexes, e.g., project1_regex, project2_regex, ... or project_regex_pattern for project1, project2
-- Option 1: Numbered regex parameters like project1_regex, project2_regex
for i = 1, 50 do -- Arbitrary limit for numbered parameters
local regex_param_name = "project" .. i .. "_regex"
if args[regex_param_name] then
table.insert(project_regexes, args[regex_param_name])
else
-- If using numbered params, break on first missing one for simplicity
-- Or, if projectN parameters are also used for names, we need a different logic
break
end
end
-- Option 2: A single project_regex_pattern to apply to project names from project1, project2, ...
-- For this request, the requirement is "each are independent regexes". So Option 1 is primary.
-- If you meant `project1="regexA"`, `project2="regexB"`, then:
if #project_regexes == 0 then -- If no projectN_regex params were found, try projectN as regex
for i = 1, 50 do
local project_arg_name = "project" .. i
if args[project_arg_name] then
table.insert(project_regexes, args[project_arg_name])
else
-- For "projectN" args, if one is missing, assume no more.
if i == 1 and not args.projects then -- if project1 is missing, and no legacy 'projects'
-- no regexes defined by projectN
elseif i > 1 then -- if project2+ is missing, stop looking
break
end
end
end
end
-- Fallback to legacy 'projects' parameter if no projectN or projectN_regex found, treat as plain strings (not regex)
-- Or, if 'projects' should also be regex, it needs to be handled explicitly.
-- The request was "multiple project parameters ... each are independent regular expressions"
-- So, if 'projects' is present and project_regexes is empty, we should clarify if 'projects' should be regex.
-- For now, assuming 'projects' (if used and no projectN regexes found) is NOT regex.
-- To make 'projects' a list of regexes, split it and add to project_regexes.
if #project_regexes == 0 and args.projects then
-- If user only provided `|projects=regex1,regex2`, split and use them.
mw.log("Using 'projects' parameter for regex list as no projectN or projectN_regex found.")
for regex_str in mw.text.gsplit(args.projects, "[,;]", true) do
if regex_str ~= "" then
table.insert(project_regexes, mw.text.trim(regex_str))
end
end
end
if #project_regexes == 0 then
return '<span class="error">错误:必须通过 "project1_regex", "project1", 或 "projects" (作为regex列表) 等参数提供至少一个专题匹配正则表达式。</span>'
end
local black_list = args.black
local black_regex = args.blackregex
local item_link_prefix = args.item_link_prefix or ""
local item_name_prefix = args.item_name_prefix or ""
local item_name_suffix = args.item_name_suffix or ""
local candidates = get_candidates_internal(review_page_title, pattern, black_list, black_regex)
if #candidates == 0 then return args.ifnone or "暂无候选项目" end
local filtered_candidate_items = {}
for _, candidate_raw_name in ipairs(candidates) do
local article_projects_names = get_article_wikiprojects(candidate_raw_name)
local match_found_for_article = false
for _, proj_name_on_article in ipairs(article_projects_names) do
local normalized_proj_name = normalize_project_for_matching(proj_name_on_article)
if normalized_proj_name ~= "" then
for _, regex_pattern in ipairs(project_regexes) do
local success, err = pcall(function() return mw.ustring.match(normalized_proj_name, regex_pattern) end)
if success and err then -- err here is the match result if successful
match_found_for_article = true
break -- Found a matching regex for this project name
elseif not success then
mw.log(string.format("Regex error for pattern '%s' on project '%s' (article '%s'): %s",
regex_pattern, normalized_proj_name, candidate_raw_name, tostring(err)))
end
end
end
if match_found_for_article then break end -- Found a match for one of the article's projects
end
if match_found_for_article then
local formatted_candidate_item = format_candidate_item(candidate_raw_name, item_link_prefix, item_name_prefix, item_name_suffix)
table.insert(filtered_candidate_items, formatted_candidate_item)
end
end
if #filtered_candidate_items == 0 then
return args.ifnone_filter or "指定专题下暂无候选项目"
end
return "# " .. table.concat(filtered_candidate_items, "\n# ")
end
return p