Modul:Classgenerator/parameter/class

This is [[MediaWiki:Tagline]]. Set to <code>display:none</code> by chameleon skin.
< Modul:Classgenerator‎ | parameter
Version vom 8. August 2018, 06:52 Uhr von imported>Oetterer
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu:Navigation, Suche
Documentation icon Module documentation[view] [edit] [history] [purge]

This is a Class Module. It implements a class in Lua using Module:Middleclass and the template class Module:Foundationclass. This class provides methods for adding and editing of a class's parameters.

Usage

local name = args[1] or args.name or mw.title.getCurrentTitle().rootText
local Class = require('Module:Name/class')
local me = Class:new(name)
me:initFromArgs(args)
me:storeData()
me:addInfobox()
me:addPageBody()
return me:render()


Data Storage

This template does not store any persistent data.

Methods

Constructor

new(uid)

Creates a new Object for the class.

uid
variable, optional
used to identify the object
return
object, of the class

Public methods

myArgumentProcessing(coreData)

Performs the individual argument processing in initFromArgs(args) right before inititializing it.

coreData
table, mandatory
the core data of the object. the one to process individually before initialization
return
table, the new core data array to be used

myPlausibilityTest(args)

Performs the individual plausibility tests in initFromArgs(args) before entering the initialization part. NOTE: The return value will be ignored. If this finds errors, it must add them to the list of errors via addError(errortext).

args
table, mandatory
arguments passed to the template to be checked
return
boolean, whether the object is plausible or not.

myStashAdjustments(self, stash)

Performs the adjusts the stash in storeData() right before storing it.

stash
table, mandatory
the array of data to be saved (in the form fieldname: value)
return
boolean, the new stash

render()

Produces the appropriate output for this parameter

return
boolean, whether adding was successful or not

static methods

ClassgeneratorParameter:mySfDynamicFieldAttribute(fieldname, attribute, value)

For some semantic form fields there are attribute values, that are not static, thus can not be provided at forehand in the configuration file. This method does the trick to adapt them at runtime shortly before the field is rendered. Essentially: the method checks, if it has a special rule for the pair fieldname:attribute and if so, calculates the new value. if not, the old value is returned.

fieldname
string, mandatory
the form field's name, needed to match current paring to an existing special treatment rule
attribute
string, mandatory
the form field's attribute, needed to match current paring to an existing special treatment rule
value
variable, mandatory
the value, that is already provided in the configuration file. this will be adapted by the method, if there is a special rule for the pair fieldname:attribute.
return
string, the value to be used forthwith (which can be the old one or a freshly calculated)

private methods

_debug(self, level, text)

Adds output to the internal debug log.

self
object, me
level
int; mandatory
debug level for the message
text
string; mandatory
debug message text
return
void

Properties

static

See also Static Properties

ClassgeneratorParameter.myConfiguration
this is your configuration. It is devided in several section, each a
table, holds configuration data found in Module:Name/config
WARNING: This is a read only table and besides functions pairs() and ipairs() nothing else will work on it, especially not the functions of the table library!
    • form, table, holds data used to create the form. here are some of them:
      • formButtons, table, which buttons should be printed at the bottom
      • fieldOrder, table, in which order the form fields should appear (note: only fields listed here will be added to the form)
    • global, table, holds some global configuration data
    • parameter, table, holds all data about all parameter used in the module (be they form only, data store only or normal). The table has the form paramname = { table of paramdata }. The tables for the parameter have data as follows:
      • cardinality, string, mandatory, one of singe or list
      • cargo_type, string, optional, if the parameter is to be stored in cargo, add the field type here (one of Page, Text, Integer, Float, Date, Datetime, Boolean (which should be provided as 1 or 0), Coordinates, Wikitext, File, String, URL, or Email)
      • description, string, mandatory, a description of this parameter
      • label, string, mandatory, the label used in the form and the template documentation
      • severity, string, optional, if provided one of mandatory, suggested
      • sf, table, optional, used to add more attributes to the semantic forms field. ref Module:SFfield/config for all possible attributes. Note that the table is of type attribute_name : value. Value can be of type string, integer, boolean, and (in case of 'show on select') a table. Please note, that the attribute name cannot contain a " " (space). Use the underscore instead.
      • td_default, string, optional, if you want a default value to be indicated in the template data section on the documentation page, add it here
      • td_type, string, optional, if the parameter should be present in the template documentation page, fill this field. all possible values are listed here
      • values, table, optional, if you want the possible values to be restricted to a specific set, fill this table
    • template, table, holds some data used only in the template
