Jump to content

Module:Template parameter value

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Aidan9382 (talk | contribs) at 18:57, 16 March 2023 (partially optimise escapeComments to avoid incredible performance issues on pages with comments (considering implementing this as an alternative, which appears to be much faster)). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local p = {}
local _getParameters = require("Module:Transcluder").getParameters
local escapeString = require("Module:String")._escapePattern
local yesno = require("Module:Yesno")

local function getTitle(title)
	local success, titleObj = pcall(mw.title.new, title)
	if success then return titleObj
	else return nil end
end

--string.gmatch will check the largest block it can without re-scanning whats inside, but we need whats inside
local function matchAllTemplates(str)
	local matches = {}
	for template in string.gmatch(str, "{%b{}}") do
		table.insert(matches, template)
		local innerContent = string.sub(template, 3, -3)
		for _,subtemplate in next,matchAllTemplates(innerContent) do
			table.insert(matches, subtemplate)
		end
	end
	return matches
end

local function escapeComments(content)
	for comment in string.gmatch(content, "%b<>") do
		if string.sub(comment,1,4) == "<!--" and string.sub(comment,-3,-1) == "-->" then
			local safeComment = string.gsub("<!--" .. mw.text.nowiki(string.sub(comment,4,-3)) .. "-->", "%%", "%%%%")
			content = string.gsub(content, escapeString(comment), safeComment, 1)
		end
	end
	return content
end

--Transcluder's getParameters, but force all keys to be a string (helps with template inputs)
local function getParameters(template)
	local parameters, text, paramOrder = _getParameters(template)
	local newParams = {}
	for key,value in next,parameters do
		newParams[tostring(key)] = value
	end
	local newParamOrder = {}
	for index,key in next,paramOrder do
		newParamOrder[index] = tostring(key)
	end
	return newParams, text, newParamOrder
end

-- Returns a table containing parameters and a table with the order in which each of their values were found.
-- Since this considers all subtemplates, a single parameter is expected to have multiple values.
-- E.g. {{ABC|X={{DEF|X=Value|Y=Other value}}{{ABC|X=Yes}}|Y=P}}
-- Would return {X={"{{DEF|X=Value|Y=Other value}}", "Value", "Yes"}, Y={"Other value", "P"}}
local function getAllParameters(template)
	local parameterTree = setmetatable({}, {
		__index = function(self,key)
			rawset(self,key,{})
			return rawget(self,key)
		end
	})
	local params, _, paramOrder = getParameters(template)
	for _,key in ipairs(paramOrder) do
		local value = params[key]
		table.insert(parameterTree[key], value) --Insert the initial value into the tree
		for subtemplate in string.gmatch(value, "{%b{}}") do --And now check for subvalues
			local subparams = getAllParameters(subtemplate)
			for subkey,subset in next,subparams do
				for _,subvalue in ipairs(subset) do
					table.insert(parameterTree[subkey], subvalue) --And add any we find to our tree
				end
			end
		end
	end
	return parameterTree
end

--Primary module entry point. Returns a success boolean and either the result or why it failed.
function p.getValue(page, templates, parameter, options)
	if not (templates and parameter) then --Required parameters
		return false, "Missing required parameters 'templates' and 'parameter'"
	end
	parameter = tostring(parameter) --Force consistency
	options = options or {}
	local templateIndex = tonumber(options.templateIndex) or 1
	local parameterIndex = tonumber(options.parameterIndex) or 1
	local ignoreSubtemplates = options.ignoreSubtemplates or false
	
	local title = getTitle(page)
	if title == nil then
		return false, "Requested title doesn't exist"
	end
	local content = escapeComments(title:getContent() or "")
	
	local foundTemplates = 0
	local foundParameters = 0
	for _,template in next,matchAllTemplates(content) do
		for wantedTemplate in string.gmatch(templates, "[^,]+") do
			if string.match(template, "^{{%s*"..wantedTemplate.."%s*[|}]") then
				foundTemplates = foundTemplates + 1
				if foundTemplates == templateIndex then --Found our wanted template
					local value
					if ignoreSubtemplates then
						value = getParameters(template)[parameter] or ""
					else
						value = getAllParameters(template)[parameter][parameterIndex] or ""
					end

					value = string.gsub(value, "</?%a*include%a*>", "")
					value = mw.text.trim(value)
					return true, value
				end
			end
		end
	end

	return false, "No valid template found"
end

--Template entry point. Returns an empty string upon failure
function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = 'Template:Template parameter value'
	})
	local options = {
		templateIndex = args[3],
		parameterIndex = args[5],
		ignoreSubtemplates = yesno(args.ignore_subtemplates or args.ist) or false
	}
	local success,result = p.getValue(args[1], args[2], args[4], options)
	if not success then
		return ""
	else
		return frame:preprocess(result)
	end
end

--Potentially useful module entry points
p.matchAllTemplates = matchAllTemplates
p.getParameters = getParameters
p.getAllParameters = getAllParameters

return p