forked from extern/egroupware
967 lines
41 KiB
HTML
967 lines
41 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html>
|
|
<head>
|
|
<title>eGroupWare: eTemplate-reference</title>
|
|
<!-- $Id$ -->
|
|
<style type="text/css">
|
|
<!--
|
|
pre { font-family: monospace; background-color: #e0e0e0; padding: 2mm; border-width: thin; border-style: solid; border-color: black; white-space: pre; }
|
|
span { color: darkblue; font-family: sans-serife; }
|
|
li { margin-top: 5px; }
|
|
body { background-color: white; color: black; }
|
|
table { border: 1px solid black; border-collapse: collapse; }
|
|
-->
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>eTemplate-reference - Templates and Dialog-Editor for eGroupWare</h1>
|
|
<h3>by Ralf Becker <a href="#" onClick="document.location='mai'+'lto:RalfBecker'+unescape('%40')+'outdoor-training'+unescape('%2E')+'de'; return false;">RalfBecker AT outdoor-training DOT de</a></h3>
|
|
<p>A reference documentation about the new eTemplates and the <a href="#reference">syntax and parameters</a>
|
|
of the several widgets.</p>
|
|
<hr>
|
|
<h1>Introduction - The concept of the eTemplates</h1>
|
|
<p>As I already covered this in the <a href="etemplate.html">Tutorial</a>, I like would suggest to have a look
|
|
there if your are not familiar with the overal concept.</p>
|
|
<p>There are two possibilities now to create an eTemplate:</p>
|
|
<ol>
|
|
<li>Use the eTemplate-Editor (as descript in the <a href="etemplate.html">Tutorial</a>) to interactivly
|
|
design your template.
|
|
<li>Write a xml-file in a Syntax similar to XUL (the mozilla UI-interface definition language) and import
|
|
it into the database with the eTemplate-Editor
|
|
</ol>
|
|
<hr>
|
|
<h1>The xml-interface to the eTemplates</h1>
|
|
<p>The eTemplates-Editor can import and export now eTemplates as xml-files. Here is short example showing
|
|
an eTemplate from the example app in the <a href="etemplate.html">Tutorial</a>
|
|
(here are screenshots of the template in the <a href="editor.gif">editor</a> and the
|
|
<a href="show.gif">show-function</a>):</p>
|
|
<pre>
|
|
<?xml version="1.0"?>
|
|
<span><!-- $Id$ --></span>
|
|
<overlay>
|
|
<template id="et_media.edit" template="" lang="" group="" version="">
|
|
<grid width="100%">
|
|
<columns>
|
|
<column/>
|
|
<column/>
|
|
<column/>
|
|
<column/>
|
|
</columns>
|
|
<rows>
|
|
<row>
|
|
<description options="ib" span="all" value="eTemplates MediaDB" no_lang="1" id="msg"/>
|
|
</row>
|
|
<row>
|
|
<hrule span="all"/>
|
|
</row>
|
|
<row>
|
|
<description span="all"/>
|
|
</row>
|
|
<row>
|
|
<description value="Name"/>
|
|
<textbox size="100" maxlength="100" span="all" id="name" statustext="here goes the name of the publication / record"/>
|
|
</row>
|
|
<row>
|
|
<description value="Author"/>
|
|
<textbox size="100" maxlength="100" span="all" id="author" statustext="please use Name, First Name"/>
|
|
</row>
|
|
<row>
|
|
<description value="Type"/>
|
|
<menulist span="all" statustext="select the type fitting most">
|
|
<menupopup id="type"/>
|
|
</menulist>
|
|
</row>
|
|
<row>
|
|
<description value="Description"/>
|
|
<textbox ="" cols="3" rows="100" span="all" id="descr" statustext="we have a fulltext search using that description"/>
|
|
</row>
|
|
<row>
|
|
<description span="all"/>
|
|
</row>
|
|
<row>
|
|
<button label="Read" id="read" statustext="reads or searches for entries matching the criteria above"/>
|
|
<button label="Save" id="save" statustext="saves the change to the db"/>
|
|
<button label="Cancel" id="cancel" statustext="clears the form, without changing anything"/>
|
|
<button label="Delete" id="delete" statustext="deletes an entry"/>
|
|
</row>
|
|
</rows>
|
|
</grid>
|
|
</template>
|
|
</overlay>
|
|
</pre>
|
|
<p>The tags / widget-names and attributes / parameters used are as close as possible to XUL. For more
|
|
information about XUL refer to <a href="http://www.xulplanet.com">www.xulplanet.com</a> or the Mozilla docs
|
|
<a href="http://www.mozilla.org/xpfe/xulref/">www.mozilla.org/xpfe/xulref/</a>.</p>
|
|
<p>Please keep in mind that the xml-files used to store the eTemplates are only similar to XUL and
|
|
implement only a subset of XUL. Here are the main differences:</p>
|
|
<ul>
|
|
<li>only certain widgets and widget attributes are implemented
|
|
<li>xul-files can contain the actual content or refer to it via a datasources (RDF's) and use a
|
|
different template syntax to fill in content from a variable: <label value="?label"/><br />
|
|
eTemplates get there content from an array passed to the exec or show-function of the template-object
|
|
and reference to the content by the id / name-field of each widget.
|
|
<li>xul-files can contain an unlimited number of nested elements, the xml-root-node of an eTemplates has to
|
|
be an overlay, containing multiple (non-xul) <template>'s. That templates can contain now (HEAD)
|
|
a tree of other widgets, but not other templates direct. You can use <template id="app.template_name" />
|
|
to load an other template by its name.</i>
|
|
</ul>
|
|
<p>Like XUL the eTemplate-xml-files are quite strict with the xml-syntax:</p>
|
|
<ul>
|
|
<li>All tags and attributes must be written in lowercase
|
|
<li>All strings must be double quoted, like id="string"
|
|
<li>Every XUL widget must use close tags (either <tag></tag> or <tag/>) to be well-formed
|
|
<li>All attributes must have a value, no <tag attr> it has to be <tag attr="1">
|
|
</ul>
|
|
<hr>
|
|
<a name="reference"></a>
|
|
<h1>Syntax and Parameter reference</h1>
|
|
<h2>Standard parametes / attributes for <a href="#widgets">all widgets</a></h2>
|
|
<table border=1>
|
|
<tr>
|
|
<th>Name in Editor</th>
|
|
<th>xml attr</th>
|
|
<th>xul</th>
|
|
<th>internal name</th>
|
|
<th>description of the attribut<th>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Type</b></td>
|
|
<td>type<br /><i>(only for<br />sub-types)</td>
|
|
<td>no</td>
|
|
<td>type</td>
|
|
<td>
|
|
The type of the widget is stored in the tag itself, some widgets have sub-types (unknow to XUL).
|
|
In that case the subtype is stored in the type attribut.<br />
|
|
The Type / tag has to be either the name of a standard eTemplate-widget or of
|
|
an already existing <a href="#extensions">extension</a>.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Name</b></td>
|
|
<td>id</td>
|
|
<td>yes</td>
|
|
<td>name</td>
|
|
<td>
|
|
A string to locate the content for the widget in the content array (index) to show the dialog
|
|
and for the returned content.
|
|
Can be left blank or be obmitted as xml-attribut if the widget needs no content.<p>
|
|
The name can contain the following variables, which gets expanded before they are used as
|
|
array index (for an example see the <a href="etemplate.html">Tutorial</a>):<br />
|
|
<table>
|
|
<tr>
|
|
<td><b>$c</b></td>
|
|
<td>the column-number (starting with 0, if you have a header, data-cells start at 1)</td>
|
|
</tr><tr>
|
|
<td><b>$col</b></td>
|
|
<td>the column-letter: 'A', 'B', 'C', ...</td>
|
|
</tr><tr>
|
|
<td><b>$row</b></td>
|
|
<td>the row-number (starting with 0, if you have a header, data-cells start at 1)</td>
|
|
</tr><tr>
|
|
<td><b>$cont</b></td>
|
|
<td>the content-array the (sub-)template, on auto-repeated row's this could eg. be
|
|
used to generate button-names with id-values in it: "del[$cont[id]]" expands to "del[123]",
|
|
if $cont = array('id' => 123)</td>
|
|
</tr><tr>
|
|
<td><b>$row_cont</b></td>
|
|
<td>the sub-array indexed by $row of the content-array, on auto-repeated row's this could eg. be
|
|
used to generate button-names with id-values in it: "del[$row_cont[id]]" expands to "del[123]",
|
|
if $cont = array('1' => array('id' => 123),'2' => array('id' => 456)) and $row = 1</td>
|
|
</tr><tr>
|
|
<td><b>$c_<br />$col_<br />$row_</b></td>
|
|
<td>are the respective values of the previous template-inclusion,
|
|
eg. the column-headers in the eTemplate-editor are templates itself,
|
|
to show the column-name in the header you can not use $col as it will
|
|
be constant as it is always the same col in the header-template,
|
|
what you want is the value of the previous template-inclusion.</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Label</b></td>
|
|
<td>label</td>
|
|
<td>no</td>
|
|
<td>label</td>
|
|
<td>
|
|
The label is displayed by default in front (for radiobuttons behind) each widget (if not empty).
|
|
If you want to specify a different position, use a '%s' in the label, which gets replaced by
|
|
the widget itself. Eg. '%s Name' to have the label Name behind a checkbox.
|
|
The label can contain variables, as descript for name.
|
|
If the label starts with a '@' it is replaced by the value of the content-array at this
|
|
index (with the '@'-removed and after expanding the variables).<br />
|
|
<b>Note</b>: The label gets always translated, if its longer than 1 char! If this is not
|
|
disired, use a label widget, place the not-to-translate label in the content-array
|
|
<u>and</u> check NoTranslation or set the xml attribute no_lang.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Help</b></td>
|
|
<td>statustext</td>
|
|
<td>yes</td>
|
|
<td>help</td>
|
|
<td>
|
|
This text / help-message is displayed in the status-bar of the browser when the widget gets focus
|
|
(or as tooltip for buttons or general in gtk).
|
|
If the user has JavaScript switched off, the help-texts get NOT submitted, as this is detected.
|
|
If the helptext starts with a '@' it is replaced by the value of the content-array at this
|
|
index (with the '@'-removed and after expanding the variables).
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Options</b></td>
|
|
<td>?</td>
|
|
<td>?</td>
|
|
<td>size</td>
|
|
<td>
|
|
This attribute controls certain aspects of the different widgets. It's meaning and xml / xul-values
|
|
are document with the widgets.
|
|
If the options-string starts with a '@' it is replaced by the value of the content-array at this
|
|
index (with the '@'-removed and after expanding the variables).
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>NoTranslation</b></td>
|
|
<td>no_lang</td>
|
|
<td>no</td>
|
|
<td>no_lang</td>
|
|
<td>
|
|
If checked the content of the widget and the label gets NOT translated.<br />
|
|
The helptext of a widget is always translated.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>needed</b></td>
|
|
<td>needed</td>
|
|
<td>no</td>
|
|
<td>needed</td>
|
|
<td>
|
|
If checked (xml-attr: needed="1") the etemplates will reprompt the user if he left
|
|
the widget / field empty.<br />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Readonly</b></td>
|
|
<td>readonly</td>
|
|
<td>yes</td>
|
|
<td>readonly</td>
|
|
<td>
|
|
If checked (xml-attr: readonly="true") the widget will NOT be editable.
|
|
If it is not supported by the browser, the etemplate-class makes shure that no
|
|
changes / content is transmitted back to the app.
|
|
Only applicable to widgets with input capabilities. Readonly Buttons get removed
|
|
from the dialog, like they where disabled.
|
|
The readonly attribute could be set on runtime via a readonly-array sublied to
|
|
the exec or show function of the class, the value on index=name/id has to be true
|
|
to make a widget readonly.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Disabled</b></td>
|
|
<td>disabled</td>
|
|
<td>yes</td>
|
|
<td>disabled</td>
|
|
<td>
|
|
If checked (xml-attr: disabled="true") the widget will NOT be shown.
|
|
For buttons this could be archived on runtime via setting them readonly.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>onChange</b></td>
|
|
<td>onchange</td>
|
|
<td>?</td>
|
|
<td>onchange</td>
|
|
<td>
|
|
If checked (xml-attr: onchange="1") and the contet of the widget is changed by
|
|
the user, the form will be submitted.
|
|
Via xml or by a program other values can be set (not in the editor at the moment and
|
|
this is not compatible with the phpGTK implementation of the eTemplates).
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Span, Class</b></td>
|
|
<td>span<br />class</td>
|
|
<td>span: no<br />class: yes</td>
|
|
<td>span</td>
|
|
<td>
|
|
In the editor and internaly this field contains two comma-separated values:<p>
|
|
<b>span</b>: how many cell a widget should span (default is one), the special value
|
|
of 'all' can be used to indicate it should span all remaining cells of a row.
|
|
This is not supported by xul-grid's at the moment, but is planned to be.<p>
|
|
<b>class</b>: the CSS class for the widget.
|
|
If the class-string starts with a '@' it is replaced by the value of the content-array at this
|
|
index (with the '@'-removed and after expanding the variables).
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Align</b></td>
|
|
<td>align</td>
|
|
<td>yes</td>
|
|
<td>align</td>
|
|
<td>
|
|
Can be set to 'left' (default), 'center' or 'right'.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Width, Disabled</b><br />column-attr.</td>
|
|
<td>width</td>
|
|
<td>yes</td>
|
|
<td>row[0][#]</td>
|
|
<td>
|
|
Can be set to a percentage (eg. '10%'), a number of pixels or ...
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Height, Disabled</b><br />row-attr.</td>
|
|
<td>height</td>
|
|
<td>yes</td>
|
|
<td>row[0][h#]</td>
|
|
<td>
|
|
Can be set to a percentage (eg. '10%'), a number of pixels or ...
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Disabled</b><br />column-attr.<br />row-attr.</td>
|
|
<td>disabled</td>
|
|
<td>no</td>
|
|
<td>disabled</td>
|
|
<td>
|
|
Syntax: <b>[!]{@name|value}[={@name2|value2}]</b><br />
|
|
Disables (=dont show it) a row/column if a certain criteria is (not (=!)) meet.<br />
|
|
If no '=...' / 2. value is given, the test is made on the first value being not empty, else
|
|
the test is made by comparing the two values. Instead of a value you can give a name as an
|
|
index into the content prefixed by @. Examples:<br />
|
|
<b>!@data</b> disables row/col if value of data is empty, <br />
|
|
<b>@val=false</b> disables if value of val is equal to (the string) 'false'
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Class, Valign</b><br />row-attr.</td>
|
|
<td>class<br />valign</td>
|
|
<td>both: yes</td>
|
|
<td>row[0][c#]</td>
|
|
<td>
|
|
In the editor and internaly this field contains two comma-separated values:<p>
|
|
<b>class</b>: the CSS class for the row, there are 4 predefined css-classes:
|
|
nmh: next-match-header-background, nmr: alternating next-match-row-background which
|
|
gets replaced by the etemplate class with nmr0 or nmr1.<p>
|
|
<b>valign</b>: vertical alignment of the widgets in the row: 'top', 'middle' (default)
|
|
or 'bottom'
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>blurText</b></td>
|
|
<td>blur</td>
|
|
<td>no</td>
|
|
<td>blur</td>
|
|
<td>
|
|
This text get displayed if an input-field is empty <u>and</u> does not have the
|
|
input-focus (blur). It can be used to show a default value or a kind of help-text.<br />
|
|
If it contains a text (eg. 'Search...'), this text is run through lang(), if it contains
|
|
a reference to the content array (eg. '@blur_text') it does <u>not</u> get translated.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<a name="widgets">
|
|
<h2>standard widgets and extensions of the eTemplates</h2>
|
|
<table border="1">
|
|
<tr>
|
|
<th>Widget Name<br />in Editor</th>
|
|
<th>xml tag</th>
|
|
<th>xul</th>
|
|
<th>internal name</th>
|
|
<th>description of the widget<th>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Label</b></td>
|
|
<td><description /></td>
|
|
<td>yes</td>
|
|
<td>label</td>
|
|
<td>
|
|
<b>a textual label</b><br />
|
|
The content is taken from the content-array but it can have an own label from
|
|
the label attribute too.<p>
|
|
<b>Options</b> has 5 comma-separated fields:<br />
|
|
<b>1.</b> if it contains a 'i' and/or a 'b' the content (not the label) is rendered
|
|
in italic and/or bold.<br />
|
|
<b>2.</b> link: if set to a menuaction string or an array with get-params (via the content-arry),
|
|
a link to that methode = app.class.method is put around the label<br />
|
|
<b>3.</b> if set URLs in the content get activated<br />
|
|
<b>4.</b> name of form-element the label is for: gives focus to that element if the label gets clicked<br />
|
|
<b>5.</b> target for the link, eg. _blank<br />
|
|
<b>6.</b> width<i>x</i>height if a popup should be used for the link, eg. 600x400
|
|
<b>7.</b> title<i>x</i>title for the link
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Text</b></td>
|
|
<td><textbox /></td>
|
|
<td>yes</td>
|
|
<td>text</td>
|
|
<td>
|
|
<b>a single-line input field for text</b><br />
|
|
In the html-UI this is rendered as <input ...><p>
|
|
<b>Options</b> has 3 comma-separated fields:<br />
|
|
xml: <b>size</b>: the length in chars of the input-field<br />
|
|
xml: <b>maxlength</b>: the maximum length of the input<br />
|
|
xml: <b>validator</b>: perl regular expression to validate the input (kommas are allowed in the expression)
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Integer</b></td>
|
|
<td><textbox<br />type="int" /></td>
|
|
<td>?</td>
|
|
<td>int</td>
|
|
<td>
|
|
<b>a input-field to enter an integer</b><br />
|
|
In the html-UI this is rendered as <input ...>. <i>The input-validation is done at the moment only on server-side,
|
|
clientside validation and input-restriction to only numbers is planed.</i><p>
|
|
<b>Options</b> has 3 comma-separated fields:<br />
|
|
xml: <b>min</b>: minimum value, default none, empty values are Ok, as long as <b>needed</b> is not set<br />
|
|
xml: <b>max</b>: maximum value, default none, empty values are Ok, as long as <b>needed</b> is not set<br />
|
|
xml: <b>size</b>: the length in chars of the input-field, default 5
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Float</b></td>
|
|
<td><textbox<br />type="float"/></td>
|
|
<td>?</td>
|
|
<td>float</td>
|
|
<td>
|
|
<b>a input-field to enter a float</b><br />
|
|
In the html-UI this is rendered as <input ...>. <i>The input-validation is done at the moment only on server-side,
|
|
clientside validation and input-restriction to only numbers is planed.</i><p>
|
|
<b>Options</b> has 4 comma-separated fields:<br />
|
|
xml: <b>min</b>: minimum value, default none, empty values are Ok, as long as <b>needed</b> is not set<br />
|
|
xml: <b>max</b>: maximum value, default none, empty values are Ok, as long as <b>needed</b> is not set<br />
|
|
xml: <b>size</b>: the length in chars of the input-field, default 5
|
|
xml: <b>precision</b>: precision of the float number, default maximum
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Textarea</b></td>
|
|
<td><textbox<br />multiline="true" /></td>
|
|
<td>yes</td>
|
|
<td>textarea</td>
|
|
<td>
|
|
<b>a multiline input-field for text</b><br />
|
|
In the html-UI this is rendered as <textarea ...>.<p>
|
|
<b>Options</b> has 2 comma-separated fields:<br />
|
|
xml: <b>cols</b>: the width of the field in chars<br />
|
|
xml: <b>rows</b>: the number of rows
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Formatted Text</b><br />(HTML)</td>
|
|
<td><htmlarea /></td>
|
|
<td>no</td>
|
|
<td>htmlarea</td>
|
|
<td>
|
|
<b>a multiline input-field for formatted (HTML) text</b><br />
|
|
In the html-UI this is rendered as <textarea ...> and the HTMLarea javascript editor is used.<p>
|
|
<b>Options</b> has 2 comma-separated fields:<br />
|
|
xml: <b>style</b>: inline styles for the text/htmlarea<br />
|
|
xml: <b>plugins</b>: plugins (comma-separated) to load
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Checkbox</b></td>
|
|
<td><checkbox /></td>
|
|
<td>yes</td>
|
|
<td>checkbox</td>
|
|
<td>
|
|
<b>a widget that can be checked or unchecked</b><br />
|
|
In the html-UI this is rendered as <input type="checkbox" ...>.
|
|
<p>Multiple checkboxes can have an identical name ending with [], in that case the value will be an array with the set_value's
|
|
of the checked boxes. You can use a button with a custom javascript onclick action of eg.
|
|
"toggle_all(this.form,form::name('nm[rows][checkbox][]')); return false;" and a set_value of "$row_cont[id]" to toggle
|
|
all checkboxes in the lines of a nextmatch widget. The <b>form::name( )</b> function translate the name used
|
|
in the template into the name used in the form. <i>If the button is an image-button, check needed to render it as button
|
|
and not as image with link, which has no this.form property!</i></p>
|
|
<b>Options</b>: [set_value][,unset_value[,ro_true[,ro_false]]]<br />
|
|
<b>set_value</b>: which value in the content represents the checked state, default=1<br />
|
|
<b>unset_value</b>: value in the content representing the unchecked state, default=0<br />
|
|
<b>ro_true</b>: what should be displayed for a readonly checked box, default=x<br />
|
|
<b>ro_false</b>: display for an unchecked box, can be set to 'disable', to not display the widget (incl. label), default is empty
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Radiobutton</b></td>
|
|
<td><radio /></td>
|
|
<td>?</td>
|
|
<td>radio</td>
|
|
<td>
|
|
<b>a widget in a group of which only one can be checked</b><br />
|
|
In the html-UI this is rendered as <input type="radio" ...><br />
|
|
Unlike XUL (and like html) the radio-buttons are grouped by giving them the same name / id.<br />
|
|
<b>Options</b>: [set_value][,ro_true[,ro_false]]<br />
|
|
<b>set_value</b>: which value in the content represents the checked state, default=1<br />
|
|
<b>ro_true</b>: what should be displayed for a readonly checked box, default=x<br />
|
|
<b>ro_false</b>: display for an unchecked box, can be set to 'disable', to not display the widget (incl. label), default is empty
|
|
If the value of the content array at index name/id matches <b>set_value</b> the radiobutton is marked 'checked'.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Submitbutton</b></td>
|
|
<td><button image="img.gif" ro_image="img-grey.gif" /></td>
|
|
<td>yes</td>
|
|
<td>button</td>
|
|
<td>
|
|
<b>a button to submit the form / end the dialog</b><br />
|
|
In the html-UI this is rendered as <input type="submit" ...>.<br />
|
|
If a button is set readonly (via seting its id in the $readonlys array passed to exec) it is not rendered
|
|
at all (if no ro_image is given), like it would be disabled.<p>
|
|
<b>needed</b>: if set and the user has JavaScript enabled the button is renderd as a link around the label
|
|
and a hidden input to set id if the link is clicked.<br />
|
|
<b>Options</b> xml: <b>image, ro_image</b>: Image to use instead of a Button with a label. There will
|
|
be no button around the image. If a ro_image is given (separated by a comma in the editors options)
|
|
it will be used if the button is set readonly (else the button is no rendered at all) .
|
|
<b>onclick</b>: specify some java-script to be called if the button gets pressed/clicked: <br />
|
|
a) general javascript: "window.close();"<br />
|
|
b) confirmation: "return window.confirm('<message>');" (message get run through lang()!)<br />
|
|
c) popup: app.class.func&id=$cont[id],target(default _blank),width (default 600),height (default 450)
|
|
You can use $cont[<name>] or $row_cont[<name>] (note no quotes!) to pass further information to the popup
|
|
via the content array.)
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Horizonatal Rule</b></td>
|
|
<td><hrule /></td>
|
|
<td>no</td>
|
|
<td>hrule</td>
|
|
<td>
|
|
<b>a horizontal rule / line</b><br />
|
|
In the html-UI this is rendered as <hr ...><br />
|
|
<b>Options</b> can contain a width of the rule, default is 100%
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Template</b></td>
|
|
<td><template id="app.name" content="subarr" /></td>
|
|
<td>yes</td>
|
|
<td>template</td>
|
|
<td>
|
|
<b>a separate eTemplate to be loaded into this cell</b><br />
|
|
<b>Name</b> xml: <b>id</b>: the name of the etemplate to load<br />
|
|
<b>Options</b> xml: <b>content</b>: if set, the template uses an own sub-array
|
|
of the content array indexed by the value of this field (if not the full
|
|
content-array is used). Variables like $row can be used as descript
|
|
for the general attribute Name.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Image</b></td>
|
|
<td><image src="foo.gif" label="Get a foo" options="app.class.method" /></td>
|
|
<td>yes</td>
|
|
<td>image</td>
|
|
<td>
|
|
<b>shows an image</b><br />
|
|
<b>Label</b> xml: <b>label</b>: the label is shown as tooltip (like html-title)<br />
|
|
<b>Name</b> xml: <b>src</b>: the name of the image to load, the image is search in the apps template-dirs<br />
|
|
<b>Options</b> xml: <b>options</b>: up to 4 comma-separated values:<br />
|
|
<b>1.</b> link to a methode = app.class.method for the image<br />
|
|
<b>2.</b> target for the link, eg. _blank<br />
|
|
<b>3.</b> imagemap<br />
|
|
<b>4.</b> width<i>x</i>height if a popup should be used for the link, eg. 600x400<br />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Selectbox</b></td>
|
|
<td>
|
|
<menulist><br />
|
|
<menupopup id="name" options="Select one" /><br />
|
|
</menulist><p>
|
|
<i>multiselect: <b>options</b> > 1</i><br />
|
|
<listbox rows="#"/><p>
|
|
<i>Examples for predefined selectboxes</i>:<p>
|
|
<listbox type="select-cat" rows="5"/><p>
|
|
<menulist><br />
|
|
<menupopup type="select-account" options="All,both,2"/><br />
|
|
</menulist><p>
|
|
|
|
|
|
</td>
|
|
<td>yes</td>
|
|
<td>select</td>
|
|
<td>
|
|
<b>shows a selectbox</b><br />
|
|
The content of the selectbox / the options have to be in an array which can be in 2 locations:
|
|
<ol>
|
|
<li>in $content["options-$name"]
|
|
<li>or in an separate array only for select-box-options under the index name, this array is passed
|
|
to the exec or show function of the etemplate-class
|
|
</ol>
|
|
<b>Options</b> <i>in the editor</i>: if set and > 1 the selectbox is a multiselection with options number of lines<p>
|
|
xml: <b>rows</b>: <i>only for <listbox></i>: number of rows to show<p>
|
|
xml <b>options</b>: <i>only for <menupopup/></i>: textual label for a first Row, e.g. 'All' or 'None'
|
|
(id will be ''), additional attr see sub-types<p>
|
|
xml: <b>type</b>: can be set to get several predefined select-contents, in that case you dont need to set
|
|
the content as descripted above (if set it too its in front of the predefined rows): <br />
|
|
<b>select-cat</b>:<br />
|
|
Select a phpgw category<br />
|
|
<b>select-account</b>:<br />
|
|
Select a user and/or group, determined by the options-field:<br />
|
|
,{accounts(default)|groups|both},{''(phpgw-default)|0(only lid)|1(only names)|2(both)}<br />
|
|
<b>select-percent, select-priority, select-access, select-country, select-state</b>:<br />
|
|
as you expect by the name<br />
|
|
<b>select-year, select-month, select-day</b>:<br />
|
|
options for year: ,start,end (start and end can be a number of
|
|
years from now or if > 100 a absolut year)<br />
|
|
<b>select-number</b>:<br />
|
|
Select a number out of a range specified by the options-field:<br />
|
|
,{start (default=1)},{end (incl., default=10)},{decrement (default={padding zeros}1)}.<br />
|
|
Example with padding zeros: options=',0,59,05' will give values: 00, 05, 10, ..., 55
|
|
(like you would use it for minutes in a time-field)<br />
|
|
<b>select-dow</b>:<br />
|
|
Select one or multiple weekdays, keys are as defined in MCAL_M_... (1=Sun, 2=Mon, 4=Tue, ...)<br />
|
|
<b>select-app</b>:<br />
|
|
Select an application, availible options: ,{''=user enabled(default)|installed|all)}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>FileUpload</b></td>
|
|
<td>
|
|
<file id="name"/><br />
|
|
</td>
|
|
<td>no</td>
|
|
<td>file</td>
|
|
<td>
|
|
<b>Input and Button to select a file for uploading</b><br />
|
|
Returns the file-name of the uploaded file in the servers tmp-dir (the webserver needs to have a
|
|
writable tmp-dir) plus, if javascript is enabled, the local filename of the client as "${name}_path".
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Date</b></td>
|
|
<td>
|
|
<date options="Y-m-d,1"/><p>
|
|
<date type="date-time"/><p>
|
|
<date type="date-timeonly" options="H:i"/><p>
|
|
<date type="date-houronly"/><p>
|
|
<date type="date-duration"/>
|
|
</td>
|
|
<td>no</td>
|
|
<td>date</td>
|
|
<td>
|
|
<b>Date-/Time-input</b> via selectboxes or a field for the year<br />
|
|
The order of the input-fields is determined by the prefs of the user.<br />
|
|
<b>Options</b>: [datetime-storage-format] [,&1=year-no-selectbox|&2=today-button|&4=one-min-steps|&8=ro-suppress-0h0]<br />
|
|
<b>datetime-storage-format</b> is the format, in which the date is stored in the variable:
|
|
empty means an unix-timestamp (in GMT), or a string containing the letters <b>Y</b>, <b>m</b>,
|
|
<b>d</b>, <b>H</b>, <b>i</b> plus separators, eg. 'Y-m-d': 2002-12-31. The storage format
|
|
for times is always 24h or timestamp with date 1.1.1970 (if no date used). (This has nothing
|
|
to do with the format of the display, which is only determined by the users preferences.)<br />
|
|
<b>year-no-selectbox</b> if set (&1) an int-widget (input-field) is used instead of a
|
|
select-year widget.<br />
|
|
<b>today-button</b>: if set (&2) a [Today] button is displayed which sets the fields to
|
|
the up-to-date date (via javascript)<br />
|
|
<b>one-min-steps</b>: if set (&4) the minute-selectbox uses one minutes steps, default 5min steps<br />
|
|
<b>ro-suppress-0h0</b>: if set (&8) the time is suppressed for readonly and a time of 0h0<br />
|
|
<b>day-of-week-prefix</b>: if set (&16) readonly dates get prefixed with the day of week
|
|
<b>Sub-widgets</b>: <b>date-time</b>: a date and a time and <b>date-timeonly</b> or <b>date-houronly</b>: only a time / hour<br />
|
|
These widgets allow the input of times too or only, they use 12h am/pm or 24h format as
|
|
specified in the user prefs.<br />
|
|
<i>If readonly is set, this widget can be used to display a date, without the need to convert it.</i><p>
|
|
<b>Duration</b> a floating point input with an optional selectbox for the unit (hours or days)<br>
|
|
<b>Options</b>: [duration-storage-format] [,[duration-display][,hours_per_day]]<br />
|
|
<b>duration-storage-format</b>: 'h' = hours (float), 'd' = days (float), default minutes (integer)<br />
|
|
<b>duration-display</b>: 'd' = days, 'h' = hours, 'dh' = days or hours with selectbox, optional '%' allows to enter a percentage<br />
|
|
<b>hours_per_day</b>: conversation between hours and (working) day, default 8
|
|
<b>hours_per_day</b>: conversation between hours and (working) day, default 8
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>VBox, HBox, Box</b></td>
|
|
<td>
|
|
<vbox><br />
|
|
<widget ...><br />
|
|
<widget ...><br />
|
|
</vbox><p>
|
|
<hbox span="all"><br />
|
|
<widget ...><br />
|
|
<widget ...><br />
|
|
</hbox><p>
|
|
<box orient="horizontal"><br />
|
|
<widget ...><br />
|
|
<widget ...><br />
|
|
</box>
|
|
</td>
|
|
<td>yes</td>
|
|
<td>vbox, hbox, box</td>
|
|
<td>
|
|
vertical or horizontal <b>container</b> for child widgets. This is useful if one needs more
|
|
widgets or widgets outside the column- / row-order of a grid. HBox or VBox is rendered as Grid/html:table
|
|
with only one row or colum. Box is rendered as a html:div containing all child-widgets.<br />
|
|
Disabled child-cells are completly left out (no empty cells or rows get generated).<p>
|
|
<b>Options</b> <i>in the editor</i>: the number of cells in the box (does NOT need to be set in xml).<br />
|
|
<b>orient</b>: horizontal, vertical or none (means h/vbox as expected and no table for boxes)
|
|
<b>options</b>: cellpadding,cellspacing of the table
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>GroupBox</b></td>
|
|
<td>
|
|
<groupbox><br />
|
|
<caption label="Legend"/><br />
|
|
<widget ...><br />
|
|
<widget ...><br />
|
|
</groupbox><p>
|
|
</td>
|
|
<td>yes</td>
|
|
<td>groupbox</td>
|
|
<td>
|
|
container to visualy <b>group other widgets</b> by putting a border around them.<br />
|
|
The upper line may contain a legend. The widgets are ordered vertical, like a VBox.
|
|
Disabled child-cells are completly left out (no empty cells or rows get generated).<p>
|
|
<b>Options</b> <i>in the editor</i>: the number of cells in the box (does NOT need to be set in xml).<br />
|
|
<b>orient</b>: horizontal, vertical or none (defaults to vertical)
|
|
<b>options</b>: cellpadding,cellspacing of the table
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Tabs</b></td>
|
|
<td>
|
|
<tabbox id="name"><br />
|
|
<tabs><br />
|
|
<tab label="Tab 1" statustext="Help"/><br />
|
|
...<br />
|
|
</tabs><br />
|
|
<tabpanels><br />
|
|
<grid id="app.name.tab1"/><br />
|
|
...<br />
|
|
</tabpanels><br />
|
|
</tabbox>
|
|
</td>
|
|
<td>yes</td>
|
|
<td>tab</td>
|
|
<td>
|
|
<b>shows a tab-widget</b><br />
|
|
The tab-widget is implemented as an extension, as html does not have a tab-widget.<p>
|
|
The following fields / attributes are in the Editor and internaly in the class separeted by '|', in the
|
|
xml/xul-file the are attributes of each tab- or grid-tag:<br />
|
|
<b>Label</b> xml: <b>label</b>: the labels of the tabs eg. 'Tab 1|Tab 2|Tab 3'<br />
|
|
<b>Help</b> xml: <b>statustext</b>: of the tabs<br />
|
|
<b>Name</b> xml: <b>id</b>: the names/ids of the eTemplates/grid's to fill the bodies of the tabs, if the
|
|
name contains no '.', it will be prefixed with the name of the template the widget is in plus a '.'
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Manual</b></td>
|
|
<td>
|
|
<manual>
|
|
</td>
|
|
<td>no</td>
|
|
<td>manual</td>
|
|
<td>
|
|
<b>open the online help</b>: displays a small manual icon.<p>
|
|
<b>Name</b> xml: <b>id</b>: optional name of the manual page (as index into $content or direct).
|
|
If no manual page is given, the link included the referer as _GET param.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Custom fields</b></td>
|
|
<td>
|
|
<custom_fields>
|
|
</td>
|
|
<td>no</td>
|
|
<td>custom_fields</td>
|
|
<td>
|
|
<b>display custom fields</b>: the fields can be configured with admin.customfields.edit&appname={app}<p>
|
|
The indexes of the custom fields in content are prefixed with a hash (#).
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>NextMatch</b></td>
|
|
<td>
|
|
<nextmatch options="notes.index.rows" id="nm"/>
|
|
</td>
|
|
<td>yes</td>
|
|
<td>tab</td>
|
|
<td>
|
|
<b>shows a table with some selectboxes, a search-field and arrows to scroll the table</b><br />
|
|
The nextmatch-widget is implemented as an extension.<p>
|
|
<b>Options</b> xml: <b>options</b>: name of the template to display the rows<br />
|
|
<b>Name</b> xml: <b>id</b>: index into the content-array, it need to be pre-set with some information
|
|
for the nextmatch widget and it returns its content with it:
|
|
</td>
|
|
<tr>
|
|
<td colspan="5">
|
|
<pre>
|
|
$content[$id] = array( <span>// I = value set by the app, 0 = value on return / output</span>
|
|
'get_rows' => <span>// I method/callback to request the data for the rows eg. 'notes.bo.get_rows'</span>
|
|
'filter_label' => <span>// I label for filter (optional)</span>
|
|
'filter_help' => <span>// I help-msg for filter (optional)</span>
|
|
'no_filter' => True<span>// I disable the 1. filter</span>
|
|
'no_filter2' => True<span>// I disable the 2. filter (params are the same as for filter)</span>
|
|
'no_cat' => True<span>// I disable the cat-selectbox</span>
|
|
'template' => <span>// I template to use for the rows, if not set via options</span>
|
|
'header_left' => <span>// I template to show left of the range-value, left-aligned (optional)</span>
|
|
'header_right' => <span>// I template to show right of the range-value, right-aligned (optional)</span>
|
|
'bottom_too' => True<span>// I show the nextmatch-line (arrows, filters, search, ...) again after the rows</span>
|
|
'never_hide' => True<span>// I never hide the nextmatch-line if less then maxmatch entrie</span>
|
|
'lettersearch' => True<span>// I show a lettersearch</span>
|
|
'searchletter' => <span>// I0 active letter of the lettersearch or false for [all]</span>
|
|
'start' => <span>// IO position in list</span>
|
|
'num_rows' => <span>// IO number of rows to show, defaults to maxmatches from the general prefs</span>
|
|
'cat_id' => <span>// IO category, if not 'no_cat' => True</span>
|
|
'search' => <span>// IO search pattern</span>
|
|
'order' => <span>// IO name of the column to sort after (optional for the sortheaders)</span>
|
|
'sort' => <span>// IO direction of the sort: 'ASC' or 'DESC'</span>
|
|
'col_filter' => <span>// IO array of column-name value pairs (optional for the filterheaders)</span>
|
|
'filter' => <span>// IO filter, if not 'no_filter' => True</span>
|
|
'filter_no_lang' => True<span>// I set no_lang for filter (=dont translate the options)</span>
|
|
'filter_onchange'=> 'this.form.submit();'<span>// I onChange action for filter, default: this.form.submit();</span>
|
|
'filter2' => <span>// IO filter2, if not 'no_filter2' => True</span>
|
|
'filter2_no_lang'=> True<span>// I set no_lang for filter2 (=dont translate the options)</span>
|
|
'filter2_onchange'=> 'this.form.submit();'<span>// I onChange action for filter, default: this.form.submit();</span>
|
|
'rows' => <span>// O content set by callback</span>
|
|
'total' => <span>// O the total number of entries</span>
|
|
'sel_options' => <span>// O additional or changed sel_options set by the callback and merged into $tmpl->sel_options</span>
|
|
);
|
|
|
|
<span>/*
|
|
* example: the get_rows function from notes.bo.get_rows (has to be in public_functions !)
|
|
*/</span>
|
|
function get_rows($query,&$rows,&$readonlys)
|
|
{
|
|
$rows = $this->read($query['start'],$query['search'],$query['filter'],$query['cat_id']);
|
|
if (!is_array($rows))
|
|
{
|
|
$rows = array( );
|
|
}
|
|
$readonlys = array( ); <span>// set readonlys to enable/disable our edit/delete-buttons</span>
|
|
while (list($n,$note) = each($rows))
|
|
{
|
|
if (!$this->check_perms($this->grants[$note['owner_id']],PHPGW_ACL_EDIT))
|
|
{
|
|
$readonlys["edit[$note[id]]"] = True;
|
|
}
|
|
if (!$this->check_perms($this->grants[$note['owner_id']],PHPGW_ACL_DELETE))
|
|
{
|
|
$readonlys["delete[$note[id]]"] = True;
|
|
}
|
|
}
|
|
return $this->total_records;
|
|
}
|
|
|
|
<span>/*
|
|
* Example how the nextmatch-widget is used in notes.ui.index:
|
|
*/</span>
|
|
function index($content = 0)
|
|
{
|
|
if (!is_array($content))
|
|
{
|
|
$content = array('nm' => $this->session_data); <span>// restore settings from the session</span>
|
|
}
|
|
if (isset($content['nm']['rows'])) <span>// one of the buttons in the rows is pressed</span>
|
|
{
|
|
$this->session_data = $values['nm']; <span>// save the settings in the session</span>
|
|
unset($this->session_data['rows']); <span>// we dont want to save the content of the rows</span>
|
|
$this->save_sessiondata();
|
|
|
|
if (isset($values['nm']['rows']['edit']))
|
|
{
|
|
list($id) = each($values['nm']['rows']['edit']);
|
|
return $this->edit($id);
|
|
}
|
|
elseif (isset($values['nm']['rows']['delete']))
|
|
{
|
|
list($id) = each($values['nm']['rows']['delete']);
|
|
return $this->delete($id);
|
|
}
|
|
}
|
|
$values['nm']['options-filter'] = array ( <span>// set up the data for our filter</span>
|
|
'all' => 'Show all',
|
|
'public' => 'Only yours',
|
|
'private' => 'Private'
|
|
);
|
|
$values['nm']['get_rows'] = 'notes.bo.get_rows';
|
|
$values['nm']['no_filter2'] = True; <span>// disable the 2. filter</span>
|
|
|
|
$this->tpl->read('notes.index');
|
|
$this->tpl->exec('notes.ui.index',$values);
|
|
}
|
|
</pre>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Nextmatch-<br />SortHeader</b><p><b>Nextmatch-<br />FilterHeader</b><p><b>Nextmatch-<br />AccountFilter</b></td>
|
|
<td>
|
|
<nextmatch type="nextmatch-sortheader" id="col-name" options="DESC" label="ColLabel"/><p>
|
|
<nextmatch type="nextmatch-filterheader" id="col-name"/><p>
|
|
<nextmatch type="nextmatch-accountfilter" id="col-name"/>
|
|
</td>
|
|
<td>no</td>
|
|
<td>nextmatch-<br />sortheader<p>nextmatch-<br />filterheader<p>nextmatch-<br />accountfilter</td>
|
|
<td>
|
|
These widget are an optional part of the nextmatch widget.<p>
|
|
<b>nextmatch-sortheader</b><br />
|
|
Widget to be placed as a colum-header in the headerline of a nextmatch-template. It allows, by clicking on it,
|
|
to order the lines of the nextmatch after a certain column. The column-name is given as <b>name</b> (xml:id) the
|
|
<b>label</b> is show as a link of button (no javascript). One can specify a default sorting: <b>options</b>={DESC|ASC} (default=ASC),
|
|
to be used when the header is clicked for the first time. Consecutive click on the header change the sorting
|
|
direction, indicated by a little up- or down-arrow.<p>
|
|
<b>nextmatch-filterheader</b><br />
|
|
Widget to allow to show only certain row, which match a selected filter-value. The column-name is given as <b>name</b>
|
|
(xml:id), the options of the displayed selectbox need to be set as for an ordinary selectbox (eg. in the options
|
|
parameter to the uietemplate::exec function). If no extra-label is given in options, lang('all') will be used for
|
|
the empty value, which means no filter activ. An (optional) <b>label</b> can be given.<p>
|
|
<b>nextmatch-accountfilter</b><br />
|
|
The Accountfilter allows to select users (via the prefered user-selection-method) to filter by them. In all other aspects
|
|
it is identical to the filterheader.<p>
|
|
<i><u>Note</u>:</i> All three widgets interoperate with the nextmatch-widget which passes the set values as part if
|
|
the query-parameter to its get_rows function, they are <u>not</u> returned in the rows sub-array.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>LinkWidgets</b></td>
|
|
<td>
|
|
<link type="link-to" id="name"/><p>
|
|
<link type="link-list" id="name"/><p>
|
|
<link type="link-string" id="name"/><p>
|
|
</td>
|
|
<td>no</td>
|
|
<td>link-to<p>link-list<p>link-string</td>
|
|
<td>
|
|
These widget are the UI-part of the link-class ({bo|so}link) in InfoLog and maybe the API soon.<p>
|
|
<b>link-to</b><br />
|
|
Widget to make a links to other entries of link-aware apps and to attach files.<p>
|
|
<b>link-list</b><br />
|
|
Widget to shows the links to an entry and a Unlink Button for each link. <p>
|
|
<i><u>Note</u>:</i> Both Widgets can be used on the same template with the <u>same</u> name. They share
|
|
the content of that variable, which contains just the id of the entry in the current app.<p>
|
|
|
|
<b>link-string</b><br />
|
|
Comma-separated list of link-titles with a link to its view-method, value is like the return
|
|
of bolink::get_links().
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<h3>One remark about cross-site-scripting</h3>
|
|
The following eTemplate Widgets are parsing its content before displaying through <b>htmlspecialchars()</b> to
|
|
correctly display the content and to gard against malecious data (like scripts etc.):
|
|
<ul>
|
|
<li>Label
|
|
<li>Text, Textarea
|
|
<li>Integer, Float
|
|
<li>SelectBoxes (it's options-strings)
|
|
<li>LinkWidgets: link-to, link-list, link-string
|
|
</ul>
|
|
This is done in most cases by the underlaying html-class and not direct in eTemplate.
|
|
<p>
|
|
<hr>
|
|
<a name="extensions">
|
|
<h1>How to implement new widgets / extensions to the eTemplates?</h1>
|
|
<p>The eTemplates have an interface to extend them with new widgets. These widgets are php-classes, can use
|
|
eTemplates to define the UI of the new widget and are stored in the eTemplate's inc-dir or the inc-dir of a
|
|
eGroupWare application. The editor and the etemplate-class autoload the existing extensions.</p>
|
|
|
|
<i>I will made more documentation about the interface availible soon. For now have a look for the source
|
|
of the existing extensions.</i>
|
|
|
|
<h2>please <a href="#" onClick="document.location='mai'+'lto:RalfBecker'+unescape('%40')+'outdoor-training'+unescape('%2E')+'de'; return false;">contact me</a> if you have further questions or comments about the eTemplates</h2>
|
|
|
|
</body>
|
|
</html>
|