ClassgeneratorParameter.publicStaticProperty
type, explanation
_privateStaticProperty
type, explanation

private

Note: all private properties are stored in table _private[self]. _private is a static array that is indexed by self, so the table _private[self] holds all properties for instance self.

dbg
object, my instance of Module:Debug/class for debugging purposes. only present afer first call of _debug(self, level, text)

Configuration Data

This class holds its control data in Module:Classgenerator/parameter/config.

Inheritance

Note: You still reference those either by self:publicMethod() or ClassgeneratorParameter:staticMethod() and ClassgeneratorParameter.staticProperty respectively!

Methods

Constructor

new(uid, superhandler)

Creates a new Object for the class.

uid
variable, suggested
used to identify the object. defaults to mw.title.getCurrentTitle().prefixedText
superhandler
object of childclass of Foundationclass, optional
supply the superhandler, if you instanciate an object within another object. the instanciated object will pass errors and warnings to the superhandler and forgo adding an object category
return
object, of the class

Static Properties

FoundationClass.globalConfig
table, holds some global configuration (data, that applies to all classes the same way). See Module:Foundationclass/globalconfig for details.
FoundationClass.myCargoUtil
table, instance of Module:CargoUtil
FoundationClass.mySwmUtil
table, instance of Module:SmwUtil
FoundationClass.myTableTools
table, instance of Module:TableTools
FoundationClass.myYesno
table, instance of Module:Yesno

local FoundationClass = require('Module:Foundationclass')
local ClassDebug = require('Module:Debug/class')

-- ****************************************************************
-- *                         inheritance                          *
-- ****************************************************************
local ClassgeneratorParameter = FoundationClass:subclass('ClassgeneratorParameter')
-- setting class's configuration data
ClassgeneratorParameter.static.myConfiguration = mw.loadData('Module:Classgenerator/parameter/config')
ClassgeneratorParameter.static.SFfieldConfig = mw.loadData('Module:SFfield/config')
--ClassgeneratorParameter.static.myConfiguration = require('Module:Classgenerator/parameter/config')
--local SFfieldConfig = mw.loadData('Module:SFfield/config')
--ClassgeneratorParameter.static.myConfiguration.parameter.input_type.values = SFfieldConfig.validInputTypes	-- cant load a table from another config in my config. therefor this
--ClassgeneratorParameter.static.myConfiguration.parameter.base_type.values = SFfieldConfig.validInputTypes	-- cant load a table from another config in my config. therefor this
--   being in a static method, use self.myConfiguration
--   being in a private method, that knows self or in a public method, use self.class.myConfiguration

-- ****************************************************************
-- *                          properties                          *
-- ****************************************************************

-- **************** initialization of table for private properties
local _private = setmetatable({}, {__mode = 'k'})   -- weak table storing all private attributes

-- **************** declaration of public static properties
-- ClassgeneratorParameter.static.myPropertyModule = require('Module:extern')
-- ClassgeneratorParameter.static.staticProperty = ' '

-- remember the static classes provided by Foundationclass:
-- ClassgeneratorParameter.globalConfig
-- ClassgeneratorParameter.myCargoUtil
-- ClassgeneratorParameter.myTableTools

-- **************** declaration of (global) private properties
-- for properties you should rather use constructor and _private[self]. this only, if you need a private class property
-- you should, however predeclare private methods here
local _debug	-- private method declared later
local _privateMethodAhead	-- declaration ahead, so this private method can be used in the constructor and in other private methods

