Module:Template parameter value

From OODA WIKI

Implements {{Template parameter value}}

Module usage

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

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


local p = {}
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

--Forked version of getParameters from [[Module:Transcluder]] with extra features removed
local function escapeString(str)
	return string.gsub(str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0')
end
local function getParameters(template)
	local parameters, parameterOrder = {}, {}
	local params = string.match(template, '{{[^|}]-|(.*)}}')
	if params then
		local count = 0
		-- Temporarily replace pipes in subtemplates and wikilinks to avoid chaos
		for subtemplate in string.gmatch(params, '{%b{}}') do
			params = string.gsub(params, escapeString(subtemplate), string.gsub(subtemplate, ".", {["%"]="%%", ["|"]="@@:@@", ["="]="@@_@@"}) )
		end
		for wikilink in string.gmatch(params, '%[%b[]%]') do
			params = string.gsub(params, escapeString(wikilink), string.gsub(wikilink, ".", {["%"]="%%", ["|"]="@@:@@", ["="]="@@_@@"}) )
		end
		for parameter in mw.text.gsplit(params, '|') do
			local parts = mw.text.split(parameter, '=')
			local key = mw.text.trim(parts[1])
			local value
			if #parts == 1 then
				value = key
				count = count + 1
				key = tostring(count)
			else
				value = mw.text.trim(table.concat(parts, '=', 2))
			end
			value = string.gsub(string.gsub(value, '@@:@@', '|'), '@@_@@', '=')
			key = string.gsub(string.gsub(key, '@@:@@', '|'), '@@_@@', '=')
			table.insert(parameterOrder, key)
			parameters[key] = value
		end
	end
	return parameters, parameterOrder
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 yesno = require("Module:Yesno")
	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
		if args.dontprocess then
			return result
		else
			return frame:preprocess(result)
		end
	end
end

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

return p