אונטערשייד צווישן ווערסיעס פון "יחידה:ParamValidator"

2,413 בייטן צוגעלייגט ,  פֿאַר 2 יאָר
אידיש
ק (rv)
(אידיש)
 
(2 מיטלסטע ווערסיעס פון איין אנדער באַניצער נישט געוויזן.)
שורה 4: שורה 4:


the source of this module is in //he.wikipedia.org/wiki/Module:ParamValidator
the source of this module is in //he.wikipedia.org/wiki/Module:ParamValidator
main purpose: use "templatedata" to verify the parameters passed to a template
Terminology: "numeric parameter" means order-based parameter. e.g. if the template is transcluded like so {{x  | k |  | a = m | b = }}
"a" and "b" are "named" parameters, and there are 2 "numeric", or order based parameters, 1 and 2.
we say that the value of a is "m", the value of 1 is "k", and "b" and 2 are "empty".


This module exports two functions: calculateViolations( frame, subpages ), and validateParams( frame ).  
This module exports two functions: calculateViolations( frame, subpages ), and validateParams( frame ).  
שורה 35: שורה 41:
it expects a parameter named "options", which contains the definition of the output. typically, it's used by placing something like so:
it expects a parameter named "options", which contains the definition of the output. typically, it's used by placing something like so:


