跳转到内容

模組:沙盒/PexEric/1

维基百科,自由的百科全书

这是本页的一个历史版本,由PexEric留言 | 贡献2025年5月31日 (六) 17:02编辑。这可能和当前版本存在着巨大的差异。

-- Module:CandidateProjectUtils
local p = {}

-- #############################################################################
-- # 依赖模块加载 (TplParamValue 仍然是可选的,因为其核心函数被复制了)
-- #############################################################################
local TplParamValue -- Lazily loaded for matchAllTemplates if needed, or can be removed if matchAllTemplates is self-contained

-- #############################################################################
-- # 内部辅助函数:获取候选条目 (逻辑从 PatternedCandidateUtils 移入)
-- #############################################################################
local function internal_get_candidates(args_table)
    -- args_table 对应 PatternedCandidateUtils 中 frame.args 的内容
    -- 例如:{ title = "page_name", pattern = "regex_pattern", black = "item1|item2", blackregex = "regex_black" }

    if not args_table or not args_table.title or not args_table.pattern then
        mw.log("CandidateProjectUtils:internal_get_candidates - Error: args_table, title, or pattern is nil.")
        if args_table and not args_table.title then
             mw.log("CandidateProjectUtils:internal_get_candidates - Missing title argument.")
        end
        if args_table and not args_table.pattern then
             mw.log("CandidateProjectUtils:internal_get_candidates - Missing pattern argument.")
        end
        return {} -- 返回空表以防止进一步错误
    end

    local page_content
    local title_obj = mw.title.new( args_table.title )
    if not title_obj then
        mw.log("CandidateProjectUtils:internal_get_candidates - Invalid title provided: " .. tostring(args_table.title))
        return {}
    end

    page_content = title_obj:getContent()
    if not page_content then
        -- mw.log("CandidateProjectUtils:internal_get_candidates - No content for title: " .. tostring(args_table.title))
        return {} -- 如果页面无内容,则没有候选者
    end

    local matches = {}
    local black = {}
    if args_table.black then
        for b in mw.text.gsplit( args_table.black, '|', true ) do
            if type(b) == "string" and b ~= "" then
                 black[b] = true
            end
        end
    end

    if type(args_table.pattern) ~= "string" then
        mw.log("CandidateProjectUtils:internal_get_candidates - Pattern is not a string: " .. tostring(args_table.pattern))
        return {}
    end

    for m in mw.ustring.gmatch( page_content, args_table.pattern ) do
        if not black[m] and not ( args_table.blackregex and type(args_table.blackregex) == "string" and mw.ustring.match( m, args_table.blackregex ) ) then
            table.insert( matches, m )
        end
    end
    return matches
end

-- 旧的 get_raw_candidates_from_pcu 现在直接调用 internal_get_candidates
local function get_raw_candidates(pcu_args_table)
    return internal_get_candidates(pcu_args_table)
end

-- #############################################################################
-- # 内部辅助函数:提取模板名 (改编自 Module:Template parameter value)
-- #############################################################################
local function matchAllTemplates(str)
	local matches = {}
	if type(str) ~= "string" then return matches end
	for template in string.gmatch(str, "{%b{}}") do
		table.insert(matches, template)
		local innerContent = string.sub(template, 3, -3)
        local sub_matches = matchAllTemplates(innerContent)
		for _,subtemplate in ipairs(sub_matches) do
			table.insert(matches, subtemplate)
		end
	end
	return matches
end

-- #############################################################################
-- # 内部辅助函数:获取条目讨论页的专题列表
-- #############################################################################
-- 辅助函数借鉴自 Module:PJBSClass/main
local function pjbs_getTemplateSection(input_text)
	local text = (input_text or '')..'\n'
	local i,j = mw.ustring.find(text, "\n%=+[^\n]*%=+%s*\n")
	if i == nil then
		i,j = mw.ustring.find(text, "%{%{%s*[Dd][Yy][Kk][Ee]ntry/archive")
	end
	text = mw.ustring.sub(text, 1, (i or 0)-1)
	return text
end