-- ***************************************************************
-- *                           methods                           *
-- ***************************************************************
 
-- **************** declaration of static methods
function ClassgeneratorParameter:initialize(uid)
	local _CFG = self.class.myConfiguration
	FoundationClass.initialize(self, uid)
	_private[self] = {
		dbg = ClassDebug:new(tostring(self.class) .. ': id ' .. uid),
	}
	_debug(self, 1, ' ClassgeneratorParameter: done initializing object &quot;' .. uid ..'&quot;, from ' .. tostring(self))
end

-- use use self.myConfiguration to access your configuration in a static method
function ClassgeneratorParameter.static:aStaticMethod(var)
	_debug(self, 1, 'entering ClassgeneratorParameter.static:aStaticMethod() to do something, from ' .. tostring(self))
end

function ClassgeneratorParameter.static:mySfDynamicFieldAttribute(fieldname, attribute, value)
	_debug(self, 1, 'entering ClassgeneratorParameter.static:mySfDynamicFieldAttribute(fieldname, attribute, value), from ' .. tostring(self))
	_debug(self, 2, ' with parameters &quot;' .. fieldname .. '&quot;, &quot;' .. attribute .. '&quot; and a ' .. type(value) .. ' value')
	-- function that can process any attribute/value pair just before rendering the semantic forms field
	-- usually done, to generate a dynamic 'default' value
	-- keep in mind: you can completely disable a form field, if you return true on attribute "disable". note however, that this causes the field to not show at all, no data will be transported/saved
	local val = value
	if FoundationClass.globalConfig.dataStore ~= 'cargo' and FoundationClass.globalConfig.dataStore ~= 'both' then
		-- disable all cargo fields
		if fieldname == 'cargo' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'cargo_hidden' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'cargo_size' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'cargo_type' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'add_cargo_table_and_field' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'mapping_cargo_field' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'mapping_cargo_table' and attribute == 'disable' then
			val = true
		end
	end
	if FoundationClass.globalConfig.dataStore ~= 'smw' and FoundationClass.globalConfig.dataStore ~= 'both' then
		-- disable all smw fields
		if fieldname == 'property' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'property_name' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'property_type' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'unique_for_concept' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'mapping_property' and attribute == 'disable' then
			val = true
		end
		if fieldname == 'values_from_concept' and attribute == 'disable' then
			val = true
		end
	end
	if fieldname == 'property_name' and attribute == 'values_from_namespace' then
		local ret, nulProperty = pcall(mw.title.new, 'NUL', 'Property')
		val = ret and mw.site.namespaces[nulProperty.namespace].name or 'NUL'
	end
	if fieldname == 'property_type' and attribute == 'values' then
		val = FoundationClass.globalConfig.smwPropertyTypesAvailable
	end
	if fieldname == 'input_type' and attribute == 'show_on_select' then
		-- build input type's show on select from SFfieldConfig.validAttributesPerType, except remove values and replace all spaces in attribute names by an underscore
		--val = mw.clone(ClassgeneratorParameter.SFfieldConfig.validAttributesPerType)
		--val = ClassgeneratorParameter.myTableTools.shallowClone(ClassgeneratorParameter.SFfieldConfig.validAttributesPerType)
		val = {}
		for param, attrlist in pairs(ClassgeneratorParameter.SFfieldConfig.validAttributesPerType) do
			if not val[param] then
				val[param] = {}
			end
			for k, attr in pairs(attrlist) do
				if attr ~= 'values' then
					val[param][k] = mw.ustring.gsub(attr, ' ', '_')
				end
			end
		end
		_debug(self, 2, ' calculated this monster: <pre>' .. ClassgeneratorParameter.myTableTools.printTable(val) .. '</pre>')
	end
	if fieldname == 'input_type' and attribute == 'values' then
		return ClassgeneratorParameter.SFfieldConfig.validInputTypes
	end
	if fieldname == 'base_type' and attribute == 'values' then
		return ClassgeneratorParameter.SFfieldConfig.validInputTypes
	end
	if fieldname == 'uploadable' and attribute == 'show_on_select' then
		val = {}
		val['true'] = {'default_filename', 'image_preview'}
	end
	_debug(self, 3, '  returning ' .. (type(val) == 'boolean' and (val and 'true' or 'false') or type(val)))
	return val	-- this value will be used as new value for field's attribute