<includeonly>{{#invoke:ParamValidatoe | validateParams | options = {{PV default options}} }}</includeonly>
<includeonly>{{#invoke:ParamValidator | validateParams | options = {{PV default options}} }}</includeonly>


at the top of the template (be mindful not to add extra spaces and newlines to the template).
at the top of the template (be mindful not to add extra spaces and newlines to the template).
to bypass some mediawiki limitation, it is also possible to pass the options as "module", like so (use one of the two, but not both):
<includeonly>{{#invoke:ParamValidator | validateParams | module_options = Module:PV default options}} }}</includeonly>
the first form expects a template named "Template:PV default options" which contains the options, and the 2nd form expects a module,
suitable for mw.loadData(), which returns a map of namespace => options (i.e. { [0] = <options>, [2] => <options> } .... )


the options parameter should be a JSON-encoded string, defining the output, and some special behaviors.  
the options parameter should be a JSON-encoded string, defining the output, and some special behaviors.  
שורה 91: שורה 102:
typically, this JSON structure will be placed in a separate template, and retrieved for the module-use as shown above.
typically, this JSON structure will be placed in a separate template, and retrieved for the module-use as shown above.
<includeonly>{{#invoke:ParamValidatoe | validateParams | options = {{PV default options}} | options1 = {"key":"value"} }}</includeonly>
<includeonly>{{#invoke:ParamValidator | validateParams | options = {{PV default options}} | options1 = {"key":"value"} }}</includeonly>
"key" can override any of the options fields described above.
"key" can override any of the options fields described above.


שורה 102: שורה 113:
end
end
,  
,  
extract_options = function ( frame, optionsPrefix )
extract_options = function( frame, optionsPrefix )
optionsPrefix = optionsPrefix or 'options'  
optionsPrefix = optionsPrefix or 'options'  
שורה 111: שורה 122:
if type( module_options ) ~= 'table' then return {} end
if type( module_options ) ~= 'table' then return {} end
local title = mw.title.getCurrentTitle()
local title = mw.title.getCurrentTitle()
return module_options[ title.namespace ] or module_options[ title.nsText ] or {}  
local local_ptions = module_options[ title.namespace ] or module_options[ title.nsText ] or {}  
for k, v in pairs( local_ptions ) do options[k] = v end
end
end
שורה 147: שורה 159:
local capture =  templateContent and mw.ustring.match( templateContent, '<templatedata%s*>(.*)</templatedata%s*>' ) -- templatedata as text
local capture =  templateContent and mw.ustring.match( templateContent, '<templatedata%s*>(.*)</templatedata%s*>' ) -- templatedata as text
-- capture = capture and mw.ustring.gsub( capture, '"(%d+)"', tonumber ) -- convert "1": {} to 1: {}. frame.args uses numerical indexes for order-based params.
-- capture = capture and mw.ustring.gsub( capture, '"(%d+)"', tonumber ) -- convert "1": {} to 1: {}. frame.args uses numerical indexes for order-based params.
if capture then return pcall( mw.text.jsonDecode, capture ) end
local trailingComma = capture and mw.ustring.find( capture, ',%s*[%]%}]' ) -- look for ,] or ,} : jsonDecode allows it, but it's verbotten in json
if capture and not trailingComma then return pcall( mw.text.jsonDecode, capture ) end
return false
return false
end
end
שורה 167: שורה 180:
-- this is the function to be called by other modules. it expects the frame, and then an optional list of subpages, e.g. { "Documentation" }.
-- this is the function to be called by other modules. it expects the frame, and then an optional list of subpages, e.g. { "Documentation" }.
-- if second parameter is nil, only tempalte page will be searched for templatedata.
-- if second parameter is nil, only tempalte page will be searched for templatedata.
function calculateViolations( frame, subpages )
local function calculateViolations( frame, subpages )
 
-- used for parameter type validy test. keyed by TD 'type' string. values are function(val) returning bool.
-- used for parameter type validy test. keyed by TD 'type' string. values are function(val) returning bool.
local type_validators = {  
local type_validators = {  
['number'] = function( s ) return mw.language.getContentLanguage():parseFormattedNumber( s ) end
['number'] = function( s ) return mw.language.getContentLanguage():parseFormattedNumber( s ) end
}
}
function compatible( typ, val )
local function compatible( typ, val )
local func = type_validators[typ]
local func = type_validators[typ]
return type( func ) ~= 'function' or util.empty( val ) or func( val )
return type( func ) ~= 'function' or util.empty( val ) or func( val )
end
local function list_empty_or_contains(ar, searched)
if not ar or #ar == 0 then return true end
for _, val in ipairs(ar) do if val == searched then return true end end
return false
end
end
שורה 183: שורה 202:
local templatedata = readTemplateData( td_source )
local templatedata = readTemplateData( td_source )
local td_params = templatedata and templatedata.params
local td_params = templatedata and templatedata.params
local all_aliases = {}
local all_aliases, all_series = {}, {}
if not td_params then return { ['no-templatedata'] = { [''] = '' } } end
if not td_params then return { ['no-templatedata'] = { [''] = '' } } end
שורה 193: שורה 213:
for _, p in pairs( td_params ) do for _, alias in ipairs( p.aliases or {} ) do  
for _, p in pairs( td_params ) do for _, alias in ipairs( p.aliases or {} ) do  
all_aliases[alias] = p
all_aliases[alias] = p
if tonumber(alias) then all_aliases[tonumber(alias)] = p end
end end
end end
 
-- handle undeclared and deprecated
-- handle undeclared and deprecated
local already_seen = {}
local already_seen = {}
local series = frame.args['series']
for p_name, value in pairs( t_args ) do
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 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
if not tp_param then -- not in TD: this is called undeclared
שורה 205: שורה 237:
noval and numeric and 'empty-undeclared-numeric' or
noval and numeric and 'empty-undeclared-numeric' or
noval and not numeric and 'empty-undeclared' or
noval and not numeric and 'empty-undeclared' or
not noval and numeric and 'undeclared-numeric' or
hasval and numeric and 'undeclared-numeric' or
'undeclared' -- tzvototi nishar.
'undeclared' -- tzvototi nishar.
else -- in td: test for depracation and mistype. if deprecated, no further tests
else -- in td: test for deprecation and mistype. if deprecated, no further tests
table_name = tp_param.deprecated and not noval and 'deprecated'  
table_name = tp_param.deprecated and hasval and 'deprecated'  
or tp_param.deprecated and noval and 'empty-deprecated'  
or tp_param.deprecated and noval and 'empty-deprecated'  
or not compatible( tp_param.type, value ) and 'incompatible'  
or not compatible( tp_param.type, value ) and 'incompatible'  
or already_seen[tp_param] and 'duplicate'
or not series and already_seen[tp_param] and hasval and 'duplicate'
or hasval and not list_empty_or_contains(tp_param.suggestedvalues , value) and 'unsuggested-value'
 
already_seen[tp_param] = true
already_seen[tp_param] = hasval
end
end
-- report it.
-- report it.
שורה 221: שורה 255:
end
end
end
end
 
-- test for empty/missing paraeters declared "required"  
-- test for empty/missing paraeters declared "required"  
for p_name, param in pairs( td_params ) do  
for p_name, param in pairs( td_params ) do  
שורה 235: שורה 269:
return res
return res
end
-- wraps report in hidden frame
local function wrapReport(report, template_name, options)
if util.empty( report ) then return '' end
local naked = mw.title.new( template_name )['text']
mw.log(report)
report = ( options['wrapper-prefix'] or "<div class = 'paramvalidator-wrapper'><span class='paramvalidator-error'>" )
.. report
.. ( options['wrapper-suffix'] or "</span></div>" )
report = mw.ustring.gsub( report, 'tname_naked', naked )
report = mw.ustring.gsub( report, 'templatename', template_name )
return report
end
end


-- this is the "user" version, called with {{#invoke:}} returns a string, as defined by the options parameter
-- this is the "user" version, called with {{#invoke:}} returns a string, as defined by the options parameter
function validateParams( frame )
local function validateParams( frame )
-- for purple pages:
if frame:getParent().args['skip parameters validation'] then return '[[ קאטעגאריע:בלעטער מיט פאראמעטער פעלערן וואס זענען געווארן פארקוקט]]' end
local options, report, template_name = util.extract_options( frame ), '', frame:getParent():getTitle()
local options, report, template_name = util.extract_options( frame ), '', frame:getParent():getTitle()
local wrap_report = function()
if util.empty( report ) then return '' end
local naked = mw.title.new( template_name )['text']
report = ( options['wrapper-prefix'] or "<div class = 'paramvalidator-wrapper'>" )
.. report
.. ( options['wrapper-suffix'] or "</div>" )
report = mw.ustring.gsub( report, 'tname_naked', naked )
report = mw.ustring.gsub( report, 'templatename', template_name )
return report
end


local ignore = function( p_name )
local ignore = function( p_name )
שורה 262: שורה 301:


local replace_macros = function( s, param_names )
local replace_macros = function( s, param_names )
function concat_and_escape( t )  
local function concat_and_escape( t )  
local s = table.concat( t, ', ' )
local s = table.concat( t, ', ' )
return ( mw.ustring.gsub( s, '%%', '%%%%' ) )
return ( mw.ustring.gsub( s, '%%', '%%%%' ) )
שורה 311: שורה 350:
if offenders > 1 then report_params( 'multiple' ) 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...
if offenders ~= 0 then report_params( 'any' ) end -- could have tested for empty( report ), but since we count them anyway...
return wrap_report()
return wrapReport(report, template_name, options)
end
end


return {
return {
['validateparams'] = validateParams,
['validateparams'] = validateParams,
['calculateViolations'] = calculateViolations
['calculateViolations'] = calculateViolations,
['wrapReport'] = wrapReport
}
}