midcom.helper.datamanager2 schema definition
- General structure
- The Schema Database
- The Individual Schema Definition
- description
- l10n_db
- operations
- customdata
- validation
- filters
- The Field Listing
- title, description and helptext
- Visibility Control
- Access Control
- Validation
- Storage location definition
- Automatic indexing
- Type and widget configuration
- customdata
- static_prepend and static_append
- Localization
This is a MidCOM 2.5 Development Technical Note covering the Datamanger Rewrite. Everything written here is subject to change without notice.
General structure
Compared to the first datamanager version, schemas for the next generation version are more verbose, and have a tighter structure, which should avoid most of the namespacing problems.
Schemas are still represented by simple PHP array structures, and are evaluated during runtime. There is no need for the main enclosing array tag, this one is added automatically during the evaluation. Other than the field list, the main schema level holds only metadata related to the complete schema.
As before, you can have more than one schema in a single file, which represents a schema database. Schemas are indexed by names, which are used as arguments on the objects to identify the schema to load. Individual schemas are identified by their key.
The Schema Database
Taking this together, the general schema database looks like this:
'schema1' => Array ( /* Definition of schema 1 */ ),
'schema2' => Array ( /* Definition of schema 2 */ ),
//...
'scheman' => Array ( /* Definition of schema n */ ),
In case objects are edited without the explicit definition of a schema, the first schema within a database is used by default.
As mentioned above, this is the complete content of the file, there must be no surrounding Array(...) tag.
The Individual Schema Definition
Each schema definition, follows the same general layout, holding a number of meta fields and, of course, the field listing, which is discussed in detail further below.
'schema1' => Array
(
'description' => 'short, descriptive schema title',
'l10n_db' => 'l10n db identifier',
'operations' => Array('save' => '', 'cancel' => ''),
'fields' => Array
(
'field1' => Array ( /* field definition */ ),
'field2' => Array ( /* field definition */ ),
// ...
'fieldn' => Array ( /* field definition */ ),
),
'customdata' => Array
(
// ...
),
'validation' => Array
(
// ...
),
'filters' => Array
(
// ...
),
),
description
This string describes the schema, and is normally used when displaying schema listings from a component. This field is required.
l10n_db
This string holds the name of a L10N string table. It is used as a first instance for the translation of the strings in the schema. At this time (since no other l10n dbs are supported), you have to set a component name here, like net.nehmer.static.
This field is optional, the default uses the l10n db of the component which handles the currently active context.
operations
This setting defines which operations are valid on the form, it must be a list consisting of a combination of the operations save, cancel, next and previous. You need to pass an array at this point, whose keys are made up of the operations you desire, and whose values represent the labels of the buttons you want to have in the form. If you want the default label "form submit: {$operation}", which should be enough in most cases, you can just pass an empty string as label, like in the default Array('save' => '', 'cancel' => '').
customdata
This array allows you to store component specifc data in the schema. There are no rules about the contents of this array from the side of the Datamanager infrastructure, it is read verbatim.
validation
This block allows you to register form-wide validation callbacks, executed by quickform. To avoid extensive loading of function code without neccessity, each validation rule must be specified as an array taking this form:
Array
(
'callback' => 'callback_function_name',
'autoload_snippet' => 'snippet_name',
'autoload_file' => 'filename',
)
Only the callback parameter is mandatory, the two autoload parameters are purely optional, but you should employ this feature to avoid loading the validation code unless it is needed. MidCOM will load the file/snippet in advance to registering the rule with QuickForm.
You may add more than one of these blocks.
See the method HTML_QuickForm::addFormRule() for details including a working callback example.
filters
This block allows you to register data filtering callbacks, executed by quickform. To avoid extensive loading of function code without neccessity, each validation rule must be specified as an array taking this form:
Array
(
'callback' => 'callback_class_name',
'fields' => 'field_name',
'constructor_argument' => null,
'autoload_snippet' => 'snippet_name',
'autoload_file' => 'filename',
)
Only the callback parameter is mandatory, the others are purely optional. The constructor_argument is made available as $this->_config within the callback class automatically.
The fields setting allows three different arguments. First, you can omit it indicating that the callback should be applied to the entire form. If you set it, you can either directly set it to a single schema field name, as in the example above or you can set it to an array of field names. If you specify an array, the callback is applied to all the fields specified there.
You should employ the autoload feature to avoid loading the validation code unless it is needed. MidCOM will load the file/snippet in advance to registering the rule with QuickForm.
You may add more than one of these blocks.
In contrast to the validation rule system, you are forced to specify a class name at this point. Reason for this is that the MidCOM Form Filtering Interface gives you access to the full Formmanager instance. Since the simple callback cannot do this, we need a small class at this point.
For ease of use, you can create a derived class using midcom_helper_datamanager2_baseclasses_filter, which is not loaded by default, you need to require_once it manually. The class is documented in the Midgard API Docs, it basically provides these members:
$_formmanageris a reference to the form manager instance which we have been instantinated with.$_configis the configuration value out of the schema array.$_fieldnameis the name of the field we are currently processing. This is the actual name of a schema field not a quickform element name.
TODO: Link this to the actual doc page after the next resync.
The class must of course have the actual callback, which is called "exectue". This name cannot be changed at this point.
A simple class file could thus look like this:
<?php
require_once(MIDCOM_ROOT . 'lib/midcom/helper/datamanager2/baseclasses/filter.php');
class test extends midcom_helper_datamanager2_baseclasses_filter
{
function execute ($input)
{
// Do Something(tm)
return $input;
}
}
?>
Be aware, that this filtering system is beefed up compared to the regular standard filtering system within QuickForm. It is mainly targeted to have complex filter operations, which are not neccessarily doable with validation. The wrapping code provides you full access to the schema so that you can do just about anything in here.
Authors Note: you might wonder why I deliberatly omitted access to the standard QuickForm Filtering, wich would allow you to quickly press an form element through trim() (or whatever). The main reason for this is that these operations are already conducted by the DM2 widgets or the controlling components where neccessary, as these have full control over the QuickForm instance where they need. Usually you will have no need for such "trivial" operations therefore.
See the method HTML_QuickForm::applyFilter() for (little) details about the actual QuickForm interface code.
The Field Listing
This is the most complex part of the schema definition. It is split into four parts: Field Metadata, Storage, Type and Widget configuration, a full field with all its verbosity looks like this:
'title' => Array
(
'title' => 'Article title',
'description' => 'Field description, shown under the title',
'helptext' => 'Field helptext, possibly a linked page?',
'hidden' => false,
'readonly' => false,
'aisonly' => false,
'read_privilege' => Array
(
'privilege' => 'de.linkm.taviewer:read_title',
'group' => 'vgroup:de.linkm.taviewer-editors',
),
'write_privilege' => Array
(
'privilege' => 'de.linkm.taviewer:write_title',
'group' => 'vgroup:de.linkm.taviewer-editors',
),
'required' => true,
'validation' => Array
(
Array
(
'message' => 'Error message',
'type' => 'rule type',
'format' => '',
),
'other rule type',
),
'storage' => 'title',
// See below for special forms of this for metadata/parameter usage
'index_method' => 'auto',
'index_merge_with_content' => true,
'type' => 'text',
'type_config' => Array
(
'maxlength' => 60,
),
'widget' => 'text',
'widget_config' => Array
(
'mode' => 'oneline',
),
'customdata' => Array
(
// ...
),
'static_prepend' => $html_text,
'static_append' => $html_text,
),
As you can see, the complexity of the schema fields has increased quite a bit, compared to the original schemas. This was neccessary to take the large amount of features into account, which has been implemented in the last years into the original datamanager. Especially the namespacing of type- and widget-configuration options is important here, adding more flexibility to the system while making the coding of the types and widgets easier.
title, description and helptext
These three fields add descriptive information to the schema field in question. Only title is required, description and helptext are optional. description is printed directly below the field's title when being rendered using Datamanager Widgets, while the helptext is made available through some sort of PopUp Window.
All three fields are assumed to be HTML, so you need to take care of escaping special characters where neccessary. Note, that with UTF-8 being the default character set on current MidCOM driven sites, you usually don't need to escape special characters like German Umlauts ('รถ' etc.).
Visibility Control
This part of the metadata consists of four flags:
The flags required, hidden , aisonly and readonly control the visibility and general accessibility of a field. All of these four options are optional, and all default to false:
required specifies, wether an input in this field is required before the form can be saved. Author's note: This might get deprecated during the implementation, in favor of the validation system (see below).
hidden and aisonly control the general visibility of the field, hiding it generally, or only on-site, respecitivly.
Finally, readonly disables the edit operation on a field entirely, showing the "view mode" even when rendering a form.
Access Control
Two operations can be checked against the MidCOM ACL system: Read and Write access to each field. This allows for more granularity than the simple Visibility flags outlined above. The Information from these two sources are merged together, with the ACL declarations being able to override the visibility flags. In other words, if any ACL is specified, it will always override the visibility flags in both directions; a hidden field where a user has read access will be shown, a readonly field where a user has write access will be made editable etc.
The checks are always done against the current user, and they are not made until the actual field is evaluated in the context of its storage object only when needed, for example immediately before deciding wether to render an edit or view widget.
The example above shows the two possible options valid for privilege checks: The key privilege checks wether the user has the given privilege assigned to him, while group requires the according group membership. You may specifiy either one or both of these constraints. If you specifiy both, this requires both checks to succeed. (An AND logical operation, not an OR one.)
Both read and write access control specifications are optional.
Note, that missing read privileges will hide a field entirely, even in the Datamanager. You need to take this into account in your style code.
Validation
Validation support maps directly to the Validation rule system of HTML_QuickForm. See the HTML_QuickForm Manual or this Example on thelinuxconsultancy.co.uk for examples on what rules exactly are available.
Each rule can consist of up to three options, all written as an accociative array: message will be displayed if the validation fails, type sets the type of the validation rule. The format field can be omitted and defaults to '', its usage depends on the validation rule.
message defaults to the same value as type. The DM2 localization libraries should bring a mostly complete set of localized standard error messages for the known validation rules, albeit some of them (like the regex string) won't be of much help to the user.
format is optional, its usage depending on the actual validator you use. Check the docs for further details.
If you just want to specify a validator like numeric with its default error message, you can omit the array declaration instead just specifying the rule type string directly as a member of the validation array. In the case where you only have a single validator which you would like to use in its default mode, you can just specify the type string directly, without the enclosing array.
Special care has to be taken with the compare validator, which cannot be specified in a shortcut variant. It allows you to force two fields to have the same content, for example to have a user enter an E-Mail address twice, to avoid typos. For that purpose, you need to add the compare validation rule to the second field of the pair (the error will display with the first field nevertheless), as you can only compare to existing fields. For example:
'validation' => Array
(
'email',
Array
(
'type' => 'compare',
'compare_with' => 'email',
'message' => 'E-Mails do not match.'
),
),
This will compare the current field (possibly called email_confirm with the field specified in the compare_with option, which is mandatory for this validation type.
Storage location definition
This superseeds the original 'location' parameter and specifies the place in the content object, where the contents of the type should be stored. The syntax above shows the shortcut for assigning data to a regular object field. There are further possibilities of course.
Enhanced storage location definitions always base on an array holding the storage settings. Standard configuration key/value syntax applies. The key 'location' holds the actual storage mode, its content defines which storage mode is used:
- You can store data directly into a member, specify the name of the field
as location in that case. You can shortcut this by assigning the location
directly to the
'storage'field. - A parameter controlled by DM2 will be used if you set
'parameter'as location. This too can be used as shortcut by assigning it directly to the'storage'field. Unless specified otherwise, the parameter is stored in the domainmidcom.helper.datamanager2. But see below. - An arbitary parameter can be used with the
'configuration'location. It is primarily aimed at just that, letting you specify both domain and name of the parameter to be used. - It is possible to access fields of the Metadata subobject introduced with
Midgard 1.8 by using the
'metadata'location. In that case you have to set the metadata field as well, see below. - Finally, setting the location to null disables all standardized storage
handling. This is required for certain types which store the data
independantly of the main object, like the
blobsormnrelationtypes.
In most cases you will either have field or parameter as a storage target. To make this part of the schema easier to write, you may shortcut these cases by assigning the target location directly to the storage key.
For parameter targets, you may additionally specify the domain in which the parameters are stored. This defaults to midcom.helper.datamanager2 and should be sufficient in most cases, unless you want your schemas to be compatible with the first generation datamanger, which uses the midcom.helper.datamanger domain. This should only be done selectivly, as the types do not need to be backwards compatible.
For configuration targets, the two additional options domain and name must be specified so that the Datamanager knows which domain and name the parameter stored should have.
For metadata, you need to specify the additional key field holding the name of the metadata member you want to query. Be aware that not all metadata entries are writeable. This can only be safely used with Midgard 1.8. There is no sanity check here, you have to take care of yourself in this respect.
Some types, like the blobs or the parameters base type, have their own storage IO code as they don't have an explicit "field" they are bound to. The storage location for these fields is actually ignored in the type code. You are strongly encouraged to make this explicit by setting the storage location of such a field to null (which is not the default).
So, to wrap this up, we have five common variants of the storage directive:
'storage' => 'title', // or any other valid storage object member
'storage' => 'parameter',
'storage' => Array
(
'location' => 'parameter',
'domain' => 'midcom.helper.datamanager',
),
'storage' => Array
(
'location' => 'configuration',
'domain' => 'de.linkm.taviewer',
'name' => 'autoindex',
),
'storage' => Array
(
'location' => 'metadata',
'field' => 'published',
),
'storage' => null,
attachment storage location has gone. The answer is easy, it has been deprecated. The main reason for the attachment location to be there was the 255 character length limit of parameters. With Midgard 1.7 and upwards, this is no longer the case, so there is no need to store variables within attachments. Be aware, that this is one of the points where the two datamanger implementations are not compatible!
Automatic indexing
The two options index_method and index_merge_with_content control the way how the midcom_services_indexer_document_datamanager2 indexer document subclass treats the field when automatically indexing an instance of the Datamanager 2.
The documentation of that class outlines the corresponding options in detail, for a quick reference, index_method may be one of auto (the default), title, abstract, content, author, keyword, unindexed, unstored, text, date or noindex. The booleam index_merge_with_content defaults to true.
Type and widget configuration
From the schema perspective, this is a rather trivial point: You specify the name of the type/widget and the options to use. The type/widget's name is mandatory, while its options are optional.
customdata
This array allows you to store component specifc data in the schema. There are no rules about the contents of this array from the side of the Datamanager infrastructure, it is read verbatim.
static_prepend and static_append
These two fields allow you to insert custom HTML code into the form using the QuickForm static element. This is quite useful to add custom styling to your (custom) schemas.
Both settings are optional.
Localization
The automatic translation of schema strings as it was in the original datamanager is now done on-demand, for example when a widget renders itself, it will translate only those strings, which are neccessary. The schema class has a translate_schema_string function to aid in this process.
