Jump to content

Module:Template parameter value: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Use faster and more advanced method of handling more complex wikisyntax to avoid some timeout errors or rare logic errors
New features ignore_blank and only_subtemplates; some minor cleanup
Line 1: Line 1:
local p = {}
local p = {}
local _getParameters = require("Module:Transcluder").getParameters
local _getParameters = require("Module:Transcluder").getParameters
local escapeString = require("Module:String")._escapePattern
local yesno = require("Module:Yesno")
local yesno = require("Module:Yesno")
local PrepareText = require("Module:Wikitext Parsing").PrepareText
local PrepareText = require("Module:Wikitext Parsing").PrepareText
Line 42: Line 41:
-- E.g. {{ABC|X={{DEF|X=Value|Y=Other value}}{{ABC|X=Yes}}|Y=P}}
-- 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"}}
-- Would return {X={"{{DEF|X=Value|Y=Other value}}", "Value", "Yes"}, Y={"Other value", "P"}}
local function getAllParameters(template)
local function getAllParameters(template, ignore_blank, only_subtemplates)
local parameterTree = setmetatable({}, {
local parameterTree = setmetatable({}, {
__index = function(self,key)
__index = function(self,key)
Line 52: Line 51:
for _,key in ipairs(paramOrder) do
for _,key in ipairs(paramOrder) do
local value = params[key]
local value = params[key]
if not ignore_blank or value ~= "" then
table.insert(parameterTree[key], value) --Insert the initial value into the tree
if not only_subtemplates then
for subtemplate in string.gmatch(value, "{%b{}}") do --And now check for subvalues
table.insert(parameterTree[key], value) --Insert the initial value into the tree
local subparams = getAllParameters(subtemplate)
end
for subkey,subset in next,subparams do
for subtemplate in string.gmatch(value, "{%b{}}") do --And now check for subvalues
for _,subvalue in ipairs(subset) do
local subparams = getAllParameters(subtemplate, ignore_blank)
table.insert(parameterTree[subkey], subvalue) --And add any we find to our tree
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
Line 65: Line 68:
end
end


--Primary module entry point. Returns a success boolean and either the result or why it failed.
--Primary module entry point. Returns a success boolean and either the result or why it failed
function p.getValue(page, templates, parameter, options)
function p.getValue(page, templates, parameter, options)
if not (templates and parameter) then --Required parameters
if not (templates and parameter) then --Required parameters
Line 72: Line 75:
parameter = tostring(parameter) --Force consistency
parameter = tostring(parameter) --Force consistency
options = options or {}
options = options or {}
local templateIndex = tonumber(options.templateIndex) or 1
--mix of camelCase and under_score is kept for backwards compatability
local parameterIndex = tonumber(options.parameterIndex) or 1
local ignoreSubtemplates = options.ignoreSubtemplates or false
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
if type(templates) == "string" then
templates = mw.text.split(templates, ", ?")
templates = mw.text.split(templates, ", ?")
Line 91: Line 98:
if string.match(template, "^{{%s*"..wantedTemplate.."%s*[|}]") then
if string.match(template, "^{{%s*"..wantedTemplate.."%s*[|}]") then
foundTemplates = foundTemplates + 1
foundTemplates = foundTemplates + 1
if foundTemplates == templateIndex then --Found our wanted template
if foundTemplates == template_index then --Found our wanted template
local value
local value
if ignoreSubtemplates then
if ignore_subtemplates then
value = getParameters(template)[parameter] or ""
value = getParameters(template)[parameter] or ""
else
else
value = getAllParameters(template)[parameter][parameterIndex] or ""
local params = getAllParameters(template, ignore_blank, only_subtemplates)
value = params[parameter][parameter_index] or ""
end
end


Line 116: Line 124:
})
})
local options = {
local options = {
templateIndex = args[3],
template_index = args[3],
parameterIndex = args[5],
parameter_index = args[5],
ignoreSubtemplates = yesno(args.ignore_subtemplates or args.ist) or false
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)
local success, result = p.getValue(args[1], args[2], args[4], options)
if not success then
if not success then
return ""
return ""

Revision as of 12:09, 3 May 2023

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
	local foundParameters = 0
	for _,template in next,matchAllTemplates(content) do
		for _,wantedTemplate in pairs(templates) do
			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