reworked and generalised Klaus new custom field header feature, to use it in the addressbook too, easy to addapt for the other apps now, only requires the get_rows/search method to understand filter and order_by with cfs

This commit is contained in:
Ralf Becker 2008-01-19 05:36:20 +00:00
parent 1d54ae791c
commit ced6adf23f
7 changed files with 258 additions and 212 deletions

View File

@ -12,6 +12,12 @@
/**
* This widget generates a template for customfields based on definitions in egw_config table
*
* All widgets here have 2 comma-separated options ($cell[size]):
* - sub-type to display only the cf's without subtype or with a matching one
* - use-private to display only (non-)private cf's (0=regular ones, 1=private ones, default both)
*
* Private cf's the user has no right to see (neither him nor his memberships are mentioned) are never displayed.
*
* @package etemplate
* @subpackage extensions
* @author RalfBecker-At-outdoor-training.de
@ -28,7 +34,6 @@
'customfields-types' => 'custom field types',
'customfields-list' => 'custom field list',
'customfields-no-label' => 'custom fields without label',
'customfields-header' => 'Header for custom fields',
);
/**
@ -82,15 +87,9 @@
function customfields_widget($ui,$appname=null)
{
$this->appname = $appname ? $appname : $GLOBALS['egw_info']['flags']['currentapp'];
$this->config =& CreateObject('phpgwapi.config',$this->appname);
$this->config->appname = $this->appname;
$config = $this->config->read_repository();
//merge old config_name in egw_config table
$config_name = isset($config['customfields']) ? 'customfields' : 'custom_fields';
$this->customfields = $config[$config_name];
$this->types = $config['types'];
$this->customfields = config::get_customfields($this->appname);
$this->types = config::get_content_types($this->appname);
$this->advanced_search = $GLOBALS['egw_info']['etemplate']['advanced_search'];
}
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
@ -100,10 +99,19 @@
list($app) = explode('.',$tmpl->name);
if ($app && $app != $this->appname) $this->customfields_widget(null,$app);
}
$type2 = $cell['size'];
list($type2,$use_private) = explode(',',$cell['size']);
$fields_with_vals=array();
$fields = $this->customfields;
// remove private or non-private cf's, if only one kind should be displayed
foreach((array)$fields as $key => $field)
{
if ((string)$use_private !== '' && (boolean)$field['private'] != (boolean)$use_private)
{
unset($fields[$key]);
}
}
// check if name refers to a single custom field --> show only that
if (($pos=strpos($cell['name'],$this->prefix)) !== false && // allow the prefixed name to be an array index too
preg_match("/$this->prefix([^\]]+)/",$cell['name'],$matches) && isset($fields[$name=$matches[1]]))
@ -138,8 +146,6 @@
//$stop_at_field = $name;
}
break;
case 'customfields-header':
return $this->pre_process_cf_header($name,$cell,$fields);
default:
foreach(array_reverse($fields) as $lname => $field)
{
@ -182,7 +188,7 @@
} elseif ($type == 'customfields-list') {
if (isset($value[$this->prefix.$lname]) && $value[$this->prefix.$lname] !== '') {
etemplate::add_child($cell,$input =& etemplate::empty_cell('image','info.png',
array('label'=>lang("custom fields").": ".$field['label'],'width'=>"16px",
array('label'=>/*lang("custom fields").": ".*/$field['label'],'width'=>"16px",
'onclick'=>"return alert('".lang("custom fields").": ".$field['label']."');")));
}
}
@ -394,59 +400,4 @@
return $options;
}
/**
* pre_process_cf_header
* function to display the customfields in a nextmatch table-header with the functionality of sorting and selecting
* by customfields. Of cource you need to adapt the source of your get_rows or search functionality to do the
* actual sorting and selecting
* You can pass the allowed/wanted fields to the header by passing an array of the wanted fields to the widget
* through the options parameter (see the eTemplate editor for fields/cells). This array is passed on through
* $cell['size']. By now the array passed through is only working, if it is the only entry in the optionsparameter.
* The allowed fields array assumes an numerical indexed array of (an) array(s) with ['name'] tag(s) set.
* The name provided assumes a preceding #. (e.g.: $allowed_fields[x]['name']='#MyCustomField')
* @param string $name -> the name of the particular field/cell object of that etemplate
* @param array $cell -> values passed from the the cell definition of the particular field/cell object of that etemplate
* @param array $fields -> the customfields of the current app
* @return false -> no extra label
*/
function pre_process_cf_header($name,&$cell,$fields)
{
$allowed_fields = $cell[size] ? (is_array($cell[size])?$cell[size]:explode(',',$cell[size])):false;
#_debug_array($allowed_fields);
$afs='';
if (is_array($allowed_fields)) {
foreach ($allowed_fields as $lidx => $afa)
{
$afs.=$afa['name'].",";
}
}
$cell['type'] = 'vbox';
$cell['name'] = '';
$cell['size'] = '0,,0,0';
foreach($fields as $lname => $field)
{
if (stripos($afs,"#".$lname)!==FALSE)
{
if($field['type'] == 'select')
{
$header =& etemplate::empty_cell('nextmatch-filterheader',$this->prefix.$lname,array(
'sel_options' => $field['values'],
'size' => $field['label'],
'no_lang' => True,
));
}
else
{
$header =& etemplate::empty_cell('nextmatch-sortheader',$this->prefix.$lname,array(
'label' => $field['label'],
));
}
etemplate::add_child($cell,$header);
unset($header);
}
}
return false; // no extra label
}
}

