sgsML tutorial: Part 2

Store and Restore


Being as simple as possible, Simple Groupware automatically stores/reads data to/from the database. But sometimes you want to store inputs made by the user differently from the original value. E.g. the user types a date with a German formatting, but in the database it has to be a timestamp. Or you want to store URLs without a leading http:// in the database.
Similar to validators and filters, functions used for storing the input get the value from the form as the first parameter and return the modified value back to the program. You don't have to care how the value is transferred from the form to your function. This makes things much easier, e.g. file uploads require big error handling routines that would boost up your code unnecessarily. When modifying data before writing into the database, it is clear that there must be a re-transformation when being read from the database. This re-transformation is called restore. That's why there are restore functions, that are identical to filter functions regarding structure and functionality.
Thus, writing store and restore functions is as easy as validators or filters. To reduce your functions in size you can also define multiple store or restore functions.

Syntax: To collect these functions in a central place, all store and restore functions are kept in core/functions_user.php and have a "modify_" prefix (because they modify data), e.g. "modify_datetime_to_int" converts a date given as string to a timestamp using integer representation. To define a store or restore function in a sgsML file, write <store function="datetime_to_int"/> and <restore function="dateformat||d.m.Y"/> between the <field>-tags to work with a date-field using the functions modify_datetime_to_int and modify_dateformat.
As told before, Simple Groupware already knows a type called "date". Therefore you only need restore/store functions when defining your date-field with type "int". Using type "date" this is done automatically for you.

Note: The store functions are called after those used for validating the input.

Note: The restore functions are called before those used for filtering values from the database.


Data sources


Often it is necessary to build fields that already contain a set of data from which the user can choose the right values. E.g. this is a typical behavior for select-boxes. When writing an address book you might like to implement a field containing the gender of a person. The preferred values are male and female. The type "select" defines a select-box in Simple Groupware. To define the preferred values you use the <data> construct in sgsML, e.g. <data values="male|female"/> Using more than one value, separate each with a "|". You can also define more than one <data> construct inside the <field>-tag.
The method shown before is used for including static data. But in reality, these values are often dynamic and need to be fetched from a special data source. To get these values you can define a function that searches the right values for you and returns them as an array. You don't have to care about transferring these values to forms or to the relevant components, this is done automatically for you. Simple Groupware automatically calls your function when it needs the data. Instead of the values-parameter you use the function parameter to tell it what function to call. E.g. you want to show a list of companies in your address book or predefined values:

<data function="dbselect|simple_companies|companyname,companyname||companyname asc|10"/>
<data values="male|female"/>

To get these values you call the function dbselect which selects data from the database. To reduce this list we only want 10 items (parameter 5), if there are more, a search box is used to select relevant values. All companies have to be listed sorted by the name of the company (=companyname) alphabetically (asc=ascending) (parameter 4). As the table "simple_companies" (parameter 1) can have more than one field we tell the function to use the field "companyname" (parameter 2). Parameter 3 is not used in this case. With parameter 3 you can reduce the list of companies with an sql-where clause, e.g. "companyname like 'a%'" to get all companies starting with "a".

Syntax: To collect these functions in a central place, all data functions are kept in core/functions_user.php and have a "select_" prefix (because they select data), e.g. "select_dbselect" can be used to get data from the database.
To define a data construct in a sgsML file, write <data function="getmydata"/> between the <field>-tags to call the function "select_getmydata". Here dbselect is illustrated because it is the most common function to get data from the database.

Note: To reduce the size of your functions you can define multiple <data function=> constructs between the <field>-tags.


Tables


When looking at tables we see rows, columns. Our rows are assets, every column is a field (similar to database design). <table></table> is the main tag used in sgsML and includes all the others (e.g. fields, views, etc.). With the table tag you define the name of the module, e.g.

<table modulename="Contacts"></table>

With a table you also define how the fields should be treated:
What is the column the list or assets should be sorted by?
What order should be used?
How many items should be displayed on one page?
Should the results be groups by a special column?
e.g.:

<table modulename="Contacts" orderby="lastname" order="asc" groupby="categories" limit="20"></table>

Of course, these are only the default values you suggest to the user. He can later choose on his own if he needs different settings.


Fields


All fields are defined inside the <table>-tag. A field can be seen as a column in a database, e.g. Last name, Street, Country in an address book. Every field has a type, in sgsML the type is called "simple_type". A type can be a normal string (simple_type is "text") or a collection of lines (simple_type is "textarea"). There are even exotic types like "htmlarea" which represents lines formatted with HTML, or different types for using passwords, dates, times, datetimes, files, wikis, etc.
Every field has a name to be identified by the program. This is similar to column definitions in databases. Besides the internal name used by the program every column can contain a displayname which is the name displayed to the user.
E.g. the name of a field can be "lastname", the displayname is "Last name". To separate these two has a lot of advantages:
The name used by the program is limited to numbers and characters, whereas displayname can contain all kinds of special characters or it can be translated into many languages without changing the internal name used by the program. This creates consistency when using internationalization with Simple Groupware.
Fields can be declared as unique, meaning that there can be no other asset having the same value within this field. To decide between required and optional fields, every field can be defined as "required", but is "optional" by default.
As written before, fields can be validated using validators, their data can be defined by some data sources and they can be modified before / after storing them in the database.

Some examples:

contacts.xml:

<field name="lastname" displayname="Last name" simple_type="text" required="true" />

<field name="zipcode" displayname="Zipcode" db_size="6" simple_type="text">
<validate function="numeric"/>
</field>

<field name="description" displayname="Description" simple_type="textarea" simple_size="4" />


Views


The next element inside the sgsML language is a "view". At first "views" are groups of fields. E.g. you show "first name" and "last name" of a person in the view "display" and the field "address" and "description" in the view "details". But views can do a lot more: views are also used to edit and create assets. Since defining forms (for editing and creating assets) is not always easy and oftentimes requires Javascript usage, Simple Groupware creates all the forms automatically for you. So you only define a field as an html-editor (simple_type "htmlarea") and you're done.
To present several fields, sgsML differs between the orientation in which a group of fields is presented: The horizontal view (sgsML "display") is a list where every field has one column (this is how Windows Explorer lists files). The second one is called "details" (vertical orientation) and presents every field as one line, meaning the first column is the name of the field and the second column contains the value of it. Both orientations have advantages and disadvantages, so you can decide on your own which one you use for what case: define template="display" or template="details" in the view-tag and you're done. Note: When naming a view "foobar", the template "asset_foobar.tpl" will be used. Using the attribute template="display" in the view tag overrides this automatic mapping.

When looking with web applications you often see a list of assets (horizontal orientation) and a "Details" button at the right side to switch to the vertical one for a selected row. If you need this behavior depends on you own, simply add a showinsingleview="true" as attribute to the details-view and you're done (sgsML defines the rows of a table as single view, the headline above is the "normal" view, thus "singlebuttons" are buttons displayed at the right side of every asset).
Similar to tables, views can have their own "order", "orderby", "limit" statements which override those defined by the table.
Other tags are "image_width" and "image_height" (both or only one of them) which enable you to automatically resize images (simple_type "files", images are detected automatically when extension is gif/jpeg/png). This is very useful when bandwidth is low.
Another way views can be used is to reduce the number of assets by a "where" clause, e.g. "lastname like 'a%'" to get all persons starting with "a" (this functionality is similar to views in databases using SQL-Views).
To group fields to special views, you define constructs like "notin" (exclusion method) or "onlyin" (inclusion method) inside the <field>-tags.

Some examples:

contacts.xml:

<view name="display" displayname="{t}Display{/t}" groupby="category" />
<view name="details" displayname="{t}Details{/t}" showinsingleview="true" />

users.xml:

<view name="display" displayname="{t}Active{/t}" where="activated=1" />
<view name="details" displayname="{t}Details{/t}" image_width="200" image_height="100" showinsingleview="true" />


Tabs


The last construct used by sgsML are tabs. Tabs are similar to views: They group fields and enable you to quickly switch from one group to another without overloading the screen. A view is static: This means when you click on a view, the program takes the required fields from the databases and presents the results to you. In contrast, tabs are subordinated to views. A view can contain several tabs, every tab is visible in every view (sounds a bit difficult at the first shot, but gets clearer when writing the first application).
Tabs are dynamic: E.g. you click on one view containing 5 tabs, all fields for the 5 tabs are loaded from the database and put into the (HTML-)output. To avoid overloading the screen, tabs use Javascript to make sure that only one tab is visible at once. When clicking another tab, the old one is hidden and the other is made visible. To assign fields to tabs, simply use <field ... simple_tab="mytab">.

Here is an example:

<tab name="general" displayname="{t}General{/t}" />
<tab name="details" displayname="{t}Details{/t}" />

<field name="description" displayname="{t}Description{/t}" simple_type="textarea" simple_size="4" simple_tab="details" />


Deploy your applications with sgsML


Finally after writing your first sgsML application you will surely want to deploy it to your Simple Groupware installation. Therefore every module is a web application and stored in one .xml file. Simple Groupware distinguishes between two types of modules: Normal modules and system modules.
System modules are very special modules providing access to other data sources (e.g. the filesystem, other databases, IMAP, iCalendar, etc.) or providing system functionality (folders, statistics, events, search functionalities, session handling, etc.). A module is a system module if Simple Groupware cannot run without it. (There are even some modules like users and groups where this border cannot be clearly pulled, these modules remain system modules since you can't work without it). System modules are kept under "modules/schema_sys". If a module doesn't represent a schema from a table with data in the database than it carries a "nosql_" prefix in the filename (e.g. the filesystem has a schema, but it is not a schema from a table with data in the database). A module that is marked as "nosql_" needs to get its data from another data source. Therefore it uses its own functions for getting and setting data. These functions are very simple and are automatically called by the sql-handler of Simple Groupware. These functions are stored in modules/lib/* and are all following the Simple Groupware API. When learning sgsML you should try to understand system modules at last.
The Simple Groupware folder structure has two branches: src/ and bin/. If you install with language English, German, etc. all files from src/* are translated and written to bin/* during the setup process. Therefore if you installed with English or German, place your .xml file in bin/modules/schema/. If you installed with Developer as language, place your .xml file in src/modules/schema/.
Note: To be able to select a module in the list when creating a new folder, you need to add the module to "modules/schema/modules.txt". Every line in this file contains the module's name and the string that is displayed in the web-interface.
Note: Every .xml file defines a structure of a table in the database. The name of the table is automatically set by the filename and "simple_" as prefix. E.g. the table name for schema/tasks.xml is set to simple_tasks. If the module is a system module, the prefix will be "simple_sys_". E.g. the table name for schema_sys/users.xml will be simple_sys_users. The "modulename" attribute is only the name displayed above the tree on every page.


Translations


With Simple Groupware you can translate everything: It doesn't matter if the strings that should translated are inside XML, PHP, Html, sgsML, etc. Every string that needs to be translated is covered with {t} and {/t}. E.g. to make "username" translatable, use it as "{t}username{/t}". That's the same using sgsML. Examples for translations inside sgsML are display-names for fields, date formats as validation / store parameters, etc. The translation process is done during the setup. So you select a language and all files from the src/ directory are translated into the bin/ directory. When installing with Developer as language, the translation isn't done. All files reside in src/ and when viewing the output, {t} and {/t} are automatically removed. So using Simple Groupware as "Developer" you always get untranslated English values in the output.
Note: If you don't need translations, you won't need to add "{t}" and "{/t}".



sgsML: Part 1 - sgsML: Part 2 - sgsML: Part 3 - sgsML: Part 4