local function pjbs_processWPBSRedirect(input_text)
	local text = input_text
	if not text then return "" end
	local norm_name = "WPBS"
	local match_list = {
		"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",
		"[維维]基[专專][题題][橫横]幅", "多?[個个]?[維维]?基?[专專][题題][橫横]幅",
		"[維维]基[专專][题題]", "多?[個个]?[維维]?基?[专專][题題]",
		"[专專][题題][橫横]幅", "通用[評评][級级]",
	}
	local lotext = mw.ustring.gsub(mw.ustring.lower(text), "_", " ")
	local normtext = text
	for i = 1,#match_list do
		local j, k = mw.ustring.find(lotext, match_list[i])
		while j ~= nil do
			normtext = mw.ustring.sub(normtext, 1, j-1)..norm_name..mw.ustring.sub(normtext, k+1, -1) 
			lotext = mw.ustring.gsub(mw.ustring.lower(normtext), "_", " ")
			j, k = mw.ustring.find(lotext, match_list[i])
		end
	end
	normtext = mw.ustring.gsub(normtext, "[Ww][Pp][Bb][Ss]", "WikiProject banner shell")
	text = normtext
	return text
end

local function pjbs_getWPBSTemplateContent(input_text)
    if not input_text then return '' end
	local text = input_text
	local re_WPBS_header = "%{%{[^%{%}%|]*[Ww]iki[Pp]roject[%s_]*[Bb]anner[%s_]*[Ss]hell"
	local it = text
	local old_it = text.."-" 
	while it ~= old_it do
		if not mw.ustring.match(it, re_WPBS_header) then break end
		old_it = it
		it = mw.ustring.gsub(it, "%{[^%{%}]*%}", "") 
	end
	local WPBS = mw.ustring.match(old_it, re_WPBS_header.."[^%{%}]*%}%}") or ''
	return WPBS
end

local function get_article_wikiprojects(article_title_str)
    local projects = {}
    local unique_project_names = {} 

    if not article_title_str or article_title_str == '' then
        return {}
    end

    local article_title_obj = mw.title.new(article_title_str)
    if not article_title_obj then
        return {}
    end

    local talk_page_title = article_title_obj.talkPageTitle
    if not talk_page_title or not talk_page_title.exists or talk_page_title.isRedirect then
        return {}
    end

    local talk_content = talk_page_title:getContent()
    if not talk_content or talk_content == '' then
        return {}
    end

    local banner_section_text = pjbs_getTemplateSection(talk_content)
    local processed_banner_section_text = pjbs_processWPBSRedirect(banner_section_text)
    local wpbs_template_wikitext = pjbs_getWPBSTemplateContent(processed_banner_section_text)

    local text_to_scan_for_templates
    if wpbs_template_wikitext and wpbs_template_wikitext ~= "" then
        text_to_scan_for_templates = mw.ustring.match(wpbs_template_wikitext, "{{[^|]+|(.*)}}") or ""
    else
        text_to_scan_for_templates = processed_banner_section_text
    end

    local all_found_templates_code = matchAllTemplates(text_to_scan_for_templates)

    for _, tpl_code in ipairs(all_found_templates_code) do
        local core_part = mw.ustring.sub(tpl_code, 3, -3) 
        local tpl_name = mw.text.trim( (mw.ustring.match(core_part, "^([^|]+)") or core_part) ) 

        if tpl_name and #tpl_name > 0 then
            local normalized_tpl_name = mw.ustring.upper(mw.ustring.sub(tpl_name, 1, 1)) .. mw.ustring.sub(tpl_name, 2)
            local lower_tpl_name = mw.ustring.lower(normalized_tpl_name)

            if (mw.ustring.find(lower_tpl_name, "^wikiproject") or
                mw.ustring.find(lower_tpl_name, "^wp[%s_]") or 
                mw.ustring.find(tpl_name, "专题") or  
                mw.ustring.find(tpl_name, "專題")) and 
                lower_tpl_name ~= "wikiproject banner shell" and
                lower_tpl_name ~= "wpbs" and
                lower_tpl_name ~= "pjbs" and
                not mw.ustring.find(lower_tpl_name, "/sandbox$") and
                not mw.ustring.find(lower_tpl_name, "/doc$")
            then
                if not unique_project_names[lower_tpl_name] then
                    table.insert(projects, normalized_tpl_name)
                    unique_project_names[lower_tpl_name] = true
                end
            end
        end
    end

    if #projects > 0 then
        table.sort(projects) 
    end
    return projects
