mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 08:39:07 +01:00
- etemplate_widget_transformer abstract baseclass to define new widgets using a transformation out of existing widgets
- defines a syntax to describe how the widget is build out of existing widgets --> reimplemented contact-widget using that aproach on serverside for old etemplate --> sending the modifications via etemplate_widget::setElementAttribute() to the client fails, because client does not support changing the widget type --> need to be implemented on the client
This commit is contained in:
parent
4114068cb3
commit
502ac42923
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* eGroupWare eTemplate Extension - Contact Widget
|
||||
* EGroupware eTemplate Extension - Contact Widget
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
@ -15,22 +15,24 @@
|
||||
*
|
||||
* This widget can be used to fetch fields of a contact specified by contact-id
|
||||
*/
|
||||
class contact_widget
|
||||
class contact_widget extends etemplate_widget_transformer
|
||||
{
|
||||
/**
|
||||
* exported methods of this class
|
||||
*
|
||||
* @var array $public_functions
|
||||
* @deprecated only used for old etemplate
|
||||
*/
|
||||
var $public_functions = array(
|
||||
public $public_functions = array(
|
||||
'pre_process' => True,
|
||||
);
|
||||
/**
|
||||
* availible extensions and there names for the editor
|
||||
*
|
||||
* @var string/array $human_name
|
||||
* @var string|array $human_name
|
||||
* @deprecated only used for old etemplate
|
||||
*/
|
||||
var $human_name = array(
|
||||
public $human_name = array(
|
||||
'contact-value' => 'Contact',
|
||||
'contact-account' => 'Account contactdata',
|
||||
'contact-template' => 'Account template',
|
||||
@ -41,24 +43,144 @@ class contact_widget
|
||||
*
|
||||
* @var contacts
|
||||
*/
|
||||
var $contacts;
|
||||
private $contacts;
|
||||
|
||||
/**
|
||||
* Cached contact
|
||||
* Array with a transformation description, based on attributes to modify.
|
||||
*
|
||||
* Exampels:
|
||||
*
|
||||
* * 'type' => array('some' => 'other')
|
||||
* if 'type' attribute equals 'some' replace it with 'other'
|
||||
*
|
||||
* * 'type' => array('some' => array('type' => 'other', 'options' => 'otheroption')
|
||||
* same as above, but additonally set 'options' attr to 'otheroption'
|
||||
*
|
||||
* --> leaf element is the action, if previous filters are matched:
|
||||
* - if leaf is scalar, it just replaces the previous filter value
|
||||
* - if leaf is an array, it contains assignments for (multiple) attributes: attr => value pairs
|
||||
*
|
||||
* * 'type' => array(
|
||||
* 'some' => array(...),
|
||||
* 'other' => array(...),
|
||||
* '__default__' => array(...),
|
||||
* )
|
||||
* it's possible to have a list of filters with actions to run, plus a '__default__' which matches all not explicitly named values
|
||||
*
|
||||
* * 'value' => array('__callback__' => 'app.class.method' || 'class::method' || 'method')
|
||||
* run value through a *serverside* callback, eg. reading an entry based on it's given id
|
||||
*
|
||||
* * 'value' => array('__js__' => 'function(value) { return value+5; }')
|
||||
* run value through a *clientside* callback running in the context of the widget
|
||||
*
|
||||
* * 'name' => '@name[@options]'
|
||||
* replace value of 'name' attribute with itself (@name) plus value of options in square brackets
|
||||
*
|
||||
* --> attribute name prefixed with @ sign means value of given attribute
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $contact;
|
||||
protected static $transformation = array(
|
||||
'type' => array(
|
||||
'contact-fields' => array( // contact-fields widget
|
||||
'sel_options' => array('__callback__' => 'get_contact_fields'),
|
||||
'type' => 'select',
|
||||
'no_lang' => true,
|
||||
'options' => 'None',
|
||||
),
|
||||
'contact-template' => array(
|
||||
'value' => array('__callback__' => 'get_contact'),
|
||||
'type' => 'template',
|
||||
'options' => '',
|
||||
'label' => array(
|
||||
'' => array('id' => '@value[@id]'),
|
||||
'__default__' => array('id' => '@label@value[@id]'), // non-empty label prefixes value
|
||||
),
|
||||
),
|
||||
'__default__' => array(
|
||||
'value' => array('__callback__' => 'get_contact'),
|
||||
'id' => '@id[@options]',
|
||||
'options' => array(
|
||||
'' => array('id' => '@value[@id]'),
|
||||
'bday' => array('type' => 'date', 'options' => 'Y-m-d'),
|
||||
'owner' => array('type' => 'select-account', 'options' => ''),
|
||||
'modifier' => array('type' => 'select-account', 'options' => ''),
|
||||
'creator' => array('type' => 'select-account', 'options' => ''),
|
||||
'modifed' => array('type' => 'date-time', 'options' => ''),
|
||||
'created' => array('type' => 'date-time', 'options' => ''),
|
||||
'cat_id' => array('type' => 'select-cat', 'options' => ''),
|
||||
'__default__' => array('type' => 'label', 'options' => ''),
|
||||
),
|
||||
'readonly' => true,
|
||||
'no_lang' => 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor of the extension
|
||||
*
|
||||
* @param string $ui '' for html
|
||||
*/
|
||||
function contact_widget($ui)
|
||||
function __construct($xml)
|
||||
{
|
||||
$this->ui = $ui;
|
||||
if (is_a($xml, 'XMLReader') || $xml != '')
|
||||
{
|
||||
parent::__construct($xml);
|
||||
}
|
||||
$this->contacts = $GLOBALS['egw']->contacts;
|
||||
}
|
||||
|
||||
$this->contacts =& $GLOBALS['egw']->contacts;
|
||||
/**
|
||||
* Get all contact-fields
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_contact_fields()
|
||||
{
|
||||
translation::add_app('addressbook');
|
||||
$this->contacts->__construct();
|
||||
$options = $this->contacts->contact_fields;
|
||||
foreach($this->contacts->customfields as $name => $data)
|
||||
{
|
||||
$options['#'.$name] = $data['label'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contact data, if $value not already contains them
|
||||
*
|
||||
* @param int|string|array $value
|
||||
* @param array $attrs
|
||||
* @return array
|
||||
*/
|
||||
public function get_contact($value, array $attrs)
|
||||
{
|
||||
if (is_array($value)) return $value;
|
||||
|
||||
switch($attrs['type'])
|
||||
{
|
||||
case 'contact-account':
|
||||
case 'contact-template':
|
||||
if (substr($value,0,8) != 'account:')
|
||||
{
|
||||
$value = 'account:'.($attrs['name'] != 'account:' ? $value : $GLOBALS['egw_info']['user']['account_id']);
|
||||
}
|
||||
// fall-throught
|
||||
case 'contact-value':
|
||||
default:
|
||||
if (substr($value,0,12) == 'addressbook:') $value = substr($value,12); // link-entry syntax
|
||||
if (!($contact = $this->contacts->read($value)))
|
||||
{
|
||||
$contact = array();
|
||||
}
|
||||
break;
|
||||
}
|
||||
unset($contact['jpegphoto']); // makes no sense to return binary image
|
||||
|
||||
//error_log(__METHOD__."('$value') returning ".array2string($contact));
|
||||
return $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,19 +196,15 @@ class contact_widget
|
||||
* @param etemplate &$tmpl reference to the template we belong too
|
||||
* @return boolean true if extra label is allowed, false otherwise
|
||||
*/
|
||||
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
|
||||
/* old code now replaced with etemplate_widget_transformer::pre_process() ...
|
||||
|
||||
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
|
||||
{
|
||||
//echo "<p>contact_widget::pre_process('$name','$value',".print_r($cell,true).",...)</p>\n";
|
||||
switch($type = $cell['type'])
|
||||
{
|
||||
case 'contact-fields':
|
||||
$GLOBALS['egw']->translation->add_app('addressbook');
|
||||
$this->contacts->__construct();
|
||||
$cell['sel_options'] = $this->contacts->contact_fields;
|
||||
foreach($this->contacts->customfields as $name => $data)
|
||||
{
|
||||
$cell['sel_options']['#'.$name] = $data['label'];
|
||||
}
|
||||
$cell['sel_options'] = $this->get_contact_fields();
|
||||
$cell['type'] = 'select';
|
||||
$cell['no_lang'] = 1;
|
||||
$cell['size'] = 'None';
|
||||
@ -98,6 +216,7 @@ class contact_widget
|
||||
{
|
||||
$value = 'account:'.($cell['name'] != 'account:' ? $value : $GLOBALS['egw_info']['user']['account_id']);
|
||||
}
|
||||
echo "<p>$name: $value</p>\n";
|
||||
// fall-throught
|
||||
case 'contact-value':
|
||||
default:
|
||||
@ -158,5 +277,7 @@ class contact_widget
|
||||
$cell['id'] = ($cell['id'] ? $cell['id'] : $cell['name'])."[$type]";
|
||||
|
||||
return True; // extra label ok
|
||||
}
|
||||
}*/
|
||||
}
|
||||
// register widgets for etemplate2
|
||||
etemplate_widget::registerWidget('contact_widget',array('contact-value', 'contact-account', 'contact-template', 'contact-fields'));
|
||||
|
@ -1122,12 +1122,28 @@ class etemplate extends boetemplate
|
||||
{
|
||||
$readonlys[$name] = true;
|
||||
}
|
||||
$cell_name = $cell['name'];
|
||||
$extra_label = $this->extensionPreProcess($type,$form_name,$value,$cell,$readonlys[$name]);
|
||||
|
||||
$readonly = $cell['readonly'] !== false && ($readonly || $cell['readonly']); // might be set or unset (===false) by extension
|
||||
|
||||
//echo "<p>set_array(\$content, '$name', ".array2string($value).")</p>\n";
|
||||
self::set_array($content,$name,$value);
|
||||
|
||||
// if widget changes the name (eg. by new widget-transformer), reevaluate name, form_name and value
|
||||
if ($cell['name'] && $cell['name'] != $cell_name)
|
||||
{
|
||||
$name = $this->expand_name($cell['name'],$show_c,$show_row,$content['.c'],$content['.row'],$content);
|
||||
// allow names like "tabs=one|two|three", which will be equal to just "tabs"
|
||||
// eg. for tabs to use a name independent of the tabs contained
|
||||
if (is_string($name) && strpos($name,'=') !== false)
|
||||
{
|
||||
list($name) = explode('=',$name);
|
||||
}
|
||||
$form_name = self::form_name($cname,$name);
|
||||
|
||||
$value = $this->get_array($content,$name);
|
||||
}
|
||||
|
||||
if ($cell['type'] == $type.'-'.$sub_type) break; // stop if no further type-change
|
||||
|
||||
list($type,$sub_type) = explode('-',$cell['type']);
|
||||
|
@ -129,7 +129,7 @@ class etemplate_new extends etemplate_widget_template
|
||||
// instanciate template to fill self::$request->sel_options for select-* widgets
|
||||
// not sure if we want to handle it this way, thought otherwise we will have a few ajax request for each dialog fetching predefined selectboxes
|
||||
$template = etemplate_widget_template::instance($this->name, $this->template_set, $this->version, $this->laod_via);
|
||||
$template->run('fillTypeOptions');
|
||||
$template->run('beforeSendToClient');
|
||||
|
||||
$data = array(
|
||||
'etemplate_exec_id' => self::$request->id(),
|
||||
|
@ -15,6 +15,7 @@
|
||||
require_once EGW_INCLUDE_ROOT.'/etemplate/inc/class.etemplate_widget_textbox.inc.php';
|
||||
require_once EGW_INCLUDE_ROOT.'/etemplate/inc/class.etemplate_widget_grid.inc.php';
|
||||
require_once EGW_INCLUDE_ROOT.'/etemplate/inc/class.etemplate_widget_checkbox.inc.php';
|
||||
require_once EGW_INCLUDE_ROOT.'/etemplate/inc/class.contact_widget.inc.php';
|
||||
|
||||
/**
|
||||
* eTemplate widget baseclass
|
||||
@ -234,6 +235,10 @@ class etemplate_widget
|
||||
*/
|
||||
public static function registerWidget($class, $widgets)
|
||||
{
|
||||
if (!is_subclass_of($class, __CLASS__))
|
||||
{
|
||||
throw new egw_exception_wrong_parameter(__METHOD__."('$class', ".array2string($widgets).") $class is no subclass or ".__CLASS__.'!');
|
||||
}
|
||||
foreach((array)$widgets as $widget)
|
||||
{
|
||||
self::$widget_registry[$widget] = $class;
|
||||
@ -676,11 +681,11 @@ class etemplate_widget
|
||||
*/
|
||||
public function &setElementAttribute($name,$attr,$val)
|
||||
{
|
||||
$attr =& self::$request->modifications[$name][$attr];
|
||||
if (!is_null($val)) $attr = $val;
|
||||
$ref =& self::$request->modifications[$name][$attr];
|
||||
if (!is_null($val)) $ref = $val;
|
||||
|
||||
error_log(__METHOD__."('$name', '$attr', ".array2string($val).')');
|
||||
return $attr;
|
||||
return $ref;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,7 +78,7 @@ class etemplate_widget_menupopup extends etemplate_widget
|
||||
$value = $value_in = self::get_array($content, $form_name);
|
||||
|
||||
$allowed = $this->attrs['multiple'] ? array() : array('' => $this->attrs['options']);
|
||||
/* if fillTypeOptions is used, we dont need to call it again here
|
||||
/* if beforeSendToClient is used, we dont need to call it again here
|
||||
if ($this->attrs['type'])
|
||||
{
|
||||
$allowed += self::typeOptions($form_name, $this->attrs['type'], $this->attrs['no_lang']);
|
||||
@ -114,7 +114,7 @@ class etemplate_widget_menupopup extends etemplate_widget
|
||||
*
|
||||
* @param string $cname
|
||||
*/
|
||||
public function fillTypeOptions($cname)
|
||||
public function beforeSendToClient($cname)
|
||||
{
|
||||
if ($this->attrs['type'])
|
||||
{
|
||||
|
222
etemplate/inc/class.etemplate_widget_transformer.inc.php
Normal file
222
etemplate/inc/class.etemplate_widget_transformer.inc.php
Normal file
@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware - eTemplate serverside base widget, to define new widgets using a transformation out of existing widgets
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Ralf Becker <RalfBecker@outdoor-training.de>
|
||||
* @copyright 2002-11 by RalfBecker@outdoor-training.de
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* eTemplate serverside base widget, to define new widgets using a transformation out of existing widgets
|
||||
*/
|
||||
abstract class etemplate_widget_transformer extends etemplate_widget
|
||||
{
|
||||
/**
|
||||
* Array with a transformation description, based on attributes to modify.
|
||||
*
|
||||
* Exampels:
|
||||
*
|
||||
* * 'type' => array('some' => 'other')
|
||||
* if 'type' attribute equals 'some' replace it with 'other'
|
||||
*
|
||||
* * 'type' => array('some' => array('type' => 'other', 'options' => 'otheroption')
|
||||
* same as above, but additonally set 'options' attr to 'otheroption'
|
||||
*
|
||||
* --> leaf element is the action, if previous filters are matched:
|
||||
* - if leaf is scalar, it just replaces the previous filter value
|
||||
* - if leaf is an array, it contains assignments for (multiple) attributes: attr => value pairs
|
||||
*
|
||||
* * 'type' => array(
|
||||
* 'some' => array(...),
|
||||
* 'other' => array(...),
|
||||
* '__default__' => array(...),
|
||||
* )
|
||||
* it's possible to have a list of filters with actions to run, plus a '__default__' which matches all not explicitly named values
|
||||
*
|
||||
* * 'value' => array('__callback__' => 'app.class.method' || 'class::method' || 'method')
|
||||
* run value through a *serverside* callback, eg. reading an entry based on it's given id
|
||||
* callback signature: mixed function(mixed $attr[, array $attrs])
|
||||
*
|
||||
* * 'value' => array('__js__' => 'function(value) { return value+5; }')
|
||||
* run value through a *clientside* callback running in the context of the widget
|
||||
*
|
||||
* * 'name' => '@name[@options]'
|
||||
* replace value of 'name' attribute with itself (@name) plus value of options in square brackets
|
||||
* * 'value' => '@value[@options]'
|
||||
* replace value array with value for key taken from value of options attribute
|
||||
*
|
||||
* --> attribute name prefixed with @ sign means value of given attribute
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $transformation = array();
|
||||
|
||||
/**
|
||||
* Switching debug messages to error_log on/off
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
const DEBUG = true;
|
||||
|
||||
/**
|
||||
* Rendering transformer widget serverside as an old etemplate extension
|
||||
*
|
||||
* This function is called before the extension gets rendered
|
||||
*
|
||||
* @param string $name form-name of the control
|
||||
* @param mixed &$value value / existing content, can be modified
|
||||
* @param array &$cell array with the widget, can be modified for ui-independent widgets
|
||||
* @param array &$readonlys names of widgets as key, to be made readonly
|
||||
* @param mixed &$extension_data data the extension can store persisten between pre- and post-process
|
||||
* @param etemplate &$tmpl reference to the template we belong too
|
||||
* @return boolean true if extra label is allowed, false otherwise
|
||||
*/
|
||||
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
|
||||
{
|
||||
$cell['value'] =& $value;
|
||||
$cell['options'] =& $cell['size']; // old engine uses 'size' instead of 'options' for legacy options
|
||||
$cell['id'] =& $cell['name']; // dto for 'name' instead of 'id'
|
||||
|
||||
// run the transformation
|
||||
foreach(static::$transformation as $filter => $data)
|
||||
{
|
||||
$this->action($filter, $data, $cell);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill type options in self::$request->sel_options to be used on the client
|
||||
*
|
||||
* @param string $cname
|
||||
*/
|
||||
public function beforeSendToClient($cname)
|
||||
{
|
||||
$attrs = $this->attrs;
|
||||
$form_name = self::form_name($cname, $this->id);
|
||||
if (empty($this->id))
|
||||
{
|
||||
error_log(__METHOD__."() $this has no id!");
|
||||
return;
|
||||
}
|
||||
$attrs['value'] = $value =& self::get_array(self::$request->content, $form_name);
|
||||
$attrs['type'] = $this->type;
|
||||
$attrs['id'] = $this->id;
|
||||
|
||||
$unmodified = $attrs;
|
||||
|
||||
// run the transformation
|
||||
foreach(static::$transformation as $filter => $data)
|
||||
{
|
||||
$this->action($filter, $data, $attrs);
|
||||
}
|
||||
|
||||
//echo $this; _debug_array($unmodified); _debug_array($attrs); _debug_array(array_diff_assoc($attrs, $unmodified));
|
||||
// compute the difference and send it to the client as modifications
|
||||
foreach(array_diff_assoc($attrs, $unmodified) as $attr => $val)
|
||||
{
|
||||
switch($attr)
|
||||
{
|
||||
case 'value':
|
||||
if ($val != $value)
|
||||
{
|
||||
$value = $val; // $value is reference to self::$request->content
|
||||
}
|
||||
break;
|
||||
case 'sel_options':
|
||||
self::$request->sel_options[$form_name] = $val;
|
||||
break;
|
||||
case 'type': // not an attribute in etempalte2
|
||||
default:
|
||||
self::setElementAttribute($form_name, $attr, $val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively run given action(s) on an attribute value
|
||||
*
|
||||
* @param string $attr attribute concerned
|
||||
* @param int|string|array $action action to run
|
||||
* @param array &$attrs attributes
|
||||
* @throws egw_exception_wrong_parameter if $action is of wrong type
|
||||
*/
|
||||
function action($attr, $action, array &$attrs)
|
||||
{
|
||||
if (self::DEBUG) error_log(__METHOD__."('$attr', ".array2string($action).')');
|
||||
// action is an assignment
|
||||
if (is_scalar($action) || is_null($action))
|
||||
{
|
||||
// check if assignment contains placeholders --> replace them
|
||||
if (strpos($action, '@') !== false)
|
||||
{
|
||||
$replace = array();
|
||||
foreach($attrs as $a => $v)
|
||||
{
|
||||
if (is_scalar($v) || is_null($v)) $replace['@'.$a] = $v;
|
||||
}
|
||||
$action = strtr($action, $replace);
|
||||
// now replace with non-scalar value, eg. if values is an array: "@value", "@value[key] or "@value[@key]"
|
||||
if (($a = strstr($action, '@')))
|
||||
{
|
||||
$action = self::get_array($attrs, substr($a,1));
|
||||
}
|
||||
}
|
||||
$attrs[$attr] = $action;
|
||||
if (self::DEBUG) error_log(__METHOD__."('$attr', ".array2string($action).") attrs['$attr'] = ".array2string($action).', attrs='.array2string($attrs));
|
||||
}
|
||||
// action is a serverside callback
|
||||
elseif(is_array($action) && isset($action['__callback__']))
|
||||
{
|
||||
if (!is_string(($callback = $action['__callback__'])))
|
||||
{
|
||||
throw new egw_exception_wrong_parameter(__METHOD__."('$attr', ".array2string($action).', '.array2string($attrs).') wrong datatype for callback!');
|
||||
}
|
||||
if (method_exists($this, $callback))
|
||||
{
|
||||
$attrs[$attr] = $this->$callback($attrs[$attr], $attrs);
|
||||
}
|
||||
elseif(count(explode('.', $callback)) == 3)
|
||||
{
|
||||
$attrs[$attr] = ExecMethod($callback, $attrs[$attr], $attrs);
|
||||
}
|
||||
elseif (is_callable($callback, false))
|
||||
{
|
||||
$attrs[$attr] = call_user_func($callback, $attrs[$attr], $attrs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new egw_exception_wrong_parameter(__METHOD__."('$attr', ".array2string($action).', '.array2string($attrs).') wrong datatype for callback!');
|
||||
}
|
||||
}
|
||||
// action is a clientside callback
|
||||
elseif(is_array($action) && isset($action['__js__']))
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
// action is a switch --> check cases
|
||||
elseif(is_array($action))
|
||||
{
|
||||
// case matches --> run all actions
|
||||
if (isset($action[$attrs[$attr]]) || !isset($action[$attrs[$attr]]) && isset($action['__default__']))
|
||||
{
|
||||
$actions = isset($action[$attrs[$attr]]) ? $action[$attrs[$attr]] : $action['__default__'];
|
||||
if (self::DEBUG) error_log(__METHOD__."(attr='$attr', action=".array2string($action).") attrs['$attr']=='{$attrs[$attr]}' --> running actions");
|
||||
foreach($actions as $attr => $action)
|
||||
{
|
||||
$this->action($attr, $action, $attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new egw_exception_wrong_parameter(__METHOD__."(attr='$attr', action=".array2string($action).', attrs='.array2string($attrs).') wrong datatype for action!');
|
||||
}
|
||||
}
|
||||
}
|
22
etemplate/templates/default/test.contact_widget.xet
Normal file
22
etemplate/templates/default/test.contact_widget.xet
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- $Id$ -->
|
||||
<overlay>
|
||||
<template id="etemplate.test.contact_widget" template="" lang="" group="0" version="">
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<contact-fields id="fields"/>
|
||||
</row>
|
||||
<row>
|
||||
<contact-account label="Benutzer" id="account:" options="n_fn"/>
|
||||
</row>
|
||||
<row>
|
||||
<contact-account label="Telefon" id="account:" options="tel_work"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</template>
|
||||
</overlay>
|
Loading…
Reference in New Issue
Block a user