local util =
local function _readTemplateData(templateName) local title = mw.title.makeTitle(0, templateName) local templateContent = title and title.exists and title:getContent -- template's raw content local capture = templateContent and mw.ustring.match(templateContent, '
local function readTemplateData(templateName) if type(templateName)
"table" then for _, name in ipairs(templateName) do local td, result = _readTemplateData(name) if td then return result end end end return nilend
-- this is the function to be called by other modules. it expects the frame, and then an optional list of subpages, e.g. .-- if second parameter is nil, only tempalte page will be searched for templatedata.function calculateViolations(frame, subpages)-- used for parameter type validy test. keyed by TD 'type' string. values are function(val) returning bool. local type_validators = function compatible(typ, val) local func = type_validators[typ] return type(func) ~= 'function' or util.empty(val) or func(val) end local t_frame = frame:getParent local t_args, template_name = t_frame.args, t_frame:getTitle template_name = mw.ustring.gsub(template_name, '/sandbox', , 1) local td_source = util.build_namelist(template_name, subpages) if frame.args['td_source'] then table.insert(td_source, frame.args['td_source']) end
local templatedata = readTemplateData(td_source) local td_params = templatedata and templatedata.params local all_aliases, all_series =,
if not td_params then return end -- from this point on, we know templatedata is valid.
local res = -- before returning to caller, we'll prune empty tables
-- allow for aliases for x, p in pairs(td_params) do for y, alias in ipairs(p.aliases or) do p['primary'] = x td_params[x] = p all_aliases[alias] = p if tonumber(alias) then all_aliases[tonumber(alias)] = p end end end
-- handle undeclared and deprecated local already_seen = local series = frame.args['series'] for p_name, value in pairs(t_args) do local tp_param, noval, numeric, table_name = td_params[p_name] or all_aliases[p_name], util.empty(value), tonumber(p_name) local hasval = not noval
if not tp_param and series then -- 2nd chance. check to see if series for s_name, p in pairs(td_params) do if mw.ustring.match(p_name, '^' .. s_name .. '%d+' .. '$') then -- mw.log('found p_name '.. p_name .. ' s_name:' .. s_name, ' p is:', p) debugging series support tp_param = p end -- don't bother breaking. td always correct. end end
if not tp_param then -- not in TD: this is called undeclared -- calculate the relevant table for this undeclared parameter, based on parameter and value types table_name = noval and numeric and 'empty-undeclared-numeric' or noval and not numeric and 'empty-undeclared' or hasval and numeric and 'undeclared-numeric' or 'undeclared' -- tzvototi nishar. else -- in td: test for deprecation and mistype. if deprecated, no further tests table_name = tp_param.deprecated and hasval and 'deprecated' or tp_param.deprecated and noval and 'empty-deprecated' or not compatible(tp_param.type, value) and 'incompatible' or not series and already_seen[tp_param] and hasval and 'duplicate'
if hasval and table_name ~= 'duplicate' then already_seen[tp_param] = p_name end end -- report it. if table_name then res[table_name] = res[table_name] or if table_name
-- check for empty/missing parameters declared "required" for p_name, param in pairs(td_params) do if param.required and util.empty(t_args[p_name]) then local is_alias for _, alias in ipairs(param.aliases or) do is_alias = is_alias or not util.empty(t_args[alias]) end if not is_alias then res['empty-required'] = res['empty-required'] or res['empty-required'][p_name] = end end end mw.logObject(res) return resend
-- wraps report in hidden framefunction wrapReport(report, template_name, options) mw.logObject(report) if util.empty(report) then return end local naked = mw.title.new(template_name)['text'] naked = mw.ustring.gsub(naked, 'Infobox', 'infobox', 1) report = (options['wrapper-prefix'] or "
") .. report .. (options['wrapper-suffix'] or "") report = mw.ustring.gsub(report, 'tname_naked', naked) report = mw.ustring.gsub(report, 'templatename', template_name)
return reportend
-- this is the "user" version, called with returns a string, as defined by the options parameterfunction validateParams(frame) local options, report, template_name = util.extract_options(frame), , frame:getParent:getTitle
local ignore = function(p_name) for _, pattern in ipairs(options['ignore'] or) do if mw.ustring.match(p_name, '^' .. pattern .. '$') then return true end end return false end
local replace_macros = function(error_type, s, param_names) function concat_and_escape(t, sep) sep = sep or ', ' local s = table.concat(t, sep) return (mw.ustring.gsub(s, '%%', '%%%%')) end if s and (type(param_names)
'table' then v = table.concat(v, ', ') end if error_type
if mw.getCurrentFrame:preprocess("") ~= "" then s = mw.ustring.gsub(s, "
", "", 1) end end return s end
local report_params = function(key, param_names) local res = replace_macros(key, options[key], param_names) res = frame:preprocess(res or ) report = report .. (res or ) return res end
-- no option no work. if util.table_empty(options) then return end
-- get the errors. local violations = calculateViolations(frame, options['doc-subpage']) -- special request of bora: use skip_empty_numeric if violations['empty-undeclared-numeric'] then for i = 1, tonumber(options['skip-empty-numeric']) or 0 do violations['empty-undeclared-numeric'][i] = nil end end -- handle ignore list, and prune empty violations - in that order! local offenders = 0 for name, tab in pairs(violations) do -- remove ignored parameters from all violations for pname in pairs(tab) do if ignore(pname) then tab[pname] = nil end end -- prune empty violations if util.table_empty(tab) then violations[name] = nil end -- WORK IS DONE. report the errors. -- if report then count it. if violations[name] and report_params(name, tab) then offenders = offenders + 1 end end
if offenders > 1 then report_params('multiple') end if offenders ~= 0 then report_params('any') end -- could have tested for empty(report), but since we count them anyway... return wrapReport(report, template_name, options)end
return