end

-- #############################################################################
-- # 主要函数 1: 列出特定内容评选中所有候选项目对应的所属专题
-- #############################################################################
function p.list_candidates_with_projects(frame)
    local args = frame:getParent().args -- Get args from the template call
    local pcu_args_table = {
        title = args.pcu_title,
        pattern = args.pcu_pattern,
        black = args.pcu_black,
        blackregex = args.pcu_blackregex
    }

    local item_prefix = args.item_prefix or ''
    local item_suffix = args.item_suffix or ''

    -- 调用内部集成的 get_raw_candidates 函数
    local candidates = get_raw_candidates(pcu_args_table)

    if #candidates == 0 then
        return args.no_candidates_msg or "暂无候选项目。"
    end

    local output_rows = {}
    table.insert(output_rows, '{| class="wikitable sortable"')
    table.insert(output_rows, '! 条目名 !! 所属专题')

    for i, article_name_raw in ipairs(candidates) do
        local article_name_display = string.gsub(article_name_raw, "_", " ")
        local projects = get_article_wikiprojects(article_name_raw)
        local formatted_projects_list = {}
        if #projects > 0 then
            for _, proj_name in ipairs(projects) do
                table.insert(formatted_projects_list, "{{tl|" .. proj_name .. "}}")
            end
        end
        
        local projects_str = table.concat(formatted_projects_list, "、")
        if projects_str == "" then
            projects_str = args.no_projects_found_msg or "(未找到专题)"
        end

        local formatted_article_link = item_prefix .. article_name_display .. item_suffix
        
        table.insert(output_rows, '|-\n| ' .. formatted_article_link .. ' \n| ' .. projects_str)
    end

    table.insert(output_rows, '|}')
    return table.concat(output_rows, '\n')
end

-- #############################################################################
-- # 主要函数 2: 获取特定内容评选栏目中指定的属于一个或多个给定专题的候选项目
-- #############################################################################
function p.filter_candidates_by_projects(frame)
    local args = frame:getParent().args -- Get args from the template call
    local pcu_args_table = {
        title = args.pcu_title,
        pattern = args.pcu_pattern,
        black = args.pcu_black,
        blackregex = args.pcu_blackregex
    }

    local item_prefix = args.item_prefix or ''
    local item_suffix = args.item_suffix or ''
    local target_projects_str = args.target_projects or ""
    
    if target_projects_str == "" then
        return "错误:未指定目标专题 (target_projects 参数为空)。"
    end

    local target_projects_map = {}
    for proj in mw.text.gsplit(target_projects_str, ',', true) do 
        local trimmed_proj = mw.text.trim(proj)
        if trimmed_proj ~= "" then
            target_projects_map[mw.ustring.lower(trimmed_proj)] = true
        end
    end

    if next(target_projects_map) == nil then
         return "错误:目标专题列表处理后为空 (target_projects 参数可能只包含逗号或空格)。"
    end

    -- 调用内部集成的 get_raw_candidates 函数
    local candidates = get_raw_candidates(pcu_args_table)

    if #candidates == 0 then
        return args.no_candidates_msg or "暂无候选项目。"
    end

    local output_items = {}
    for i, article_name_raw in ipairs(candidates) do
        local article_name_display = string.gsub(article_name_raw, "_", " ")
        local article_projects = get_article_wikiprojects(article_name_raw)
        
        local match_found = false
        for _, found_proj_name in ipairs(article_projects) do
            if target_projects_map[mw.ustring.lower(found_proj_name)] then
                match_found = true
                break
            end
        end

        if match_found then
            local formatted_article_link = item_prefix .. article_name_display .. item_suffix
            table.insert(output_items, formatted_article_link)
        end
    end

    if #output_items == 0 then
        return args.no_match_msg or "暂无符合指定专题的候选项目。"
    end

    local result_list = {}
    for i, item in ipairs(output_items) do
        table.insert(result_list, "# " .. item)
    end
    return table.concat(result_list, "\n")
end

return p