* InfoLog/CalDAV: store unsupported iCal properties like custom fields incl. history logging, thought they are not displayed unless you explicitly add a custom field from them (prefixed with one #)

r37832: fixed fatal error calling widgetExists on a non-object
r37837: storing unsupported (valarm-)components like unsupported (X-)properties
r37854: need to load custom fields now allways, as x-props and unsupported components are stored there
This commit is contained in:
Ralf Becker 2012-02-10 10:39:27 +00:00
parent fe98471d18
commit 3dc0e31fa9
6 changed files with 200 additions and 78 deletions

View File

@ -355,7 +355,7 @@ abstract class bo_tracking
}
foreach($changed_fields as $name)
{
$status = $this->field2history[$name];
$status = isset($this->field2history[$name]) ? $this->field2history[$name] : $name;
//error_log(__METHOD__.__LINE__." Name $name,".' Status:'.array2string($status));
if (is_array($status)) // 1:N relation --> remove common rows
{
@ -423,6 +423,13 @@ abstract class bo_tracking
//echo "<p>$name: ".array2string($data[$name]).' != '.array2string($old[$name])."</p>\n";
}
}
foreach($data as $name => $value)
{
if ($name[0] == '#' && $name[1] == '#' && $value !== $old[$name])
{
$changed_fields[] = $name;
}
}
//error_log(__METHOD__."() changed_fields=".array2string($changed_fields));
return $changed_fields;
}

View File

