Module:Template parameter value
| This Lua module is used on approximately 11,700,000 pages, or roughly 18% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
| This module depends on the following other modules: |
Implements {{Template parameter value}} and {{HasTemplate}}, and can be used from other modules.
Module functions
getParameter
getParameter takes 4 arguments: The page name (string), the template/s (string or table of strings), the parameter (string), and an optional options table. It will return either true and the contents of the requested parameter or false and a reason for failure.
The following options are available:
- template_index: Which occurrence of the template to look for the parameter in. Set to -1 for last occurrence. (default: 1)
- parameter_index: Which occurrence of the parameter to look for (default: 1; only applies when
ignore_subtemplatesis false) - ignore_subtemplates: If parameters should only be searched for in the top-level template, ignoring the parameters in subtemplates (default: false)
- only_subtemplates: If parameters should only be searched for in subtemplates of the top-level template (default: false)
- ignore_blank: Whether or not blank values should count towards
parameter_index(default: false) - treat_as_regex: Whether or not the template string(s) should be treated as a lua regex (default: false)
getTemplate
getTemplate takes 3 arguments: The page name (string), the template/s (string or table of strings), and an optional options table. It will return either true and the text of the requested template or false and a reason for failure.
getTemplate supports the options template_index and treat_as_regex from getParameter.
Helper functions
The module exposes some of the helper functions used (matchAllTemplates, getParameters, and getAllParameters) for convenience. Each function has some comments above it in the code explaining its rough purpose.
Template functions
main implements {{Template parameter value}} and acts as a template wrapper for getParameter.
hasTemplate implements {{HasTemplate}} and somewhat acts as a wrapper for getTemplate (it only provides if the template was found, not the template text itself).
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 -- Finds all templates in the given string, including nested templates -- Templates are returned in position order, e.g. "<a<b>>" returns {"<a<b>>", "<b>"} local function matchAllTemplates(str) local matchLayers = {{}} local unfinished = {} local position = 1 local lastClosedLayer = 1 while true do local nextOpen = string.find(str, "{{", position, true) local nextClose = string.find(str, "}}", position, true) if nextOpen and (not nextClose or nextOpen < nextClose) then unfinished[#unfinished+1] = nextOpen if not matchLayers[#unfinished] then matchLayers[#unfinished] = {} end position = nextOpen + 2 elseif nextClose then local lenLayers = #unfinished if lenLayers > 0 then local matches = matchLayers[lenLayers] matches[#matches+1] = string.sub(str, unfinished[lenLayers], nextClose + 1) unfinished[lenLayers] = nil -- Ensure templates are ordered by their starting position, not their ending position if lenLayers < lastClosedLayer then local children = matchLayers[lenLayers+1] for i = 1, #children do matches[#matches+1] = children[i] children[i] = nil end end lastClosedLayer = lenLayers end position = nextClose + 2 else break end end -- Check for any uncaught matches caused by bad input strings for i = #matchLayers, 2, -1 do local cur, parent = matchLayers[i], matchLayers[i-1] for i = 1, #cur do parent[#parent+1] = cur[i] end end return matchLayers[1] 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 --Module entry point. Returns a success boolean and either the target template or why it failed function p.getTemplate(page, templates, options) if not templates then --Required parameters return false, "Missing required parameter 'templates'" end options = options or {} local template_index = tonumber(options.template_index) or 1 local treat_as_regex = options.treat_as_regex or false if type(templates) == "string" then -- TODO: Find a good way to allow specifying multiple templates via template invocation -- (Modules can just provide a table so no concerns there) -- Comma splitting is a bad idea (lots of templates have a comma in their name) templates = {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 sanitisedTemplates = {} for i,template in pairs(templates) do if not treat_as_regex then template = escapeString(template) end local firstLetter = string.sub(template, 1, 1) local firstUpper, firstLower = firstLetter:upper(), firstLetter:lower() if firstUpper ~= firstLower then template = "[" .. firstUpper .. firstLower .. "]" .. string.sub(template, 2) end sanitisedTemplates[i] = template end local foundTemplates = 0 local foundTemplate for _,template in next,matchAllTemplates(content) do for _,wantedTemplate in pairs(sanitisedTemplates) do if string.match(template, "^{{%s*"..wantedTemplate.."%s*[|}]") then foundTemplate = template foundTemplates = foundTemplates + 1 if foundTemplates == template_index then --Found our wanted template return true, foundTemplate end end end end if foundTemplates > 0 and template_index == -1 then return true, foundTemplate end return false, "No valid template found" end --Module entry point. Returns a success boolean and either the target parameter's value or why it failed function p.getParameter(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 success, text = p.getTemplate(page, templates, options) if not success then return success, text else local parameter_index = tonumber(options.parameter_index) or 1 local ignore_subtemplates = options.ignore_subtemplates or false local only_subtemplates = options.only_subtemplates or false local ignore_blank = options.ignore_blank or false local value if ignore_subtemplates then value = getParameters(text)[parameter] or "" else local params = getAllParameters(text, ignore_blank, only_subtemplates) value = params[parameter][parameter_index] or "" end value = string.gsub(value, "</?%a*include%a*>", "") value = mw.text.trim(value) --technically wrong in some cases but not a big issue return true, mw.text.decode(value) --decode due to PrepareText end end --Template entry point. Returns either "yes" or nothing depending on if the wanted template is found --Will return error text if no template is provided function p.hasTemplate(frame) local args = require('Module:Arguments').getArgs(frame) local yesno = require("Module:Yesno") local page = args[1] or args.page local template = args[2] or args.template local template_index = tonumber(args[3] or args.N) or 1 if not template or template == "" then return '<span class="error">No template provided for hasTemplate</span>' end local follow = yesno(args.follow) or false if follow then page = require("Module:Redirect").luaMain(page) end local options = { template_index = template_index, treat_as_regex = yesno(args.treat_as_regex) or false, } local success, _ = p.getTemplate(page, template, options) return success and "yes" or "" end --Template entry point for getParameter. 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] or args.template_index, parameter_index = args[5] or args.parameter_index, 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, treat_as_regex = yesno(args.treat_as_regex) or false, } local page = args[1] or args.page local template = args[2] or args.template local parameter = args[4] or args.parameter local success, result = p.getParameter(page, template, parameter, options) if not success then return "" else if args.dontprocess then return result else return frame:preprocess(result) end end end --Backwards compatability p.getValue = p.getParameter --Potentially useful module entry points p.matchAllTemplates = matchAllTemplates p.getParameters = getParameters p.getAllParameters = getAllParameters return p