Semantic MediaWikiDit bericht verdwijnt nadat alle relevante taken zijn afgerond.

Er is één onvoltooide of openstaande taak, die nodig is om het bijwerken van Semantic MediaWiki te voltooien. Een beheerder of gebruiker met voldoende rechten kan deze uitvoeren. Dit moet worden gedaan voordat nieuwe gegevens worden toegevoegd om tegenstrijdigheden te voorkomen.

Module:FFSchema

Summary
Allows you to use JSON schemas to create forms.



--[[
FlexForm Schema - experiment of letting users define a form in JSON scheme

{{#invoke:FFSchema |wiki
|schema=Json:FFSchema23 // For a working example see Json:FF/Test1
|page=
|slot=jsonprops
|template=Text // without template, we're assuming the content is JSON (detect content model with WSSlots)? But what if we are in interested in the raw content?
}}

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

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 

]]--

local p = {}
local mFFInput = require('Module:FFInput')
--local mFFSelect = require('Module:FFSelect')
--local mFFToken = require('Module:FFToken')

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

	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
	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 ""
	nameWithoutBrackets = string.gsub( def.name or "", "%[%]", "" )
	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
		labelText = labelText .. ' <sup class="fa fa-asterisk text-danger" title="verplicht"></sup>'
	end
	labelTag = mw.text.tag( "label", { ["for"] = nameWithoutBrackets }, labelText )
	--mw.log( "label: " .. labelTag .. " ... " )
	tooltipText = def['_tooltip'] or nil
	itemClass = def["_itemClass"] or "item"
	local itemShowOnSelectTrigger = def["_show-on-select-trigger"] or def["_data-wssos-value"] or nil
	-- whether to ignore item layout:
	standalone = false

	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 == "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 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, itemClass, itemShowOnSelectTrigger )
		labelTag = nil
		return res
	end
end

--[[
@labelTag
@tooltipText string|nil
@itemClass string
@itemShowOnSelectTrigger nil|string
]]--
p.buildItemLayout = function( labelTag, tooltipText, inputField, itemClass, itemShowOnSelectTrigger )
	tooltip = ""
	if tooltipText ~= nil then
		-- smw tooltip
		local frame = mw.getCurrentFrame()
		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( 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 )
	itemStr = ""
	--name="Items" format="json"
	for _,def in ipairs( attributes['_fields'] or {} ) do
		itemStr = itemStr .. p.parseFieldDefinition( def, nil )
	end
	attributes = p.removeNonFFAttributes( attributes )
	contentDiv = mw.html.create("div")
		:attr( "class", "item-instance" )
		:wikitext( itemStr )
	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

--[[
Remove parameters that are not FlexForm attributes
i.e. removes each parameter whose key begins with an underscore
]]--
p.removeNonFFAttributes = function( attributes )
	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 )
	str = ""
	for k,v in pairs(tbl) do
		str = str .. k .. " = " .. v .. ". "
	end
	return str
end

return p