end

function ClassgeneratorParameter.static:sfGenerateForm(embedIn)
	_debug(self, 1, 'entering ClassgeneratorParameter.static:sfGenerateForm()')
	local _CFG = self.myConfiguration
	local embedIn = embedIn or 'Classgenerator[parameter_parameter]'
	local tagStart = '{{{'
	local tagEnd = '}}}'
	-- create the table
	-- start the html object
	html = mw.html.create('')
	html:wikitext(tagStart .. 'for template|' .. _CFG.template.name .. '|label=' .. _CFG.global.entityTitle ..
		'|embed in field=' .. embedIn .. '|multiple|add button text=' .. _CFG.form.labelCreate .. '|minimum instances=1' .. tagEnd)
		:node(self:sfGenerateFormTable(self.myConfiguration.form.fieldOrderGeneral))
		:newline()
		:wikitext(':')
		:node(self:sfGenerateFormTable(self.myConfiguration.form.fieldOrderSFgeneral, 'row_parameter'))
		:newline()
		:wikitext('::')
		:node(self:sfGenerateFormTable(self.myConfiguration.form.fieldOrderSFtypeDependent))
		:wikitext(tagStart .. 'end template' .. tagEnd)
		:newline()
	return tostring(html)
end

-- **************** declaration of private methods
-- use self.class.myConfiguration to access your configuration in a public or a private method that is called by a public method
_debug = function (self, level, text)
	if _private[self] and _private[self].dbg then
		_private[self].dbg:log(level, text)
	else
		ClassDebug:log(level, text, tostring(self) .. '.static')
	end
end

local _processAttribute = function (self, attr, indent)
	_debug(self, 1, 'entering private _processAttribute() to print a line for attribute ' .. attr .. ', from ' .. tostring(self))
	local coreData = self:getCoreData()
	local ret = ''
	local omissionList = {'name'}
	local attr = attr
	local val = coreData[attr]
	if type(val) == 'boolean' then
		val = val and 'true' or 'false'
	end
	if val and not ClassgeneratorParameter.myTableTools.inTable(omissionList, attr) then
		ret = indent .. attr .. ' = '
		if type(val) == 'table' then
			ret = ret .. ClassgeneratorParameter.myTableTools.printTable(val, 'inline')
		elseif val == 'true' or val == 'false' or attr == 'cargo_table' or (attr == 'restricted' and (val == 'global.restrictedTo' or val == 'superglobal.restrictedTo')) or mw.ustring.match(val, '^[0-9]+$') then
			ret = ret .. val
		else
			local valMangled = mw.text.trim(val)
			valMangled = mw.ustring.gsub(mw.ustring.gsub(valMangled, "\\'", "'"), "'", "\\'")
			valMangled = mw.ustring.gsub(valMangled, '\n', '\\n\' ..\n' .. indent .. '\t\'')
			ret = ret .. "'" .. valMangled .. "'"	-- the gsub catches multiline input from textareas
		end
		ret = ret .. ','
		if self.class.myConfiguration.template.addCommentsToConfig then
			ret = ret .. '\t-- ' .. self.class.myConfiguration.parameter[attr].description
		end
		ret = ret .. '\n'
	end
	return ret
end

local _privateMethod = function (self)
	_debug(self, 1, 'entering private _privateMethod() to do something, from ' .. tostring(self))
end

