Geen bewerkingssamenvatting |
Geen bewerkingssamenvatting |
||
| Regel 7: | Regel 7: | ||
@frame table | @frame table | ||
- schema (olim json) - pagename of JSON schema | - schema (olim json) - pagename of JSON schema | ||
- | - page - title of existing page, or title of new page to be created | ||
- slot | - slot | ||
- template - wiki template if any. Without it, we're assuming the content is JSON (detect content model with WSSlots)? @todo: what if we are in interested in the raw content? | - template - wiki template if any. Without it, we're assuming the content is JSON (detect content model with WSSlots)? @todo: what if we are in interested in the raw content? | ||
fields only | |||
{{#invoke:FFSchema |wiki | {{#invoke:FFSchema |wiki | ||
|schema=Json:FFSchema23 | |schema=Json:FFSchema23 | ||
| | |mode=fields // can be omitted | ||
|page={{FULLPAGENAME}} | |page={{FULLPAGENAME}} | ||
|slot= | |slot=ws-page-props | ||
|template=Text | |||
}} | |||
new | |||
{{#invoke:FFSchema |wiki | |||
|schema=Json:FFSchema23 | |||
|mode=new | |||
|page=Titel/{{#tool:randomint|1000000|9999999}} | |||
|slot=ws-page-props | |||
|template=Text | |||
--@todo | |||
|classslot=ws-class-props | |||
}} | |||
edit | |||
{{#invoke:FFSchema |wiki | |||
|schema=Json:FFSchema23 | |||
|mode=edit | |||
|page={{FULLPAGENAME}} | |||
|slot=ws-page-props | |||
|template=Text | |template=Text | ||
}} | }} | ||
| Regel 37: | Regel 57: | ||
end | end | ||
local | -- modes: fields (fields only, default), new (create tags + fields), edit (create tag) | ||
local mode = frame.args.mode or "fields" | |||
local page = frame.args.page or "" | local page = frame.args.page or "" | ||
local slot = frame.args.slot or "main" | local slot = frame.args.slot or "main" | ||
| Regel 457: | Regel 478: | ||
fieldsetContent or "" | fieldsetContent or "" | ||
) | ) | ||
end | end | ||
| Regel 495: | Regel 502: | ||
end | end | ||
return fieldNames | return fieldNames | ||
end | |||
--[[ | |||
Remove parameters that are not FlexForm attributes | |||
i.e. removes each parameter whose key begins with an underscore | |||
]]-- | |||
p.removeNonFFAttributes = function( attributes ) | |||
local newAttributes = {} | |||
for k,v in pairs(attributes) do | |||
if ( string.sub( k, 1, 1 ) ~= "_" ) then | |||
newAttributes[k] = v | |||
end | |||
end | |||
return newAttributes | |||
end | end | ||
p.printAssocTable = function( tbl ) | p.printAssocTable = function( tbl ) | ||
str = "" | local str = "" | ||
for k,v in pairs(tbl) do | for k,v in pairs(tbl) do | ||
str = str .. k .. " = " .. v .. ". " | str = str .. k .. " = " .. v .. ". " | ||
Versie van 25 aug 2025 07:29
Module:FFSchema
Summary
Allows you to use JSON schemas to create forms.
Uses
local p = {}
local mFFInput = require('Module:FFInput')
--[[
main entry point - @todo rename wiki
@frame table
- schema (olim json) - pagename of JSON schema
- page - title of existing page, or title of new page to be created
- slot
- template - wiki template if any. Without it, we're assuming the content is JSON (detect content model with WSSlots)? @todo: what if we are in interested in the raw content?
fields only
{{#invoke:FFSchema |wiki
|schema=Json:FFSchema23
|mode=fields // can be omitted
|page={{FULLPAGENAME}}
|slot=ws-page-props
|template=Text
}}
new
{{#invoke:FFSchema |wiki
|schema=Json:FFSchema23
|mode=new
|page=Titel/{{#tool:randomint|1000000|9999999}}
|slot=ws-page-props
|template=Text
--@todo
|classslot=ws-class-props
}}
edit
{{#invoke:FFSchema |wiki
|schema=Json:FFSchema23
|mode=edit
|page={{FULLPAGENAME}}
|slot=ws-page-props
|template=Text
}}
Field definitions accept two types of parameters:
- those that are native to FlexForm
- additional parameters beginning with an underscore (_) that can assist in some tedious tasks, including _type, _label, _default,_fields _options, _optionsep, _fields, _instance, _note (before), _tooltip, _itemClass, _htmlElement and _content.
with select/token:
- with _options you can set 'arrays' of either single values or pairs of 'label' and 'value'
At the time of writing, FlexForm is not stable with regard to its JavaScript implementation of instances
]]--
p.wiki = function(frame)
local schema = frame.args.schema or frame.args.json or ""
local schemaTbl = mw.loadJsonData( schema )
if schemaTbl == nil then
return ""
end
-- modes: fields (fields only, default), new (create tags + fields), edit (create tag)
local mode = frame.args.mode or "fields"
local page = frame.args.page or ""
local slot = frame.args.slot or "main"
local template = frame.args.template or nil
if ( template ~= nil and template ~= "" ) then
sourceFormat = "wiki"
else
sourceFormat = "json"
end
-- get content for autofill
local slotContent = {}
if page ~= "" or page ~= nil then
if sourceFormat == "wiki" then
slotContent = p.getSlotContent( page, slot, template )
else
slotContent = p.getSlotContent( page, slot, nil )
end
end
if type(slotContent) ~= "table" then
mw.log( "No suitable content recognised." )
slotContent = nil
end
-- a field is a form field definition
local fieldDefinitions = schemaTbl.fields or nil
if fieldDefinitions == nil then return "" end
local str = p.buildFromFieldDefinitions( fieldDefinitions, sourceFormat, slotContent, false )
-- mw.log( "FlexForm result: " .. mw.text.unstrip( str ) )--
return frame:preprocess(str)
end
--[[
@fieldDefinitions table
@sourceFormat string
@slotContent
]]--
p.buildFromFieldDefinitions = function( fieldDefinitions, sourceFormat, slotContent, isNested )
--frame = mw.getCurrentFrame()
--str = ""
local res = {}
local fieldDefinitions = fieldDefinitions or {}
for _,def in ipairs(fieldDefinitions) do
local f
if def["_type"] == "fieldset" and def["_fields"] ~= nil then
-- ! needs to be tested -- inputType = def["_type"] or nil
-- @todo allow other custom definitions
f = p.createPopulatedFieldset(
def,
p.buildFromFieldDefinitions( def["_fields"], sourceFormat, slotContent, true )
)
-- str = str .. fieldset
--table.insert(res, fieldset)
else
f = p.buildFromFieldDefinition( def, sourceFormat, slotContent )
end
table.insert(res, f )
end
return table.concat(res,"")
--return str
end
--[[
@def table
@sourceFormat
@slotContent
]]--
p.buildFromFieldDefinition = function( def, sourceFormat, slotContent )
if def.name ~= nil then
nameWithoutBrackets = string.gsub( def.name, "%[%]", "" )
else
nameWithoutBrackets = ""
end
local val = nil
if nameWithoutBrackets == "" then
--do nothing
elseif sourceFormat == "wiki" then
-- todo could more efficient
--mw.log( "wiki content " )
if ( slotContent ~= nil and slotContent[1] ~= nil and slotContent[1][nameWithoutBrackets] ~= nil ) then
--[param]["_text"]
val = slotContent[1][nameWithoutBrackets]["_text"] or nil
if val == "" or val == nil then
val = def["_default"] or nil end
else
val = def["_default"] or nil
end
elseif sourceFormat == "json" and slotContent ~= nil then
--mw.log( "JSON content " )
--mw.log( "type: " .. type(slotContent[nameWithoutBrackets]) .. "<br>" )
--val may be string, table or nil
--mw.log( slotContent[nameWithoutBrackets] )
val = slotContent[nameWithoutBrackets] or nil
if val == "" or val == nil then
val = def["_default"] or nil end
else
mw.log( "No content detected " )
end
if type(val) ~= "table" then
mw.log( "val: " .. ( val or "(none)" ) )
end
return p.parseFieldDefinition( def, val )
end
--[[
@def
@selectedVal string, table (JSON instance only) or nil
@boolean
]]--
p.parseFieldDefinition = function( def, selectedVal )
--name = def.name or ""
local nameWithoutBrackets = string.gsub( def.name or "", "%[%]", "" )
local inputType = def["_type"] or nil
selectedVal = selectedVal or nil
-- @todo revise 'for'
local labelText = def['_label'] or ""
if def["required"] ~= nil and def["required"] == "required" then
-- @todo localisation, make choice configurable, not dependent on Bootstrap
labelText = labelText .. ' <sup class="fa fa-asterisk text-danger" title="verplicht"></sup>'
end
local labelTag = mw.text.tag( "label", { ["for"] = nameWithoutBrackets }, labelText )
--mw.log( "label: " .. labelTag .. " ... " )
local tooltipText = def['_tooltip'] or nil
local itemClass = def["_itemClass"] or "ff-item"
if def["_type"] == "instance" then
itemClass = itemClass .. " ff-item-instances"
end
local itemShowOnSelectTrigger = def["_show-on-select-trigger"] or def["_data-wssos-value"] or nil
-- note before input
local beforeInputField = ""
local note = def['_note'] or nil
if note ~= nil and note ~= "" then
local currentFrame = mw.getCurrentFrame()
beforeInputField = "<div class='ff-note'>" .. currentFrame:preprocess(note) .. "</div>"
end
-- whether to ignore item layout:
local standalone = false
local attributes = {}
for k,v in pairs( def ) do
attributes[k] = v or ""
end
local inputField = ""
if inputType == "instance" or inputType == "instances" then
local instanceLabelTag = labelTag
if attributes.template ~= nil then
-- wiki template version
if selectedVal ~= nil then
attributes["default-content"] = mw.text.nowiki( selectedVal )
end
inputField = p.createTemplateInstance( attributes )
else
-- JSON version
selectedVal = selectedVal or nil
if type(selectedVal) == "table" then
attributes["default-content"] = mw.text.nowiki( mw.text.jsonEncode( selectedVal ) )
elseif type(selectedVal) == "string" then
-- shouldn't happen but..
attributes["default-content"] = mw.text.nowiki( selectedVal )
end
inputField = p.createJsonInstance( attributes )
end
-- todo
-- attributes.fields
--??
labelTag = instanceLabelTag
elseif inputType == "text" then
-- @todo default class
if ( attributes.class == nil ) then
attributes.class = "form-control"
end
selectedVal = selectedVal or nil
inputField = mFFInput.createTextInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "number" then
-- @todo default class
if ( attributes.class == nil ) then
attributes.class = "form-control"
end
selectedVal = selectedVal or nil
inputField = mFFInput.createNumberInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "date" then
if ( attributes.class == nil ) then
attributes.class = "form-control"
end
selectedVal = selectedVal or nil
inputField = mFFInput.createDateInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "hidden" then
--todo - temporary solutipn
itemClass = "d-none"
selectedVal = selectedVal or nil
inputField = mFFInput.createTextInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "textarea" then
-- @todo default class
if attributes.class == nil then
attributes.class = "form-control"
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or ""
inputField = mFFInput.createTextarea( nameWithoutBrackets, attributes, selectedVal)
elseif ( inputType == "token" or inputType == "tokens" ) then
-- @todo just an example
-- @todo remove stuff from attributes that does not belong
-- run attributes through a validator
local options = {}
if ( attributes['_options'] ~= nil and attributes['_options'] ~= "" ) then
options = attributes['_options']
if type(options) == "string" then
-- expand wikitext
local currentFrame = mw.getCurrentFrame()
local optionText = currentFrame:preprocess( options )
-- @todo what's a good for default for _optionsep (semi-colon?)
local optionSep = attributes['_optionsep'] or "\n"
options = mw.text.split( optionText, optionSep )
end
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createTokenField( nameWithoutBrackets, attributes, selectedVal, options )
--mw.log( "FlexForm token: " .. inputField )
elseif inputType == "select" then
if ( attributes['_options'] ~= nil and attributes['_options'] ~= "" ) then
options = attributes['_options']
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
--mw.log( selectedVal or "select : selectedVal: none!" )
inputField = mFFInput.createSelectField( nameWithoutBrackets, attributes, selectedVal, options )
--mw.log( "FlexForm select: " .. inputField )
elseif inputType == "checkboxes" or inputType == "checkbox" then
if ( attributes["_options"] ~= nil and attributes["_options"] ~= "" ) then
options = attributes["_options"]
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createCheckInputField( "checkbox", nameWithoutBrackets, attributes, selectedVal, options )
elseif inputType == "radio" then
if ( attributes["_options"] ~= nil and attributes["_options"] ~= "" ) then
options = attributes["_options"]
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createCheckInputField( "radio", nameWithoutBrackets, attributes, selectedVal, options )
elseif inputType == "rating" then
local steps = nil
if attributes["_options"] ~= nil then
-- semi-colon separated list from 0.5;
steps = attributes["_options"]
end
inputField = mFFInput.createRatingField( nameWithoutBrackets, selectedVal, steps, "ster", "sterren", "true", "Annuleren" )
-- @todo make configurable - starLabel, starsLabel, isCancellable, cancelTitle, static
elseif attributes["_htmlElement"] ~= nil then
-- method closest to FlexForm's JSON schema
local el = attributes["_htmlElement"]
local content = attributes["_content"] or ""
local sosTrigger = attributes["show-on-select-trigger"] or attributes["data-wssos-value"] or nil
if sosTrigger ~= nil then
-- because Lua somehow throws away this attribute
attributes["data-wssos-value"] = sosTrigger
end
local attributes = p.removeNonFFAttributes( attributes )
inputField = mw.text.tag( el, attributes, content )
--[[
inputFieldTbl = mw.html.create( el )
:attr( attributes )
:wikitext( content )
inputField = tostring(inputFieldTbl)
]]--
standalone = true
end
-- item
-- @todo id and show-on-select id on
if standalone then
local res = inputField
return res
else
local res = p.buildItemLayout( labelTag, tooltipText, inputField, beforeInputField, itemClass, itemShowOnSelectTrigger )
labelTag = nil
return res
end
end
--[[
@labelTag
@tooltipText string|nil
@inputField
@beforeInputField - optional short message to be inserted just before the input
@itemClass string
@itemShowOnSelectTrigger nil|string
]]--
p.buildItemLayout = function( labelTag, tooltipText, inputField, beforeInputField, itemClass, itemShowOnSelectTrigger )
local tooltip = ""
if tooltipText ~= nil then
-- smw tooltip
local frame = mw.getCurrentFrame()
-- @todo create tooltip which does not depend on SMW's info
tooltip = frame:callParserFunction( '#info', { tooltipText } )
end
labelDiv = mw.html.create( "div" )
:attr( "class", "item-label" )
:wikitext( labelTag .. tooltip )
--labelItem = "<div class='item-label'>" .. labelTag .. "</div>"
labelItem = tostring(labelDiv)
fieldDiv = mw.html.create( "div" )
:attr( "class", "item-field" )
:wikitext( beforeInputField .. inputField )
fieldItem = tostring(fieldDiv)
resDiv = mw.html.create( "div" )
:attr( "class", itemClass )
:wikitext( labelItem .. fieldItem )
if itemShowOnSelectTrigger ~= nil then
-- show-on-select-trigger
resDiv:attr( "data-wssos-value", itemShowOnSelectTrigger )
end
return tostring(resDiv)
end
p.createTemplateInstance = function( attributes )
itemStr = ""
--frame = mw.getCurrentFrame()
for _,def in ipairs( attributes['_fields'] or {} ) do
-- @todo defaults?
--val = def["_default"] or nil
--don't use 'val' arg because the instance takes care of this
itemStr = itemStr .. p.parseFieldDefinition( def, nil )
end
attributes = p.removeNonFFAttributes( attributes )
--content = "<div class='item-instance'>" .. itemStr .. "</div>"
contentDiv = mw.html.create("div")
:attr( "class", "item-instance" )
:wikitext( itemStr )
content = tostring(contentDiv)
return mw.text.tag( "_instance", attributes, content )
end
-- Identical to createTemplateInstance, currently
p.createJsonInstance = function( attributes )
local itemStr = ""
--name="Items" format="json"
for _,def in ipairs( attributes['_fields'] or {} ) do
itemStr = itemStr .. p.parseFieldDefinition( def, nil )
end
local attributes = p.removeNonFFAttributes( attributes )
local contentDiv = mw.html.create("div")
:attr( "class", "item-instance" )
:wikitext( itemStr )
local content = tostring(contentDiv)
return mw.text.tag( "_instance", attributes, content )
end
--[[
@page string - full name of page
@slot - name of slot
@template - name of the template (without NS prefix)
@return table
]]--
p.getSlotContent = function( page, slot, template )
template = template or nil
if ( template == "" or template == nil ) then
-- Assuming JSON
if slot == "main" then
--loadJsonData does not alway work. Use WSSlots instead
--return mw.loadJsonData( page )
end
local str = mw.slots.slotContent( slot, page )
if type(str) == "table" then
-- @todo VERY unlikely
return str
end
if str ~= nil then
return mw.text.jsonDecode( str )
else
return {}
end
end
-- template
slotData = mw.slots.slotData( slot, page )
if ( slotData == nil ) then
return type(slotData)
end
return slotData[template]
end
p.showSlotContent = function( frame )
page = frame.args.page
slot = frame.args.slot or "main"
template = frame.args.template or nil
content = p.getSlotContent( page, slot, template )
str = mw.text.jsonEncode( content )
return str
end
--[[
if ( content == nil ) then
str = mw.slots.slotContent( slot, page )
return "content seems to be nil . " .. st
end
return "<pre>" .. mw.text.jsonEncode( content ) .. "</pre>"
]]--
p.createPopulatedFieldset = function( def, fieldsetContent )
local def = def or {}
local fieldsetContent = fieldsetContent or ""
local fieldDefs = def["_fields"] or {}
local itemShowOnSelectTrigger = def["_show-on-select-trigger"] or def["_data-wssos-value"] or nil
if itemShowOnSelectTrigger ~= nil then def["data-wssos-value"] = itemShowOnSelectTrigger end
return mw.text.tag(
"fieldset",
p.removeNonFFAttributes( def ),
fieldsetContent or ""
)
end
-- work in progress
p.createCreateTag = function( page, attributes, fields )
-- attributes["mwwrite"] = page
-- local fieldsTbl = p.getFieldNames( fields )
-- attributes["mwfields"] =
return mw.text.tag( "create", attributes, false )
end
--[[
Get the field names so that they can be added to 'mwfields'
@fields table
@return table
]]--
p.getFieldNames = function( fields )
local fieldNames
for _,field in ipair(fields) do
if field.name ~= nil then
local fieldName = mw.text.trim(field.name)
table.insert(fieldNames, fieldName)
end
end
return fieldNames
end
--[[
Remove parameters that are not FlexForm attributes
i.e. removes each parameter whose key begins with an underscore
]]--
p.removeNonFFAttributes = function( attributes )
local newAttributes = {}
for k,v in pairs(attributes) do
if ( string.sub( k, 1, 1 ) ~= "_" ) then
newAttributes[k] = v
end
end
return newAttributes
end
p.printAssocTable = function( tbl )
local str = ""
for k,v in pairs(tbl) do
str = str .. k .. " = " .. v .. ". "
end
return str
end
return p