@ -46,6 +46,94 @@ class historylog_widget
// 'historylog-helper' => '',
);
static $status_widgets;
/**
* pre-processing of the historylog-helper
*
* @param mixed &$value value / existing content, can be modified
* @param array &$cell array with the widget, can be modified for ui-independent widgets
* @return boolean true if extra label is allowed, false otherwise
*/
private function pre_process_helper(&$value, &$cell, etemplate $tmpl)
{
if (empty($value) && (string)$value !== '0')
{
$cell = etemplate::empty_cell();
return true;
}
//echo $value.'/'.$cell['size']; _debug_array(self::$status_widgets);
$type = isset(self::$status_widgets[$cell['size']]) ? self::$status_widgets[$cell['size']] : 'label';
$options = '';
if (!is_array($type) && strpos($type,':') !== false)
{
list($type,$options) = explode(':',$type,2);
}
// For all select-cats, show missing entries as IDs
if($type == 'select-cat')
{
list($rows,$type1,$type2,$type3,$type4,$type5,$type6) = explode(',',$options);
$type6 = 2;
$options = implode(',',array($rows,$type1,$type2,$type3,$type4,$type5,$type6));
}
$cell = etemplate::empty_cell($type,$cell['name'],array('readonly' => true,'size' => $options));
// display unsupported iCal properties, which have multiple values or attributes, or multiple components
if ($type === 'label' && $value[1] === ':' && ($values = unserialize($value)))
{
if (isset($values['values']))
{
foreach((array)$values['params'] as $name => $val)
{
$values['values'][] = $name.': '.$val;
}
$values = $values['values'];
}
$value = implode("\n", $values);
}
elseif (is_array($type))
{
list($t) = explode(':',$type[0]);
if (isset($type[0]) && // numeric indexed array --> multiple values of 1:N releation
$tmpl->widgetExists($t))
{
$cell['type'] = 'vbox';
$cell['size'] = '0,,0,0';
$value = explode(bo_tracking::ONE2N_SEPERATOR,$value);
foreach($type as $n => $t)
{
$opt = '';
if(is_array($t))
{
$sel_options = $t;
$t = 'select';
}
else
{
list($t,$opt) = explode(':',$t);
}
$child = etemplate::empty_cell($t,$cell['name']."[$n]",array('readonly' => true,'no_lang' => true,'size' => $opt));
$child['sel_options'] = $sel_options;
etemplate::add_child($cell,$child);
unset($sel_options);
unset($child);
}
}
else
{
$cell['sel_options'] = $cell['type'];
$cell['type'] = 'select';
}
}
// For all times, show time in user time
elseif ($type == 'date-time' && $value)
{
$value = egw_time::server2user($value);
}
if ($cell['type'] == 'label') $cell['no_lang'] = 'true';
return true;
}
/**
* pre-processing of the history logging extension
*
@ -59,74 +147,13 @@ class historylog_widget
*/
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,etemplate $tmpl)
{
static $status_widgets;
if ($cell['type'] == 'historylog-helper')
switch ($cell['type'])
{
if (empty($value) && (string)$value !== '0')
{
$cell = etemplate::empty_cell();
return true;
}
//echo $value.'/'.$cell['size']; _debug_array($status_widgets);
$type = isset($status_widgets[$cell['size']]) ? $status_widgets[$cell['size']] : 'label';
$options = '';
if (!is_array($type) && strpos($type,':') !== false)
{
list($type,$options) = explode(':',$type,2);
}
// For all select-cats, show missing entries as IDs
if($type == 'select-cat')
{
list($rows,$type1,$type2,$type3,$type4,$type5,$type6) = explode(',',$options);
$type6 = 2;
$options = implode(',',array($rows,$type1,$type2,$type3,$type4,$type5,$type6));
}
$cell = etemplate::empty_cell($type,$cell['name'],array('readonly' => true,'size' => $options));
if (is_array($type))
{
list($t) = explode(':',$type[0]);
if (isset($type[0]) && // numeric indexed array --> multiple values of 1:N releation
$tmpl->widgetExists($t))
{
$cell['type'] = 'vbox';
$cell['size'] = '0,,0,0';
$value = explode(bo_tracking::ONE2N_SEPERATOR,$value);
foreach($type as $n => $t)
{
$opt = '';
if(is_array($t))
{
$sel_options = $t;
$t = 'select';
}
else
{
list($t,$opt) = explode(':',$t);
}
$child = etemplate::empty_cell($t,$cell['name']."[$n]",array('readonly' => true,'no_lang' => true,'size' => $opt));
$child['sel_options'] = $sel_options;
etemplate::add_child($cell,$child);
unset($sel_options);
unset($child);
}
}
else
{
$cell['sel_options'] = $cell['type'];
$cell['type'] = 'select';
}
}
// For all times, show time in user time
elseif ($type == 'date-time' && $value)
{
$value = egw_time::server2user($value);
}
if ($cell['type'] == 'label') $cell['no_lang'] = 'true';
return true;
case 'historylog-helper':
return $this->pre_process_helper($value, $cell, $tmpl);
}
$app = is_array($value) ? $value['app'] : $GLOBALS['egw_info']['flags']['currentapp'];
$status_widgets = is_array($value) && isset($value['status-widgets']) ? $value['status-widgets'] : null;
self::$status_widgets = is_array($value) && isset($value['status-widgets']) ? $value['status-widgets'] : null;
$id = is_array($value) ? $value['id'] : $value;
$filter = is_array($value) ? $value['filter'] : array();
@ -168,24 +195,34 @@ class historylog_widget
{
$tmpl->sel_options[$status]['#'.$cf_name] = lang($cf_data['label']);
}
if (isset($status_widgets['#'.$cf_name])) continue; // app set a status widget --> use that
if (isset(self::$status_widgets['#'.$cf_name])) continue; // app set a status widget --> use that
if(!is_array($cf_data['values']) || !$cf_data['values'])
{
$status_widgets['#'.$cf_name] = $cf_data['type'] != 'text' ? $cf_data['type'] : 'label';
self::$status_widgets['#'.$cf_name] = $cf_data['type'] != 'text' ? $cf_data['type'] : 'label';
}
elseif($cf_data['values']['@'])
{
$status_widgets['#'.$cf_name] = customfields_widget::_get_options_from_file($cf_data['values']['@']);
self::$status_widgets['#'.$cf_name] = customfields_widget::_get_options_from_file($cf_data['values']['@']);
}
elseif(count($cf_data['values']))
{
$status_widgets['#'.$cf_name] = $cf_data['values'];
self::$status_widgets['#'.$cf_name] = $cf_data['values'];
}
}
if ($value) // autorepeated data-row only if there is data
{
// add "labels" for unsupported iCal properties, we just remove the '##' prefix
foreach($value as &$row)
{
if ($row['status'][0] == '#' && $row['status'][1] == '#' &&
isset($tmpl->sel_options[$status]) && !isset($tmpl->sel_options[$status][$row['status']]))
{
$tmpl->sel_options[$status][$row['status']] = substr($row['status'], 2);
}
}
$tpl->new_cell(2,'date-time','','${row}[user_ts]',array('readonly' => true));
$tpl->new_cell(2,'select-account','','${row}[owner]',array('readonly' => true));
@ -201,7 +238,7 @@ class historylog_widget
}
// if $value[status-widgets] is set, use them together with the historylog-helper
// to display new_ & old_value in the specified widget, otherwise use a label
if ($status_widgets)
if (self::$status_widgets)
{
$tpl->new_cell(2,'historylog-helper','','${row}[new_value]',array('size' => '$row_cont[status]','no_lang' => true,'readonly' => true));
$tpl->new_cell(2,'historylog-helper','','${row}[old_value]',array('size' => '$row_cont[status]','no_lang' => true,'readonly' => true));

View File

@ -694,10 +694,12 @@ class infolog_bo
* @param boolean $user2server=true conversion between user- and server-time necessary
* @param boolean $skip_notification=false true = do NOT send notification, false (default) = send notifications
* @param boolean $throw_exception=false Throw an exception (if required fields are not set)
* @param string $purge_cfs=null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs
*
* @return int/boolean info_id on a successfull write or false
* @return int|boolean info_id on a successfull write or false
*/
function write(&$values_in, $check_defaults=true, $touch_modified=true, $user2server=true, $skip_notification=false, $throw_exception=false)
function write(&$values_in, $check_defaults=true, $touch_modified=true, $user2server=true,
$skip_notification=false, $throw_exception=false, $purge_cfs=null)
{
$values = $values_in;
//echo "boinfolog::write()values="; _debug_array($values);
@ -904,7 +906,7 @@ class infolog_bo
//_debug_array($values);
// error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($values)."\n",3,'/tmp/infolog');
if (($info_id = $this->so->write($to_write,$check_modified)))
if (($info_id = $this->so->write($to_write, $check_modified, $purge_cfs)))
{
if (!isset($values['info_type']) || $status_only || empty($values['caldav_url']))
{

View File

@ -126,7 +126,7 @@ class infolog_groupdav extends groupdav_handler
'filter' => $task_filter,
'info_type' => explode(',', $infolog_types),
);
error_log(__METHOD__."('$path', $user) returning ".array2string($ret));
//error_log(__METHOD__."('$path', $user) returning ".array2string($ret));
return $ret;
}
@ -208,6 +208,7 @@ class infolog_groupdav extends groupdav_handler
'filter' => $task_filter,
'date_format' => 'server',
'col_filter' => $filter,
'custom_fields' => true, // otherwise custom fields get NOT loaded!
);
if (!$calendar_data)

View File

@ -327,6 +327,34 @@ class infolog_ical extends infolog_bo
}
$vevent->setAttribute('PRIORITY', $priority);
// for CalDAV add all X-Properties previously parsed
if ($this->productManufacturer == 'groupdav')
{
foreach($taskData as $name => $value)
{
if (substr($name, 0, 2) == '##')
{
if ($name[2] == ':')
{
if ($value[1] == ':' && ($v = unserialize($value)) !== false) $value = $v;
foreach((array)$value as $compvData)
{
$comp = Horde_iCalendar::newComponent(substr($name,3), $vevent);
$comp->parsevCalendar($compvData,substr($name,3),'utf-8');
$vevent->addComponent($comp);
}
}
elseif ($value[1] == ':' && ($attr = unserialize($value)) !== false)
{
$vevent->setAttribute(substr($name, 2), $attr['value'], $attr['params'], true, $attr['values']);
}
else
{
$vevent->setAttribute(substr($name, 2), $value);
}
}
}
}
$vcal->addComponent($vevent);
$retval = $vcal->exportvCalendar();
@ -461,7 +489,7 @@ class infolog_ical extends infolog_bo
{
$taskData['caldav_name'] = $caldav_name;
}
return $this->write($taskData, true, true, false);
return $this->write($taskData, true, true, false, false, false, 'ical');
}
/**
@ -612,6 +640,7 @@ class infolog_ical extends infolog_bo
$taskData['info_startdate'] = $component->getAttribute('DTSTART');
}
$attribute['value'] += $taskData['info_startdate'];
$taskData['##DURATION'] = $attribute['value'];
// fall throught
case 'DUE':
// even as EGroupware only displays the date, we can still store the full value
@ -647,6 +676,8 @@ class infolog_ical extends infolog_bo
}
break;
case 'X-INFOLOG-STATUS':
break;
case 'STATUS':
// check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user)
$taskData['info_status'] = $this->vtodo2status($attribute['value'],
@ -679,10 +710,47 @@ class infolog_ical extends infolog_bo
case 'PERCENT-COMPLETE':
$taskData['info_percent'] = (int) $attribute['value'];
break;
// ignore all PROPS, we dont want to store like X-properties or unsupported props
case 'DTSTAMP':
case 'SEQUENCE':
case 'CREATED':
case 'LAST-MODIFIED':
//case 'ATTENDEE': // todo: add real support for it
break;
default: // X- attribute or other by EGroupware unsupported property
error_log(__METHOD__."() $attribute[name] = ".array2string($attribute));
// for attributes with multiple values in multiple lines, merge the values
if (isset($taskData['##'.$attribute['name']]))
{
error_log(__METHOD__."() taskData['##$attribute[name]'] = ".array2string($taskData['##'.$attribute['name']]));
$attribute['values'] = array_merge(
is_array($taskData['##'.$attribute['name']]) ? $taskData['##'.$attribute['name']]['values'] : (array)$taskData['##'.$attribute['name']],
$attribute['values']);
}
$taskData['##'.$attribute['name']] = $attribute['params'] || count($attribute['values']) > 1 ?
serialize($attribute) : $attribute['value'];
break;
}
}
break;
}
// store included, but unsupported components like valarm as x-properties
foreach($component->getComponents() as $comp)
{
$name = '##:'.strtoupper($comp->getType());
$compvData = $comp->exportvCalendar($comp,'utf-8');
if (isset($taskData[$name]))
{
$taskData[$name] = array($taskData[$name]);
$taskData[$name][] = $compvData;
}
else
{
$taskData[$name] = $compvData;
}
}
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_taskID)\n" .

View File

@ -529,9 +529,10 @@ class infolog_so
*
* @param array $values with the data of the log-entry
* @param int $check_modified=0 old modification date to check before update (include in WHERE)
* @param string $purge_cfs=null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs
* @return int|boolean info_id, false on error or 0 if the entry has been updated in the meantime
*/
function write($values,$check_modified=0) // did _not_ ensure ACL
function write($values, $check_modified=0, $purge_cfs=null) // did _not_ ensure ACL
{
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
@ -604,6 +605,12 @@ class infolog_so
//echo "<p>soinfolog.write values= "; _debug_array($values);
// write customfields now
if ($purge_cfs)
{
$where = array('info_id' => $info_id);
if ($purge_cfs == 'ical') $where[] = "info_extra_name LIKE '#%'";
$this->db->delete($this->extra_table,$where,__LINE__,__FILE__);
}
$to_delete = array();
foreach($values as $key => $val)
{
@ -627,7 +634,7 @@ class infolog_so
$to_delete[] = substr($key,1);
}
}
if ($to_delete)
if ($to_delete && !$purge_cfs)
{
$this->db->delete($this->extra_table,array(
'info_id' => $info_id,