-- **************** declaration of public methods
-- use self.class.myConfiguration to access your configuration in a public method
function ClassgeneratorParameter:addInfobox()
	_debug(self, 1, 'entering ClassgeneratorParameter:addInfobox(), from ' .. tostring(self))
	if self:goodToGo() then
		local _CFG = self.class.myConfiguration
		local coreData = self:getPrivate('coreData')
		local ib_args = {
			bodyclass = 'infobox_name',
			aboveclass = 'objtitle titletext',
			headerclass = 'headertext',
			labelstyle = 'width: 30%;',
			datastyle = 'width: 70%;',
			title = self:getPrivate('uid'),
			subheader = nil,
			label1 = _CFG.parameter.name.label,
			data1 = coreData.name and coreData.name or '',
			header1 = nil,
		}
		self:addOutput(require('Module:Infobox').infobox(ib_args))
		return true
	end
	return false
end

function ClassgeneratorParameter:addPageBody()
	_debug(self, 1, 'entering ClassgeneratorParameter:addPageBody(), from ' .. tostring(self))
	_debug(self, 2, ' rendering errors and warnings and adding them to output')
	local frame = mw.getCurrentFrame()
	self:addOutput(self:renderErrors())
	self:addOutput(self:renderWarnings())
	if self:goodToGo() then
		self:addOutput('No out put yet')
		return true
	end
	return false
end

function ClassgeneratorParameter:myArgumentProcessing(coreData)
	_debug(self, 1, 'entering ClassgeneratorParameter:myArgumentProcessing(args) to process coreData, from ' .. tostring(self))
	-- function that performs some individual transformation args --&amp;gt; coreData
	local coreData = coreData
	-- if dataStore == 'cargo' set sf_cargo_table and sf_cargo_field
	if (FoundationClass.globalConfig.dataStore == 'cargo' or FoundationClass.globalConfig.dataStore == 'both') and FoundationClass.myYesno(coreData.cargo) and FoundationClass.myYesno(coreData.add_cargo_table_and_field) then
		coreData.cargo_table = 'global.cargoTable'
		coreData.cargo_field = coreData.name
	end
	coreData.add_cargo_table_and_field = nil
	if (FoundationClass.globalConfig.dataStore == 'smw' or FoundationClass.globalConfig.dataStore == 'both') and FoundationClass.myYesno(coreData.property) then
		coreData.property_name = mw.ustring.gsub(mw.ustring.gsub(coreData.property_name, ' ', '_'), mw.site.namespaces[mw.title.new('Nul', 'Property').namespace].name .. ':', '')
	else
		coreData.property_name = nil
		coreData.property_type = nil
	end
	if coreData.sf and mw.ustring.lower(coreData.sf) == 'holds template' then
		coreData.sf = 'yes'
		coreData.holds_template = 'true'
	end
	if coreData.show_on_select and type(coreData.show_on_select) == 'table' then
		local sos = {}
		for _, selector in pairs(coreData.show_on_select) do
			local value, field = mw.ustring.match(selector, '^([^=]+)%s*=%s*(.+)$')
			if value and field then
				if mw.ustring.match(value, ' ') then
					value = '[\'' .. value .. '\']'
				end
				if not sos[value] then
					sos[value] = {}
				end
				table.insert(sos[value], field)
			elseif coreData.input_type == 'checkbox' or selector == 'false' then
				table.insert(sos, mw.text.trim(selector))
			else
				self:addError('Error processing value for \'\'' .. self.class.myConfiguration.parameter.show_on_select.label .. '\'\': ' .. 
					ClassgeneratorParameter.myTableTools.printTable(coreData.show_on_select) .. ' on entry :' .. mw.text.encode(selector))
			end
		end
		coreData.show_on_select = sos
		--coreData.show_on_select = FoundationClass.myTableTools.printTable(sos)
		--coreData.show_on_select = FoundationClass.myTableTools.printTable(coreData.show_on_select)
	end
	-- kill two cosmetical switches
	coreData.cargo = nil
	coreData.property = nil
	return coreData	-- this is your new coreData.
end

