Module:Template parameter value

Implements {{Template parameter value}}

Module usage

lemba

The module can be used directly for other modules through the function getValue(page, templates, parameters, options), which will return a success boolean and either the resulting parameter value or why it failed.

page, templates, and parameters are all required arguments. options is an optional table which can have the parameters template_index, parameter_index, ignore_subtemplates, only_subtemplates, and ignore_blank. More info on what these parameters do can be found in the template's template data.

Testcases

lemba

Testcases are available at Module talk:Template parameter value/testcases


local p = {}
local _getParameters = require("Module:Transcluder").getParameters
local yesno = require("Module:Yesno")
local PrepareText = require("Module:Wikitext Parsing").PrepareText

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

--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, ignore_blank, only_subtemplates)
	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]
		if not ignore_blank or value ~= "" then
			if not only_subtemplates then
				table.insert(parameterTree[key], value) --Insert the initial value into the tree
			end
			for subtemplate in string.gmatch(value, "{%b{}}") do --And now check for subvalues
				local subparams = getAllParameters(subtemplate, ignore_blank)
				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
	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 {}
	
	--mix of camelCase and under_score is kept for backwards compatability
	local template_index = tonumber(options.templateIndex or options.template_index) or 1
	local parameter_index = tonumber(options.parameterIndex or options.parameter_index) or 1
	local ignore_subtemplates = options.ignoreSubtemplates or options.ignore_subtemplates or false
	local only_subtemplates = options.onlySubtemplates or options.only_subtemplates or false
	local ignore_blank = options.ignoreBlank or options.ignore_blank or false
	if type(templates) == "string" then
		templates = mw.text.split(templates, ", ?")
	end
	
	local title = getTitle(page)
	if title == nil then
		return false, "Requested title doesn't exist"
	end
	local content = PrepareText(title:getContent() or "")
	
	local foundTemplates = 0
	for _,template in next,matchAllTemplates(content) do
		for _,wantedTemplate in pairs(templates) do
			local firstLetter = string.sub(wantedTemplate, 1, 1)
			local firstUpper, firstLower = firstLetter:upper(), firstLetter:lower()
			if firstUpper ~= firstLower then
				wantedTemplate = "[" .. firstUpper .. firstLower .. "]" .. string.sub(wantedTemplate, 2)
			end
			if string.match(template, "^{{%s*"..wantedTemplate.."%s*[|}]") then
				foundTemplates = foundTemplates + 1
				if foundTemplates == template_index then --Found our wanted template
					local value
					if ignore_subtemplates then
						value = getParameters(template)[parameter] or ""
					else
						local params = getAllParameters(template, ignore_blank, only_subtemplates)
						value = params[parameter][parameter_index] or ""
					end

					value = string.gsub(value, "</?%a*include%a*>", "")
					value = mw.text.trim(value)
					return true, mw.text.decode(value) --due to PrepareText
				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 = {
		template_index = args[3],
		parameter_index = args[5],
		ignore_subtemplates = yesno(args.ignore_subtemplates or args.ist) or false,
		only_subtemplates = yesno(args.only_subtemplates) or false,
		ignore_blank = yesno(args.ignore_blank) 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