View File

@ -63,6 +63,12 @@
*/
class nextmatch_widget
{
/**
* Prefix for custom field names
*
*/
const CF_PREFIX = '#';
/**
* exported methods of this class
* @var array
@ -82,7 +88,9 @@
'nextmatch-accountfilter' => 'Nextmatch Accountfilter',
'nextmatch-customfilter' => 'Nextmatch Custom Filterheader',
'nextmatch-header' => 'Nextmatch Header',
'nextmatch-customfields' => 'Nextmatch Custom Fields Header',
);
/**
* Turn on debug messages (mostly in post_process)
*
@ -90,6 +98,15 @@
*/
var $debug = false;
/**
* Vars used to comunicated for the custom field header
*
* @var unknown_type
*/
private $selectcols;
public $cf_header;
private $cfs;
/**
* Constructor of the extension
*
@ -124,7 +141,7 @@
* @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)
function pre_process($name,&$value,array &$cell,&$readonlys,&$extension_data,etemplate &$tmpl)
{
$nm_global = &$GLOBALS['egw_info']['etemplate']['nextmatch'];
//echo "<p>nextmatch_widget.pre_process(name='$name',type='$cell[type]'): value = "; _debug_array($value);
@ -189,6 +206,9 @@
$cell['span'] = implode(',',$parts);
$extension_data['old_value'] = $value = $nm_global['col_filter'][$this->last_part($name)];
return True;
case 'nextmatch-customfields':
return $this->_pre_process_cf_header($cell,$tmpl);
}
// presetting the selectboxes with their default values, to NOT loop, because post-process thinks they changed
if (!isset($value['cat_id'])) $value['cat_id'] = '';
@ -367,16 +387,19 @@
$cell['name'] = $nextmatch->name;
}
// preset everything for the column selection
if (!$value['no_columnselection'] && // fetching column-names & -labels from the template
$this->_cols_from_tpl($value['template'],$value['options-selectcols'],$name2col,$value['rows']))
if (!$value['no_columnselection'])
{
$name = is_object($value['template']) ? $value['template']->name : $value['template'];
list($app) = explode('.',$name);
if (isset($value['columnselection_pref'])) $name = $value['columnselection_pref'];
$this->selectcols = $value['selectcols'] = $GLOBALS['egw_info']['user']['preferences'][$app]['nextmatch-'.$name];
// fetching column-names & -labels from the template
if($this->_cols_from_tpl($value['template'],$value['options-selectcols'],$name2col,$value['rows'],$value['selectcols']))
{
//_debug_array($name2col);
//_debug_array($value['options-selectcols']);
// getting the selected colums from the prefs (or if not set a given default or all)
$name = is_object($value['template']) ? $value['template']->name : $value['template'];
list($app) = explode('.',$name);
if (isset($value['columnselection_pref'])) $name = $value['columnselection_pref'];
if (!($value['selectcols'] = $GLOBALS['egw_info']['user']['preferences'][$app]['nextmatch-'.$name]))
if (!$value['selectcols'])
{
$value['selectcols'] = array_keys($value['options-selectcols']);
if (isset($value['default_cols']))
@ -390,6 +413,7 @@
$value['selectcols'] = $value['default_cols'];
}
}
$this->selectcols = $value['selectcols'];
}
if (!is_array($value['selectcols'])) $value['selectcols'] = explode(',',$value['selectcols']);
foreach(array_keys($value['options-selectcols']) as $name)
@ -419,6 +443,7 @@
// should reset on each submit
unset($value['default_prefs']);
}
}
$cell['type'] = 'template';
$cell['label'] = $cell['help'] = '';
@ -431,6 +456,56 @@
return False; // NO extra Label
}
/**
* Preprocess for the custom fields header
*
* @param array &$cell
*/
private function _pre_process_cf_header(array &$cell,etemplate $tmpl)
{
//echo __CLASS__.'::'.__METHOD__."() selectcols=$this->selectcols\n";
if (is_null($this->cfs))
{
list($app) = explode('.',$tmpl->name);
$this->cfs = config::get_customfields($app);
}
$cell['type'] = 'vbox';
$cell['name'] = '';
$cell['size'] = '0,,0,0';
if ($this->selectcols)
{
foreach(explode(',',$this->selectcols) as $col)
{
if ($col[0] == self::CF_PREFIX) $allowed[] = $col;
}
}
foreach($this->cfs as $name => $field)
{
if (!$allowed || in_array(self::CF_PREFIX.$name,$allowed))
{
if($field['type'] == 'select')
{
$header =& etemplate::empty_cell('nextmatch-filterheader',self::CF_PREFIX.$name,array(
'sel_options' => $field['values'],
'size' => $field['label'],
'no_lang' => True,
));
}
else
{
$header =& etemplate::empty_cell('nextmatch-sortheader',self::CF_PREFIX.$name,array(
'label' => $field['label'],
));
}
etemplate::add_child($cell,$header);
unset($header);
}
}
return false; // no extra label
}
/**
* Extract the column names and labels from the template
*
@ -438,9 +513,10 @@
* @param array &$cols here we add the column-name/-label
* @param array &$name2col
* @param array $content nextmatch content, to be able to resolve labels with @name
* @param array $selectcols selected colums
* @return int columns found, count($cols)
*/
function _cols_from_tpl(&$tmpl,&$cols,&$name2col,&$content)
function _cols_from_tpl(etemplate $tmpl,&$cols,&$name2col,&$content,$selectcols)
{
//_debug_array($cols);
// fetching column-names & -labels from the template
@ -474,6 +550,25 @@
$name2col[$name] = $col;
}
$cols[$name] = $label;
// we are behind the column of a custom fields header --> add the individual fields
if ($name == $this->cf_header && (!$selectcols ||
in_array($this->cf_header,explode(',',$selectcols))))
{
$cols[$name] .= ':';
list($app) = explode('.',$tmpl->name);
if (($this->cfs = config::get_customfields($app)))
{
foreach($this->cfs as $name => $field)
{
$cols[self::CF_PREFIX.$name] = '- '.$field['label'];
}
}
else
{
unset($cols[$name]); // no cf's defined -> no header
}
}
}
//_debug_array($cols);
return count($cols);
@ -489,6 +584,13 @@
function _cols_from_tpl_walker(&$widget,&$cols,$path)
{
list($type,$subtype) = explode('-',$widget['type']);
if ($subtype == 'customfields')
{
if (!$widget['name']) $widget['name'] = 'customfields';
if (!$widget['label']) $widget['label'] = 'Custom fields';
$this->cf_header = $widget['name'];
}
if ($type != 'nextmatch' || !$subtype || !$widget['name'] || $widget['disabled'])
{
return;

View File

@ -78,12 +78,6 @@ class boinfolog
* @var array
*/
var $timestamps = array('info_startdate','info_enddate','info_datemodified','info_datecompleted');
/**
* instance of the config class for infolog
*
* @var config
*/
var $config;
/**
* fields the responsible user can change
*
@ -188,37 +182,34 @@ class boinfolog
}
$this->link =& $GLOBALS['egw']->link;
$this->config =& CreateObject('phpgwapi.config','infolog');
$this->config->read_repository();
if ($this->config->config_data)
if (($config_data = config::read('infolog')))
{
$this->link_pathes = $this->config->config_data['link_pathes'];
$this->send_file_ips = $this->config->config_data['send_file_ips'];
$this->link_pathes = $config_data['link_pathes'];
$this->send_file_ips = $config_data['send_file_ips'];
if (isset($this->config->config_data['status']) && is_array($this->config->config_data['status']))
if (isset($config_data['status']) && is_array($config_data['status']))
{
foreach($this->config->config_data['status'] as $key => $data)
foreach($config_data['status'] as $key => $data)
{
if (!is_array($this->status[$key]))
{
$this->status[$key] = array();
}
$this->status[$key] = array_merge($this->status[$key],$this->config->config_data['status'][$key]);
$this->status[$key] = array_merge($this->status[$key],$config_data['status'][$key]);
}
}
if (isset($this->config->config_data['types']) && is_array($this->config->config_data['types']))
if (isset($config_data['types']) && is_array($config_data['types']))
{
//echo "stock-types:<pre>"; print_r($this->enums['type']); echo "</pre>\n";
//echo "config-types:<pre>"; print_r($this->config->config_data['types']); echo "</pre>\n";
$this->enums['type'] += $this->config->config_data['types'];
//echo "config-types:<pre>"; print_r($config_data['types']); echo "</pre>\n";
$this->enums['type'] += $config_data['types'];
//echo "types:<pre>"; print_r($this->enums['type']); echo "</pre>\n";
}
if ($this->config->config_data['group_owners']) $this->group_owners = $this->config->config_data['group_owners'];
if ($config_data['group_owners']) $this->group_owners = $config_data['group_owners'];
if (isset($this->config->config_data['customfields']) && is_array($this->config->config_data['customfields']))
$this->customfields = config::get_customfields('infolog');
if ($this->customfields)
{
if (!($this->customfields = $this->config->config_data['customfields'])) $this->customfields = array();
foreach($this->customfields as $name => $field)
{
// old infolog customefield record
@ -230,21 +221,21 @@ class boinfolog
else $field['type'] = 'label'; // header-row
$field['type2'] = $field['typ'];
unset($field['typ']);
$this->customfields[$name] = $this->config->config_data['customfields'][$name] = $field;
$this->customfields[$name] = $field;
$save_config = true;
}
}
if ($save_config) $this->config->save_repository();
if ($save_config) config::save_value('customfields',$this->customfields,'infolog');
}
if (is_array($this->config->config_data['responsible_edit']))
if (is_array($config_data['responsible_edit']))
{
$this->responsible_edit = array_merge($this->responsible_edit,$this->config->config_data['responsible_edit']);
$this->responsible_edit = array_merge($this->responsible_edit,$config_data['responsible_edit']);
}
if ($this->config->config_data['implicit_rights'] == 'edit')
if ($config_data['implicit_rights'] == 'edit')
{
$this->implicit_rights = 'edit';
}
$this->history = $this->config->config_data['history'];
$this->history = $config_data['history'];
}
// sort types by there translation
foreach($this->enums['type'] as $key => $val)

View File

@ -669,16 +669,20 @@ class soinfolog // DB-Layer
}
}
$sortbycf='';
if (!empty($query['order']) && (eregi('^[a-z_0-9, ]+$',$query['order']) || stripos($query['order'],'#')!==FALSE ) && (empty($query['sort']) || eregi('^(DESC|ASC)$',$query['sort'])))
if (!empty($query['order']) && (eregi('^[a-z_0-9, ]+$',$query['order']) || stripos($query['order'],'#')!==FALSE ) &&
(empty($query['sort']) || eregi('^(DESC|ASC)$',$query['sort'])))
{
$order = array();
foreach(explode(',',$query['order']) as $val)
{
$val = trim($val);
if (substr($val,0,1)=='#') {
$sortbycf=substr($val,1);
$val="cfsortcrit";
} else {
if ($val[0] == '#')
{
$sortbycf = substr($val,1);
$val = "cfsortcrit";
}
else
{
$val = (substr($val,0,5) != 'info_' ? 'info_' : '').$val;
if ($val == 'info_des' && $this->db->capabilities['order_on_text'] !== true)
{
@ -720,8 +724,13 @@ class soinfolog // DB-Layer
$filtermethod .= ' AND '.$this->db->expression($this->info_table,array($col => $data));
}
}
if (substr($col,0,1)=='#' && $query['custom_fields'] && $data) {
$filtermethod.=" and main.info_id in (select distinct info_id from $this->extra_table"." where (info_extra_name='".substr($col,1)."' and info_extra_value='".$data."')) ";
if ($col[0] == '#' && $query['custom_fields'] && $data)
{
$filtermethod .= " AND main.info_id IN (SELECT DISTINCT info_id FROM $this->extra_table WHERE ".
$this->db->expression($this->extra_table,array(
'info_extra_name' => substr($col,1),
'info_extra_value' => $data,
)).')';
$cfcolfilter++;
}
}
@ -751,9 +760,9 @@ class soinfolog // DB-Layer
$sql_query = 'AND ('.(is_numeric($query['search']) ? 'main.info_id='.(int)$query['search'].' OR ' : '').
implode(" LIKE $pattern OR ",$columns)." LIKE $pattern) ";
}
if ($query['search'] || $query['custom_fields'] )
if ($query['search'] || $query['custom_fields'])
{
$join = ($cfcolfilter>0 ? '':"LEFT")." JOIN $this->extra_table ON main.info_id=$this->extra_table.info_id ";
$join = ($cfcolfilter>0 ? '':'LEFT')." JOIN $this->extra_table ON main.info_id=$this->extra_table.info_id ";
// mssql and others cant use DISTICT if text columns (info_des) are involved
$distinct = $this->db->capabilities['distinct_on_text'] ? 'DISTINCT' : '';
}
@ -778,11 +787,12 @@ class soinfolog // DB-Layer
}
if ($this->db->capabilities['sub_queries'])
{
$count_subs = ",(SELECT count(*) FROM $this->info_table sub WHERE sub.info_id_parent=main.info_id AND $acl_filter) AS info_anz_subs";
$count_subs = ",(SELECT COUNT(*) FROM $this->info_table sub WHERE sub.info_id_parent=main.info_id AND $acl_filter) AS info_anz_subs";
}
$info_customfield = "";
if ($sortbycf!='') {
$info_customfield = ", (select distinct info_extra_value from $this->extra_table sub2 where sub2.info_id=main.info_id and info_extra_name='".$sortbycf."') AS cfsortcrit ";
$info_customfield = '';
if ($sortbycf != '')
{
$info_customfield = ", (SELECT DISTINCT info_extra_value FROM $this->extra_table sub2 where sub2.info_id=main.info_id AND info_extra_name=".$this->db->quote($sortbycf).") AS cfsortcrit ";
}
//echo "SELECT $distinct main.* $count_subs $info_customfield $sql_query $ordermethod"."<br>";
$this->db->query($sql="SELECT $distinct main.* $count_subs $info_customfield $sql_query $ordermethod",__LINE__,__FILE__,

View File

@ -137,9 +137,8 @@ class uiinfolog
// read the duration format from project-manager
if ($GLOBALS['egw_info']['apps']['projectmanager'])
{
$pm_config =& CreateObject('phpgwapi.config','projectmanager');
$pm_config->read_repository();
$this->duration_format = str_replace(',','',$pm_config->config_data['duration_units']).','.$pm_config->config_data['hours_per_workday'];
$pm_config = config::read('projectmanager');
$this->duration_format = str_replace(',','',$pm_config['duration_units']).','.$pm_config['hours_per_workday'];
unset($pm_config);
}
/* these are just for testing of the notifications
@ -344,63 +343,16 @@ class uiinfolog
//_debug_array($columselection);
if ($columselection)
{
$query['selectcols']=$columselection;
$query['selectcols'] = $columselection;
$columselection = explode(',',$columselection);
} else {
if (isset($query['selectcols']))
}
else
{
$columselection =explode(',',$query['selectcols']);
} else {
$columselection=array();
}
}
$show_custom_fields = (!$columselection || in_array('customfields',$columselection)) && $this->bo->customfields;
$lv_customfields=array(); // used to set the visible columns
$showallcustfields=0; // control the switching on and off, of the customfields
if ((stripos($query['selectcols'],'#')===FALSE && stripos($query['selectcols'],'customfields')!==FALSE) ) $showallcustfields=1;
if ( $show_custom_fields || $query['custom_fields'] )
{
if ($query['col_filter']['info_type'])
{
foreach ($this->bo->customfields as $cf=>$cfa)
{
if (isset($cfa['type2'])&& trim($cfa['type2'])!=='') // type specific fields
{
if ((stripos($cfa['type2'], $query['col_filter']['info_type'] )) !== FALSE &&
(in_array('#'.$cf,$columselection)||$showallcustfields==1))
{
$lv_customfields[$cf]=$cfa;
$lv_customfields[$cf]['name']='#'.$cf;
$readonlys['#'.$cf] = true;
}
} else {
if ($showallcustfields==1 || in_array('#'.$cf,$columselection)) {
$lv_customfields[$cf]=$cfa;
$lv_customfields[$cf]['name']='#'.$cf;
$readonlys['#'.$cf] = true;
}
}
// set the array for the available cust-cols
$query['options-selectcols']['#'.$cf]=$cfa['label'];
}
} else {
// set the columns to be available for selections
$cvp=array();
foreach($this->bo->customfields as $name => $value)
{
if ($showallcustfields==1 || in_array('#'.$name,$columselection)) {
$lv_customfields[$name]=$value;
$lv_customfields[$name]['name']='#'.$name;
//echo $name."->". $value['label']."<br>";
$readonlys['#'.$name] = true;
}
//set the array for the available cust-cols
$cvp['#'.$name] = $value['label'];
}
$query['options-selectcols']=$cvp;
}
$query['custom_fields'] =true;
$columselection = $query['selectcols'] ? explode(',',$query['selectcols']) : array();
}
// do we need to query the cf's
$query['custom_fields'] = $this->bo->customfields && (!$columselection || in_array('customfields',$columselection));
$ids = $this->bo->search($query);
if (!is_array($ids))
{
@ -440,10 +392,10 @@ class uiinfolog
if ($query['no_actions']) $rows['no_actions'] = true;
$rows['no_timesheet'] = !isset($GLOBALS['egw_info']['user']['apps']['timesheet']);
$rows['duration_format'] = ','.$this->duration_format.',,1';
if ( $show_custom_fields || $query['custom_fields'] )
{
$rows['customfields'] = array_values($lv_customfields);
}
// switch cf column off, if we have no cf's
if (!$query['custom_fields']) $rows['no_customfields'] = true;
if ($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'none' &&
!isset($GLOBALS['egw_info']['user']['apps']['admin']))
{

File diff suppressed because one or more lines are too long

View File

@ -13,11 +13,12 @@
<button image="note" label="Note" id="add[note]" statustext="Add a new Note" onclick="window.open(egw::link('/index.php','menuaction=infolog.uiinfolog.edit&amp;type=note&amp;action=$cont[action]&amp;action_id=$cont[action_id]&amp;cat_id=$cont[cat_id]'),'_blank','dependent=yes,width=750,height=600,scrollbars=yes,status=yes'); return false;"/>
</hbox>
</template>
<template id="infolog.index.rows-noheader" template="" lang="" group="0" version="1.3.003">
<template id="infolog.index.rows-noheader" template="" lang="" group="0" version="1.4.002">
<grid>
<columns>
<column width="2%"/>
<column/>
<column disabled="@no_customfields"/>
<column width="120"/>
<column disabled="@no_info_used_time_info_planned_time"/>
<column width="8%" disabled="@no_info_onwer_info_responsible"/>
@ -36,6 +37,19 @@
<description value="Subject"/>
<description value="Description"/>
</vbox>
<vbox options="0,0">
<description id="customfields" options="Custom fields"/>
<grid spacing="0" padding="0">
<columns>
<column disabled="@no_customfields"/>
</columns>
<rows>
<row>
<description no_lang="1" id="customfields[$row][label]" options="Custom fields"/>
</row>
</rows>
</grid>
</vbox>
<vbox options="0,0">
<description value="Startdate"/>
<description value="Enddate"/>
@ -69,6 +83,7 @@
<description options=",,1" no_lang="1" id="${row}[info_des]"/>
<link-string id="${row}[filelinks]"/>
</vbox>
<customfields-list class="customfields" id="$row" readonly="true"/>
<vbox options="0,0,1" rows="3" cols="1">
<date-time options=",8" id="${row}[info_startdate]" readonly="true" class="fixedHeight"/>
<date class="$row_cont[end_class] fixedHeight" id="${row}[info_enddate]" readonly="true"/>
@ -106,11 +121,12 @@
</rows>
</grid>
</template>
<template id="infolog.index.rows" template="" lang="" group="0" version="1.3.002">
<template id="infolog.index.rows" template="" lang="" group="0" version="1.5.001">
<grid>
<columns>
<column width="2%"/>
<column/>
<column disabled="@no_customfields"/>
<column/>
<column width="120"/>
<column/>
@ -146,6 +162,7 @@
</row>
</rows>
</grid>
<nextmatch-customfields id="customfields"/>
<nextmatch-header label="Category" id="cat_id"/>
<vbox options="0,0">
<nextmatch-sortheader label="Startdate" id="info_startdate"/>
@ -180,6 +197,7 @@
<description no_lang="1" id="${row}[info_des]" options=",,1"/>
<link-string id="${row}[filelinks]"/>
</vbox>
<customfields-list class="customfields" id="$row"/>
<menulist>
<menupopup type="select-cat" id="${row}[info_cat]" readonly="true"/>
</menulist>