function ClassgeneratorParameter:myPlausibilityTest(args)
	_debug(self, 1, 'entering ClassgeneratorParameter:myPlausibilityTest(args) to test arguments, from ' .. tostring(self))
	local _CFG = self.class.myConfiguration
	-- function that performs the individual plausibility tests
	-- note: before you access a field args.fieldname you should check for existance
	-- self:addError(text)
	if args.name and not mw.ustring.match(args.name, '^[a-zA-Z_][0-9a-zA-Z_-]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.name.label .. ':</i> "' .. mw.text.encode(tostring(args.name)) .. '"')
	end
	if args.cargo_size and not mw.ustring.match(args.cargo_size, '^[0-9]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.cargo_size.label .. ':</i> "' .. mw.text.encode(tostring(args.cargo_size)) .. '"')
	end
	if args.cols and not mw.ustring.match(args.cols, '^[0-9]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.cols.label .. ':</i> "' .. mw.text.encode(tostring(args.cols)) .. '"')
	end
	if args.max_values and not mw.ustring.match(args.max_values, '^[0-9]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.max_values.label .. ':</i> "' .. mw.text.encode(tostring(args.max_values)) .. '"')
	end
	if args.maxlength and not mw.ustring.match(args.maxlength, '^[0-9]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.maxlength.label .. ':</i> "' .. mw.text.encode(tostring(args.maxlength)) .. '"')
	end
	if args.rows and not mw.ustring.match(args.rows, '^[0-9]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.rows.label .. ':</i> "' .. mw.text.encode(tostring(args.rows)) .. '"')
	end
	if args.size and not mw.ustring.match(args.size, '^[0-9]*$') then
		self:addError('Invalid value detected for parameter <i>' .. _CFG.parameter.size.label .. ':</i> "' .. mw.text.encode(tostring(size.cols)) .. '"')
	end
	return false	-- return value will be ignored. but if you add any error, the object's initialization will fail with the error
end

function ClassgeneratorParameter:myStashAdjustments(stash)
	_debug(self, 1, 'entering ClassgeneratorParameter:myStashAdjustments(stash) to do some minor adjustments on data before storing, from ' .. tostring(self))
	-- function that alters the stash before storing the data
	local stash = stash
	return stash	-- this is your new stash. this will be stored. it has format (fieldname: value)
end

function ClassgeneratorParameter:render()
	_debug(self, 1, 'entering ClassgeneratorParameter:render() to render the code I pass to template class generator, from ' .. tostring(self))
	local str = ''
	if self:goodToGo() then
		local _CFG = self.class.myConfiguration
		local coreData = self:getCoreData()
		local indent = '\t'
		local sfAttributes = {}
		for _, val in pairs(_CFG.form.fieldOrderSFgeneral) do
			table.insert(sfAttributes, val)
		end
		for _, val in pairs(_CFG.form.fieldOrderSFtypeDependent) do
			table.insert(sfAttributes, val)
		end
		table.sort(sfAttributes)
		if FoundationClass.myYesno(coreData.sf, true) then
			str = str .. coreData.name
		end
		str = str .. _CFG.template.delimiterFormFieldName
		str = str .. indent .. coreData.name .. ' = {\n'
		for _, globalAttr in pairs(_CFG.form.fieldOrderGeneral) do
			if globalAttr ~= 'sf' then
				str = str .. _processAttribute(self, globalAttr, indent .. '\t')
			elseif coreData.sf ~= 'Information' then
				local sf = ''
				sf = sf .. _processAttribute(self, 'cargo_table', indent .. '\t\t')
				sf = sf .. _processAttribute(self, 'cargo_field', indent .. '\t\t')
				for _, sfAttr in pairs(sfAttributes) do
					sf = sf .. _processAttribute(self, sfAttr, indent .. '\t\t')
				end
				if mw.ustring.len(sf) > 0 then
					str = str .. indent .. '\t' .. 'sf = {\n' .. sf .. indent .. '\t' .. '},\n'
				end
			end
		end
		str = str .. indent .. '},\n' .. _CFG.template.delimiterResult
	else
		str = tostring(self:renderErrors())
	end
	return str
end

return ClassgeneratorParameter