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

2,375 בייטן אראפגענומען ,  פֿאַר 3 יאָר
ק
rv
ק (שינה את הגדרות ההגנה של הדף "יחידה:ParamValidator": טיפול מונע (וק:במ) ([עריכה=רק עורכי תבניות או ממשק מורשים] (בלתי מוגבלת בזמן) [העברה=רק עורכי תבניות או ממשק מורשים] (בלתי מוגבלת בזמן)))
 
ק (rv)
שורה 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 ).  
שורה 41: שורה 35:
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:ParamValidator | validateParams | options = {{PV default options}} }}</includeonly>
<includeonly>{{#invoke:ParamValidatoe | 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.  
שורה 102: שורה 91:
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:ParamValidator | validateParams | options = {{PV default options}} | options1 = {"key":"value"} }}</includeonly>
<includeonly>{{#invoke:ParamValidatoe | 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.


שורה 113: שורה 102:
end
end
,  
,  
extract_options = function( frame, optionsPrefix )
extract_options = function ( frame, optionsPrefix )
optionsPrefix = optionsPrefix or 'options'  
optionsPrefix = optionsPrefix or 'options'  
שורה 122: שורה 111:
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()
local local_ptions = module_options[ title.namespace ] or module_options[ title.nsText ] or {}  
return module_options[ title.namespace ] or module_options[ title.nsText ] or {}  
for k, v in pairs( local_ptions ) do options[k] = v end
end
end
שורה 159: שורה 147:
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.
local trailingComma = capture and mw.ustring.find( capture, ',%s*[%]%}]' ) -- look for ,] or ,} : jsonDecode allows it, but it's verbotten in json
if capture then return pcall( mw.text.jsonDecode, capture ) end
if capture and not trailingComma then return pcall( mw.text.jsonDecode, capture ) end
return false
return false
end
end
שורה 180: שורה 167:
-- 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.
local function calculateViolations( frame, subpages )
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
שורה 202: שורה 183:
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, all_series = {}, {}
local all_aliases = {}
if not td_params then return { ['no-templatedata'] = { [''] = '' } } end
if not td_params then return { ['no-templatedata'] = { [''] = '' } } end
שורה 213: שורה 193:
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
שורה 237: שורה 205:
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
hasval and numeric and 'undeclared-numeric' or
not noval and numeric and 'undeclared-numeric' or
'undeclared' -- tzvototi nishar.
'undeclared' -- tzvototi nishar.
else -- in td: test for deprecation and mistype. if deprecated, no further tests
else -- in td: test for depracation and mistype. if deprecated, no further tests
table_name = tp_param.deprecated and hasval and 'deprecated'  
table_name = tp_param.deprecated and not noval 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 not series and already_seen[tp_param] and hasval and 'duplicate'
or already_seen[tp_param] and 'duplicate'
or hasval and not list_empty_or_contains(tp_param.suggestedvalues , value) and 'unsuggested-value'
 
already_seen[tp_param] = hasval
already_seen[tp_param] = true
end
end
-- report it.
-- report it.
שורה 255: שורה 221:
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  
שורה 269: שורה 235:
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
local function validateParams( frame )
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 )
שורה 301: שורה 262:


local replace_macros = function( s, param_names )
local replace_macros = function( s, param_names )
local function concat_and_escape( t )  
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, '%%', '%%%%' ) )
שורה 350: שורה 311:
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 wrapReport(report, template_name, options)
return wrap_report()
end
end


return {
return {
['validateparams'] = validateParams,
['validateparams'] = validateParams,
['calculateViolations'] = calculateViolations,
['calculateViolations'] = calculateViolations
['wrapReport'] = wrapReport
}
}
אַנאנימער באַניצער