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 #)

This commit is contained in:
Ralf Becker 2012-01-29 22:34:43 +00:00
parent f293f8edb8
commit fb16ff842d
6 changed files with 171 additions and 78 deletions

View File

@ -355,7 +355,7 @@ abstract class bo_tracking
} }
foreach($changed_fields as $name) 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)); //error_log(__METHOD__.__LINE__." Name $name,".' Status:'.array2string($status));
if (is_array($status)) // 1:N relation --> remove common rows 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"; //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)); //error_log(__METHOD__."() changed_fields=".array2string($changed_fields));
return $changed_fields; return $changed_fields;
} }

View File

@ -46,30 +46,24 @@ class historylog_widget
// 'historylog-helper' => '', // 'historylog-helper' => '',
); );
/**
* pre-processing of the history logging extension
*
* @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,etemplate $tmpl)
{
static $status_widgets; static $status_widgets;
if ($cell['type'] == 'historylog-helper') /**
* 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)
{ {
if (empty($value) && (string)$value !== '0') if (empty($value) && (string)$value !== '0')
{ {
$cell = etemplate::empty_cell(); $cell = etemplate::empty_cell();
return true; return true;
} }
//echo $value.'/'.$cell['size']; _debug_array($status_widgets); //echo $value.'/'.$cell['size']; _debug_array(self::$status_widgets);
$type = isset($status_widgets[$cell['size']]) ? $status_widgets[$cell['size']] : 'label'; $type = isset(self::$status_widgets[$cell['size']]) ? self::$status_widgets[$cell['size']] : 'label';
$options = ''; $options = '';
if (!is_array($type) && strpos($type,':') !== false) if (!is_array($type) && strpos($type,':') !== false)
{ {
@ -83,7 +77,17 @@ class historylog_widget
$options = implode(',',array($rows,$type1,$type2,$type3,$type4,$type5,$type6)); $options = implode(',',array($rows,$type1,$type2,$type3,$type4,$type5,$type6));
} }
$cell = etemplate::empty_cell($type,$cell['name'],array('readonly' => true,'size' => $options)); $cell = etemplate::empty_cell($type,$cell['name'],array('readonly' => true,'size' => $options));
if (is_array($type)) // display unsupported iCal properties, which have multiple values or attributes
if ($type === 'label' && $value[1] === ':' && ($v = unserialize($value)))
{
$values = $v['values'];
foreach((array)$v['params'] as $name => $val)
{
$values[] = $name.': '.$val;
}
$value = implode("\n", $values);
}
elseif (is_array($type))
{ {
list($t) = explode(':',$type[0]); list($t) = explode(':',$type[0]);
if (isset($type[0]) && // numeric indexed array --> multiple values of 1:N releation if (isset($type[0]) && // numeric indexed array --> multiple values of 1:N releation
@ -123,10 +127,30 @@ class historylog_widget
$value = egw_time::server2user($value); $value = egw_time::server2user($value);
} }
if ($cell['type'] == 'label') $cell['no_lang'] = 'true'; if ($cell['type'] == 'label') $cell['no_lang'] = 'true';
return true; return true;
} }
/**
* pre-processing of the history logging extension
*
* @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,etemplate $tmpl)
{
switch ($cell['type'])
{
case 'historylog-helper':
return $this->pre_process_helper($value, $cell);
}
$app = is_array($value) ? $value['app'] : $GLOBALS['egw_info']['flags']['currentapp']; $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; $id = is_array($value) ? $value['id'] : $value;
$filter = is_array($value) ? $value['filter'] : array(); $filter = is_array($value) ? $value['filter'] : array();
@ -168,24 +192,34 @@ class historylog_widget
{ {
$tmpl->sel_options[$status]['#'.$cf_name] = lang($cf_data['label']); $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']) 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']['@']) 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'])) 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 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,'date-time','','${row}[user_ts]',array('readonly' => true));
$tpl->new_cell(2,'select-account','','${row}[owner]',array('readonly' => true)); $tpl->new_cell(2,'select-account','','${row}[owner]',array('readonly' => true));
@ -201,7 +235,7 @@ class historylog_widget
} }
// if $value[status-widgets] is set, use them together with the historylog-helper // 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 // 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}[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)); $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 $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 $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 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; $values = $values_in;
//echo "boinfolog::write()values="; _debug_array($values); //echo "boinfolog::write()values="; _debug_array($values);
@ -902,7 +904,7 @@ class infolog_bo
//_debug_array($values); //_debug_array($values);
// error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($values)."\n",3,'/tmp/infolog'); // 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'])) 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, 'filter' => $task_filter,
'info_type' => explode(',', $infolog_types), 'info_type' => explode(',', $infolog_types),
); );
error_log(__METHOD__."('$path', $user) returning ".array2string($ret)); //error_log(__METHOD__."('$path', $user) returning ".array2string($ret));
return $ret; return $ret;
} }

View File

@ -327,6 +327,24 @@ class infolog_ical extends infolog_bo
} }
$vevent->setAttribute('PRIORITY', $priority); $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 ($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); $vcal->addComponent($vevent);
$retval = $vcal->exportvCalendar(); $retval = $vcal->exportvCalendar();
@ -461,7 +479,7 @@ class infolog_ical extends infolog_bo
{ {
$taskData['caldav_name'] = $caldav_name; $taskData['caldav_name'] = $caldav_name;
} }
return $this->write($taskData, true, true, false); return $this->write($taskData, true, true, false, false, false, 'ical');
} }
/** /**
@ -612,6 +630,7 @@ class infolog_ical extends infolog_bo
$taskData['info_startdate'] = $component->getAttribute('DTSTART'); $taskData['info_startdate'] = $component->getAttribute('DTSTART');
} }
$attribute['value'] += $taskData['info_startdate']; $attribute['value'] += $taskData['info_startdate'];
$taskData['##DURATION'] = $attribute['value'];
// fall throught // fall throught
case 'DUE': case 'DUE':
// even as EGroupware only displays the date, we can still store the full value // even as EGroupware only displays the date, we can still store the full value
@ -647,6 +666,8 @@ class infolog_ical extends infolog_bo
} }
break; break;
case 'X-INFOLOG-STATUS':
break;
case 'STATUS': case 'STATUS':
// check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user) // 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'], $taskData['info_status'] = $this->vtodo2status($attribute['value'],
@ -679,6 +700,28 @@ class infolog_ical extends infolog_bo
case 'PERCENT-COMPLETE': case 'PERCENT-COMPLETE':
$taskData['info_percent'] = (int) $attribute['value']; $taskData['info_percent'] = (int) $attribute['value'];
break; 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; break;

View File

@ -529,9 +529,10 @@ class infolog_so
* *
* @param array $values with the data of the log-entry * @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 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 * @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'])) 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); //echo "<p>soinfolog.write values= "; _debug_array($values);
// write customfields now // 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(); $to_delete = array();
foreach($values as $key => $val) foreach($values as $key => $val)
{ {
@ -627,7 +634,7 @@ class infolog_so
$to_delete[] = substr($key,1); $to_delete[] = substr($key,1);
} }
} }
if ($to_delete) if ($to_delete && !$purge_cfs)
{ {
$this->db->delete($this->extra_table,array( $this->db->delete($this->extra_table,array(
'info_id' => $info_id, 'info_id' => $info_id,