diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php
index f497e46caa..de4f9e931c 100644
--- a/infolog/inc/class.infolog_bo.inc.php
+++ b/infolog/inc/class.infolog_bo.inc.php
@@ -30,16 +30,27 @@ class infolog_bo
var $vfs_basedir='/infolog';
var $link_pathes = array();
var $send_file_ips = array();
-
- var $tz_offset = 0;
+ /**
+ * Set Logging
+ *
+ * @var boolean
+ */
+ var $log = false;
/**
* offset in secconds between user and server-time,
* it need to be add to a server-time to get the user-time or substracted from a user-time to get the server-time
*
* @var int
*/
+ var $tz_offset = 0;
var $tz_offset_s = 0;
+ /**
+ * current time as timestamp in user-time and server-time
+ *
+ * @var int
+ */
var $user_time_now;
+ var $now;
/**
* name of timestamps in an InfoLog entry
*
@@ -235,7 +246,8 @@ class infolog_bo
$this->tz_offset = $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'];
$this->tz_offset_s = 60*60*$this->tz_offset;
- $this->user_time_now = time() + $this->tz_offset_s;
+ $this->now = time();
+ $this->user_time_now = $this->now + $this->tz_offset_s;
$this->grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true);
$this->so = new infolog_so($this->grants);
@@ -411,9 +423,12 @@ class infolog_bo
* @param int/array $info_id integer id or array with key 'info_id' of the entry to read
* @param boolean $run_link_id2from=true should link_id2from run, default yes,
* need to be set to false if called from link-title to prevent an infinit recursion
+ * @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time,
+ * 'array'=array or string with date-format
+ *
* @return array/boolean infolog entry, null if not found or false if no permission to read it
*/
- function &read($info_id,$run_link_id2from=true)
+ function &read($info_id,$run_link_id2from=true,$date_format='ts')
{
if (is_array($info_id))
{
@@ -437,13 +452,32 @@ class infolog_bo
}
if ($run_link_id2from) $this->link_id2from($data);
- // convert system- to user-time
- foreach($this->timestamps as $time)
+ if ($data['info_enddate'])
{
- if ($data[$time]) $data[$time] += $this->tz_offset_s;
+ // Set due date to 00:00
+ $data['info_enddate'] = mktime(0, 0, 0,
+ date('m', $data['info_enddate']),
+ date('d', $data['info_enddate']),
+ date('Y', $data['info_enddate']));
+ }
+
+ // convert server- to user-time
+ if ($date_format == 'ts' && $this->tz_offset_s)
+ {
+ foreach ($this->timestamps as $time)
+ {
+ // we keep dates the same in user-time
+ if ($data[$time] && date('Hi', $data[$time]) != '0000')
+ {
+ $data[$time] += $this->tz_offset_s;
+ }
+ }
+ }
+ if ($date_format == 'ts' || !$this->tz_offset_s)
+ {
+ // pre-cache title and file access
+ self::set_link_cache($data);
}
- // pre-cache title and file access
- self::set_link_cache($data);
return $data;
}
@@ -488,7 +522,7 @@ class infolog_bo
}
}
}
- if (!($info = $this->read($info_id))) return false; // should not happen
+ if (!($info = $this->read($info_id, true, 'server'))) return false; // should not happen
$deleted = $info;
$deleted['info_status'] = 'deleted';
@@ -532,9 +566,11 @@ class infolog_bo
* @param array &$values values to write
* @param boolean $check_defaults=true check and set certain defaults
* @param boolean $touch_modified=true touch the modification data and sets the modiefier's user-id
+ * @param boolean $user2server=true conversion between user- and server-time necessary
+ *
* @return int/boolean info_id on a successfull write or false
*/
- function write(&$values, $check_defaults=True, $touch_modified=True)
+ function write(&$values, $check_defaults=true, $touch_modified=true, $user2server=true)
{
//echo "boinfolog::write()values="; _debug_array($values);
if ($status_only = $values['info_id'] && !$this->check_access($values['info_id'],EGW_ACL_EDIT))
@@ -572,13 +608,13 @@ class infolog_bo
'info_id' => $values['info_id'],
'info_datemodified' => $values['info_datemodified'],
);
- foreach($this->responsible_edit as $name)
+ foreach ($this->responsible_edit as $name)
{
if (isset($backup_values[$name])) $values[$name] = $backup_values[$name];
}
if ($set_completed)
{
- $values['info_datecompleted'] = $this->user_time_now;
+ $values['info_datecompleted'] = $user2server ? $this->user_time_now : $this->now;
$values['info_percent'] = 100;
$forcestatus = true;
$status = 'done';
@@ -605,7 +641,7 @@ class infolog_bo
if (!$values['info_datecompleted'] &&
(in_array($values['info_status'],array('done','billed')) || (int)$values['info_percent'] == 100))
{
- $values['info_datecompleted'] = $this->user_time_now; // set date completed to today if status == done
+ $values['info_datecompleted'] = $user2server ? $this->user_time_now : $this->now; // set date completed to today if status == done
}
if (in_array($values['info_status'],array('done','billed')))
{
@@ -651,43 +687,78 @@ class infolog_bo
{
$values['info_owner'] = $this->so->user;
}
+
if ($info_from_set = ($values['info_link_id'] && isset($values['info_from']) && empty($values['info_from'])))
{
$values['info_from'] = $this->link_id2from($values);
}
+
+ if ($status_only && !$undelete) $values = array_merge($backup_values,$values);
+
+
+ if (isset($values['info_enddate']) && $values['info_enddate'])
+ {
+ // Set due date to 00:00
+ $values['info_enddate'] = mktime(0, 0, 0,
+ date('m', $values['info_enddate']),
+ date('d', $values['info_enddate']),
+ date('Y', $values['info_enddate']));
+ }
+
+ $to_write = $values;
+ if ($user2server)
+ {
+ // convert user- to server-time
+ foreach ($this->timestamps as $time)
+ {
+ if ($to_write[$time] && date('Hi', $to_write[$time]) != '0000')
+ {
+ $to_write[$time] -= $this->tz_offset_s;
+ }
+ }
+ }
+ else
+ {
+ // convert server- to user-time
+ foreach ($this->timestamps as $time)
+ {
+ if ($values[$time] && date('Hi', $values[$time]) != '0000')
+ {
+ $values[$time] += $this->tz_offset_s;
+ }
+ }
+ }
+
if ($touch_modified || !$values['info_datemodified'])
{
// Should only an entry be updated which includes the original modification date?
// Used in the web-GUI to check against a modification by an other user while editing the entry.
// It's now disabled for xmlrpc, as otherwise the xmlrpc code need to be changed!
$xmlrpc = is_object($GLOBALS['server']) && $GLOBALS['server']->last_method;
- $check_modified = $values['info_datemodified'] && !$xmlrpc ? $values['info_datemodified']-$this->tz_offset_s : false;
+ $check_modified = $values['info_datemodified'] && !$xmlrpc ? $to_write['info_datemodified'] : false;
$values['info_datemodified'] = $this->user_time_now;
+ $to_write['info_datemodified'] = $this->now;
}
if ($touch_modified || !$values['info_modifier'])
{
$values['info_modifier'] = $this->so->user;
+ $to_write['info_modifier'] = $this->so->user;
}
//_debug_array($values);
- $to_write = $values;
- if ($status_only && !$undelete) $values = array_merge($backup_values,$values);
- // convert user- to system-time
- foreach($this->timestamps as $time)
- {
- if ($to_write[$time]) $to_write[$time] -= $this->tz_offset_s;
- }
+ // error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($values)."\n",3,'/tmp/infolog');
+
// we need to get the old values to update the links in customfields and for the tracking
if ($values['info_id'])
{
- $old = $this->read($values['info_id'],false);
+ $old = $this->read($values['info_id'], false, 'server');
}
- if(($info_id = $this->so->write($to_write,$check_modified)))
+ if (($info_id = $this->so->write($to_write,$check_modified)))
{
if (!isset($values['info_type']) || $status_only)
{
- $values = $this->read($info_id);
+ $values = $this->read($info_id, true, 'server');
}
- if($values['info_id'] && $old['info_status'] != 'deleted')
+ if ($values['info_id'] && $old['info_status'] != 'deleted')
{
// update
$GLOBALS['egw']->contenthistory->updateTimeStamp(
@@ -704,11 +775,13 @@ class infolog_bo
);
}
$values['info_id'] = $info_id;
+ $to_write['info_id'] = $info_id;
// if the info responbsible array is not passed, fetch it from old.
if (!array_key_exists('info_responsible',$values)) $values['info_responsible'] = $old['info_responsible'];
if (!is_array($values['info_responsible'])) // this should not happen, bug it does ;-)
{
$values['info_responsible'] = $values['info_responsible'] ? explode(',',$values['info_responsible']) : array();
+ $to_write['info_responsible'] = $values['info_responsible'];
}
// create (and remove) links in custom fields
customfields_widget::update_customfield_links('infolog',$values,$old,'info_id');
@@ -725,11 +798,12 @@ class infolog_bo
$this->tracking = new infolog_tracking($this);
}
- if (($missing_fields = array_diff_key($old,$values)))
+ if ($old && ($missing_fields = array_diff_key($old,$values)))
{
$values = array_merge($values,$missing_fields);
+ $to_write = array_merge($to_write,$missing_fields);
}
- $this->tracking->track($values,$old,$this->user,$values['info_status'] == 'deleted' || $old['info_status'] == 'deleted');
+ $this->tracking->track($to_write,$old,$this->user,$values['info_status'] == 'deleted' || $old['info_status'] == 'deleted');
}
if ($info_from_set) $values['info_from'] = '';
@@ -763,18 +837,43 @@ class infolog_bo
function &search(&$query)
{
//echo "
boinfolog::search(".print_r($query,True).")
\n";
+ if (!empty($query['start']))
+ {
+ $query['start'] -= $this->tz_offset_s;
+ }
+
$ret = $this->so->search($query);
- // convert system- to user-time
+
if (is_array($ret))
{
- foreach($ret as $id => &$data)
+ foreach ($ret as $id => &$data)
{
- if($this->tz_offset_s)
+ if (!$this->check_access($data,EGW_ACL_READ))
{
- foreach($this->timestamps as $time)
+ unset($ret[$id]);
+ continue;
+ }
+
+ if ($data['info_enddate'])
+ {
+ // Set due date to 00:00
+ $data['info_enddate'] = mktime(0, 0, 0,
+ date('m', $data['info_enddate']),
+ date('d', $data['info_enddate']),
+ date('Y', $data['info_enddate']));
+ }
+
+ if ($this->tz_offset_s)
+ {
+ // convert system- to user-time
+ foreach ($this->timestamps as $time)
{
- if ($data[$time]) $data[$time] += $this->tz_offset_s;
+ // we keep dates the same in user-time
+ if ($data[$time] && date('Hi', $data[$time]) != '0000')
+ {
+ $data[$time] += $this->tz_offset_s;
+ }
}
}
// pre-cache title and file access
@@ -882,7 +981,7 @@ class infolog_bo
* @param int/array $info int info_id or array with infolog entry
* @return string/boolean string with the title, null if $info not found, false if no perms to view
*/
- function link_title( $info )
+ function link_title($info)
{
if (!is_array($info))
{
@@ -901,16 +1000,16 @@ class infolog_bo
*
* @param array $ids
*/
- function link_titles( array $ids )
+ function link_titles(array $ids)
{
$titles = array();
- foreach($this->search($params=array(
+ foreach ($this->search($params=array(
'col_filter' => array('info_id' => $ids),
)) as $info)
{
$titles[$info['info_id']] = $this->link_title($info);
}
- foreach(array_diff($ids,array_keys($titles)) as $id)
+ foreach (array_diff($ids,array_keys($titles)) as $id)
{
$titles[$id] = false; // we assume every not returned entry to be not readable, as we notify the link class about all deletes
}
@@ -925,7 +1024,7 @@ class infolog_bo
* @param string $pattern pattern to search
* @return array with info_id - title pairs of the matching entries
*/
- function link_query( $pattern )
+ function link_query($pattern)
{
$query = array(
'search' => $pattern,
@@ -936,7 +1035,7 @@ class infolog_bo
$content = array();
if (is_array($ids))
{
- foreach($ids as $id => $info )
+ foreach ($ids as $id => $info )
{
$content[$id] = $this->link_title($id);
}
@@ -1006,7 +1105,7 @@ class infolog_bo
}
while ($infos = $this->search($query))
{
- foreach($infos as $info)
+ foreach ($infos as $info)
{
$time = (int) adodb_date('Hi',$info['info_startdate']);
$date = adodb_date('Y/m/d',$info['info_startdate']);
@@ -1021,7 +1120,7 @@ class infolog_bo
$info['info_subject'];
$view = egw_link::view('infolog',$info['info_id']);
$content=array();
- foreach($icons = array(
+ foreach ($icons = array(
$info['info_type'] => 'infolog',
$this->status[$info['info_type']][$info['info_status']] => 'infolog',
) as $name => $app)
@@ -1088,18 +1187,17 @@ class infolog_bo
{
$this->categories = new categories($this->user,'infolog');
}
-
- if($info_id && $info_id > 0)
+ $old_cats_preserve = array();
+ if ($info_id && $info_id > 0)
{
// preserve categories without users read access
$old_infolog = $this->read($info_id);
$old_categories = explode(',',$old_infolog['info_cat']);
- $old_cats_preserve = array();
- if(is_array($old_categories) && count($old_categories) > 0)
+ if (is_array($old_categories) && count($old_categories) > 0)
{
foreach($old_categories as $cat_id)
{
- if(!$this->categories->check_perms(EGW_ACL_READ, $cat_id))
+ if ($cat_id && !$this->categories->check_perms(EGW_ACL_READ, $cat_id))
{
$old_cats_preserve[] = $cat_id;
}
@@ -1108,7 +1206,7 @@ class infolog_bo
}
$cat_id_list = array();
- foreach($catname_list as $cat_name)
+ foreach ((array)$catname_list as $cat_name)
{
$cat_name = trim($cat_name);
$cat_id = $this->categories->name2id($cat_name, 'X-');
@@ -1129,7 +1227,7 @@ class infolog_bo
}
}
- if(is_array($old_cats_preserve) && count($old_cats_preserve) > 0)
+ if (count($old_cats_preserve) > 0)
{
$cat_id_list = array_merge($old_cats_preserve, $cat_id_list);
}
@@ -1230,19 +1328,19 @@ class infolog_bo
{
case 'notify_due_responsible':
$info['message'] = lang('%1 you are responsible for is due at %2',$this->enums['type'][$info['info_type']],
- $this->tracking->datetime($info['info_enddate']-$this->tz_offset_s,false));
+ $this->tracking->datetime($info['info_enddate'],false));
break;
case 'notify_due_delegated':
$info['message'] = lang('%1 you delegated is due at %2',$this->enums['type'][$info['info_type']],
- $this->tracking->datetime($info['info_enddate']-$this->tz_offset_s,false));
+ $this->tracking->datetime($info['info_enddate'],false));
break;
case 'notify_start_responsible':
$info['message'] = lang('%1 you are responsible for is starting at %2',$this->enums['type'][$info['info_type']],
- $this->tracking->datetime($info['info_startdate']-$this->tz_offset_s,null));
+ $this->tracking->datetime($info['info_startdate'],null));
break;
case 'notify_start_delegated':
$info['message'] = lang('%1 you delegated is starting at %2',$this->enums['type'][$info['info_type']],
- $this->tracking->datetime($info['info_startdate']-$this->tz_offset_s,null));
+ $this->tracking->datetime($info['info_startdate'],null));
break;
}
//error_log("notifiying $user($email) about $info[info_subject]: $info[message]");
@@ -1359,109 +1457,319 @@ class infolog_bo
/**
* Try to find a matching db entry
+ * This expects timestamps to be in server-time.
*
- * @param array $egwData the vTODO data we try to find
+ * @param array $infoData the infolog data we try to find
* @param boolean $relax=false if asked to relax, we only match against some key fields
- * @return the infolog_id of the matching entry or false (if none matches)
+ * @param boolean $user2server=true conversion between user- and server-time necessary
+ *
+ * @return array of infolog_ids of matching entries
*/
- function findVTODO($egwData, $relax=false)
+ function findInfo($infoData, $relax=false, $user2server=true)
{
- if (!empty($egwData['info_uid']))
- {
- $filter = array('col_filter' => array('info_uid' => $egwData['info_uid']));
- if (($found = $this->search($filter))
- && ($uidmatch = array_shift($found)))
- {
- return $uidmatch['info_id'];
- }
- }
- unset($egwData['info_uid']);
-
+ $foundInfoLogs = array();
$filter = array();
- $description = '';
- if (!empty($egwData['info_des'])) {
- $description = trim(preg_replace("/\r?\n?\\[[A-Z_]+:.*\\]/i", '', $egwData['info_des']));
- unset($egwData['info_des']);
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '('. ($relax ? 'RELAX, ': 'EXACT, ')
+ . ($user2server ? 'USERTIME': 'SERVERTIME'). ')[InfoData]:'
+ . array2string($infoData));
+ }
+
+ if ($infoData['info_id']
+ && ($egwData = $this->read($infoData['info_id'], true, 'server')))
+ {
+ // we only do a simple consistency check
+ if (!$relax || strpos($egwData['info_subject'], $infoData['info_subject']) === 0)
+ {
+ return array($egwData['info_id']);
+ }
+ if (!$relax) return array();
+ }
+ unset($infoData['info_id']);
+
+ if (!$relax && !empty($infoData['info_uid']))
+ {
+ $filter = array('col_filter' => array('info_uid' => $infoData['info_uid']));
+ foreach($this->so->search($filter) as $egwData)
+ {
+ if (!$this->check_access($egwData,EGW_ACL_READ)) continue;
+ $foundInfoLogs[$egwData['info_id']] = $egwData['info_id'];
+ }
+ return $foundInfoLogs;
+ }
+ unset($infoData['info_uid']);
+
+ if (empty($infoData['info_des']))
+ {
+ $description = false;
+ }
+ else
+ {
+ // ignore meta information appendices
+ $description = trim(preg_replace('/\s*\[[A-Z_]+:.*\].*/im', '', $infoData['info_des']));
+ $text = trim(preg_replace('/\s*\[[A-Z_]+:.*\]/im', '', $infoData['info_des']));
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . "()[description]: $description");
+ }
// Avoid quotation problems
- $description = preg_replace("/[^\x20-\x7F].*/", '', $description);
- if (strlen($description)) {
- $filter['search'] = $description;
- }
- }
-
- if ($egwData['info_id']
- && ($found = $this->read($egwData['info_id'])))
- {
- // We only do a simple consistency check
- if ($found['info_subject'] == $egwData['info_subject']
- && strpos($found['info_des'], $description) === 0)
+ if (preg_match_all('/[\x20-\x7F]*/m', $text, $matches, PREG_SET_ORDER))
{
- return $found['info_id'];
+ $text = '';
+ foreach ($matches as $chunk)
+ {
+ if (strlen($text) < strlen($chunk[0]))
+ {
+ $text = $chunk[0];
+ }
+ }
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . "()[search]: $text");
+ }
+ $filter['search'] = $text;
}
}
- unset($egwData['info_id']);
+ // Set due date to 00:00
+ $infoData['info_enddate'] = mktime(0, 0, 0,
+ date('m', $infoData['info_enddate']),
+ date('d', $infoData['info_enddate']),
+ date('Y', $infoData['info_enddate']));
+
+ $filter['col_filter'] = $infoData;
+
+ if ($user2server)
+ {
+ // convert user- to server-time
+ foreach($this->timestamps as $time)
+ {
+ if (isset($infoData[$time]) &&
+ $infoData[$time] &&
+ date('Hi', $infoData[$time]) != '0000')
+ {
+ $filter['col_filter'][$time] -= $this->tz_offset_s;
+ }
+ }
+ }
+
+ $filter['col_filter'] = $infoData;
// priority does not need to match
- unset($egwData['info_priority']);
+ unset($filter['col_filter']['info_priority']);
+ // we ignore description and location first
+ unset($filter['col_filter']['info_des']);
+ unset($filter['col_filter']['info_location']);
- $filter['col_filter'] = $egwData;
+ foreach ($this->so->search($filter) as $itemID => $egwData)
+ {
+ if (!$this->check_access($egwData,EGW_ACL_READ)) continue;
- if($foundItems = $this->search($filter)) {
- if(count($foundItems) > 0) {
- $itemIDs = array_keys($foundItems);
- return $itemIDs[0];
+ switch ($infoData['info_type'])
+ {
+ case 'task':
+ if (!empty($egwData['info_location']))
+ {
+ $egwData['info_location'] = str_replace("\r\n", "\n", $egwData['info_location']);
+ }
+ if (!$relax &&
+ !empty($infoData['info_location']) && (empty($egwData['info_location'])
+ || strpos($egwData['info_location'], $infoData['info_location']) !== 0))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[location mismatch]: '
+ . $infoData['info_location'] . ' <> ' . $egwData['info_location']);
+ }
+ continue;
+ }
+ default:
+ if (!empty($egwData['info_des']))
+ {
+ $egwData['info_des'] = str_replace("\r\n", "\n", $egwData['info_des']);
+ }
+ if (!$relax && ($description && empty($egwData['info_des'])
+ || !empty($egwData['info_des']) && empty($infoData['info_des'])
+ || strpos($egwData['info_des'], $description) === false))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[description mismatch]: '
+ . $infoData['info_des'] . ' <> ' . $egwData['info_des']);
+ }
+ continue;
+ }
+ // no further criteria to match
+ $foundInfoLogs[$egwData['info_id']] = $egwData['info_id'];
}
}
- $filter = array();
-
- if (!$relax && strlen($description)) {
- $filter['search'] = $description;
+ if (!$relax && !empty($foundInfoLogs))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[FOUND]:' . array2string($foundInfoLogs));
+ }
+ return $foundInfoLogs;
}
- $filter['col_filter'] = $egwData;
+ if ($relax)
+ {
+ unset($filter['search']);
+ }
- // search for date only match
+ // search for matches by date only
unset($filter['col_filter']['info_startdate']);
+ unset($filter['col_filter']['info_enddate']);
unset($filter['col_filter']['info_datecompleted']);
+ // Some devices support lesser stati
+ unset($filter['col_filter']['info_status']);
// try tasks without category
unset($filter['col_filter']['info_cat']);
- #Horde::logMessage("findVTODO Filter\n"
- # . print_r($filter, true),
- # __FILE__, __LINE__, PEAR_LOG_DEBUG);
- foreach ($this->search($filter) as $itemID => $taskData) {
- # Horde::logMessage("findVTODO Trying\n"
- # . print_r($taskData, true),
- # __FILE__, __LINE__, PEAR_LOG_DEBUG);
- if (isset($egwData['info_cat'])
- && isset($taskData['info_cat']) && $taskData['info_cat']
- && $egwData['info_cat'] != $taskData['info_cat']) continue;
- if (isset($egwData['info_startdate'])
- && isset($taskData['info_startdate']) && $taskData['info_startdate']) {
- $parts = @getdate($taskData['info_startdate']);
- $startdate = @mktime(0, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
- if ($egwData['info_startdate'] != $startdate) continue;
+ // Horde::logMessage("findVTODO Filter\n"
+ // . print_r($filter, true),
+ // __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ foreach ($this->so->search($filter) as $itemID => $egwData)
+ {
+ if (!$this->check_access($egwData,EGW_ACL_READ)) continue;
+ // Horde::logMessage("findVTODO Trying\n"
+ // . print_r($egwData, true),
+ // __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ if (isset($infoData['info_cat'])
+ && isset($egwData['info_cat']) && $egwData['info_cat']
+ && $infoData['info_cat'] != $egwData['info_cat'])
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[category mismatch]: '
+ . $infoData['info_cat'] . ' <> ' . $egwData['info_cat']);
+ }
+ continue;
}
- // some clients don't support DTSTART
- if (isset($egwData['info_startdate'])
- && (!isset($taskData['info_startdate']) || !$taskData['info_startdate'])
- && !$relax) continue;
- if (isset($egwData['info_datecompleted'])
- && isset($taskData['info_datecompleted']) && $taskData['info_datecompleted']) {
- $parts = @getdate($taskData['info_datecompleted']);
- $enddate = @mktime(0, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
- if ($egwData['info_datecompleted'] != $enddate) continue;
+ if (isset($infoData['info_startdate']) && $infoData['info_startdate'])
+ {
+ // We got a startdate from client
+ if (isset($egwData['info_startdate']) && $egwData['info_startdate'])
+ {
+ // We compare the date only
+ if (date('Ymd', $infoData['info_startdate']) != date('Ymd', $egwData['info_startdate']))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[start mismatch]: '
+ . $taskTime->format('Ymd') . ' <> ' . $egwTime->format('Ymd'));
+ }
+ continue;
+ }
+ }
+ elseif (!$relax)
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[start mismatch]');
+ }
+ continue;
+ }
}
- if ((isset($egwData['info_datecompleted'])
- && (!isset($taskData['info_datecompleted']) || !$taskData['info_datecompleted'])) ||
- (!isset($egwData['info_datecompleted'])
- && isset($taskData['info_datecompleted']) && $taskData['info_datecompleted'])
- && !$relax) continue;
- return($itemID);
+ if ($infoData['info_type'] == 'task')
+ {
+ if (isset($infoData['info_status']) && isset($egwData['info_status'])
+ && $egwData['info_status'] == 'done'
+ && $infoData['info_status'] != 'done' ||
+ $egwData['info_status'] != 'done'
+ && $infoData['info_status'] == 'done')
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[status mismatch]: '
+ . $infoData['info_status'] . ' <> ' . $egwData['info_status']);
+ }
+ continue;
+ }
+ if (isset($infoData['info_enddate']) && $infoData['info_enddate'])
+ {
+ // We got a enddate from client
+ if (isset($egwData['info_enddate']) && $egwData['info_enddate'])
+ {
+ // We compare the date only
+ if (date('Ymd', $infoData['info_enddate']) != date('Ymd', $egwData['info_enddate']))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[DUE mismatch]: '
+ . $taskTime->format('Ymd') . ' <> ' . $egwTime->format('Ymd'));
+ }
+ continue;
+ }
+ }
+ elseif (!$relax)
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[DUE mismatch]');
+ }
+ continue;
+ }
+ }
+ if (isset($infoData['info_datecompleted']) && $infoData['info_datecompleted'])
+ {
+ // We got a completed date from client
+ if (isset($egwData['info_datecompleted']) && $egwData['info_datecompleted'])
+ {
+ // We compare the date only
+ if (date('Ymd', $infoData['info_datecompleted']) != date('Ymd', $egwData['info_datecompleted']))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[completed mismatch]: '
+ . $taskTime->format('Ymd') . ' <> ' . $egwTime->format('Ymd'));
+ }
+ continue;
+ }
+ }
+ elseif (!$relax)
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[completed mismatch]');
+ }
+ continue;
+ }
+ }
+ elseif (!$relax && isset($egwData['info_datecompleted']) && $egwData['info_datecompleted'])
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[completed mismatch]');
+ }
+ continue;
+ }
+ }
+ $foundInfoLogs[$itemID] = $itemID;
}
- return false;
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
+ . '()[FOUND]:' . array2string($foundInfoLogs));
+ }
+ return $foundInfoLogs;
}
}
diff --git a/infolog/inc/class.infolog_groupdav.inc.php b/infolog/inc/class.infolog_groupdav.inc.php
index 80bc82a109..528e7bbba2 100644
--- a/infolog/inc/class.infolog_groupdav.inc.php
+++ b/infolog/inc/class.infolog_groupdav.inc.php
@@ -7,7 +7,7 @@
* @package infolog
* @subpackage groupdav
* @author Ralf Becker
- * @copyright (c) 2007/8 by Ralf Becker
+ * @copyright (c) 2007-9 by Ralf Becker
* @version $Id$
*/
@@ -70,6 +70,8 @@ class infolog_groupdav extends groupdav_handler
*/
function propfind($path,$options,&$files,$user,$id='')
{
+ $starttime = microtime(true);
+
// todo add a filter to limit how far back entries from the past get synced
$filter = array(
'info_type' => 'task',
@@ -77,7 +79,7 @@ class infolog_groupdav extends groupdav_handler
if ($id) $filter['info_id'] = $id; // propfind on a single id
// ToDo: add parameter to only return id & etag
- if (($tasks = $this->bo->search($params=array(
+ if (($tasks =& $this->bo->search($params=array(
'order' => 'info_datemodified',
'sort' => 'DESC',
'filter' => 'own', // filter my: entries user is responsible for,
@@ -85,7 +87,7 @@ class infolog_groupdav extends groupdav_handler
'col_filter' => $filter,
))))
{
- foreach($tasks as $task)
+ foreach($tasks as &$task)
{
$files['files'][] = array(
'path' => self::get_path($task),
@@ -100,6 +102,7 @@ class infolog_groupdav extends groupdav_handler
);
}
}
+ if ($this->debug) error_log(__METHOD__."($path) took ".(microtime(true) - $starttime).' to return '.count($files['files']).' items');
return true;
}
@@ -178,7 +181,7 @@ class infolog_groupdav extends groupdav_handler
*/
function read($id)
{
- return $this->bo->read($id,false);
+ return $this->bo->read($id,false,'server');
}
/**
@@ -203,7 +206,7 @@ class infolog_groupdav extends groupdav_handler
{
if (!is_array($info))
{
- $info = $this->bo->read($info);
+ $info = $this->bo->read($info,true,'server');
}
if (!is_array($info) || !isset($info['info_id']) || !isset($info['info_datemodified']))
{
diff --git a/infolog/inc/class.infolog_hooks.inc.php b/infolog/inc/class.infolog_hooks.inc.php
index 3d3f1f0eaa..4485bf08dc 100644
--- a/infolog/inc/class.infolog_hooks.inc.php
+++ b/infolog/inc/class.infolog_hooks.inc.php
@@ -241,7 +241,7 @@ class infolog_hooks
),
'cat_add_default' => array(
'type' => 'select',
- 'label' => 'Default categorie for new Infolog entries',
+ 'label' => 'Default category for new Infolog entries',
'name' => 'cat_add_default',
'values' => self::all_cats(),
'help' => 'You can choose a categorie to be preselected, when you create a new Infolog entry',
diff --git a/infolog/inc/class.infolog_ical.inc.php b/infolog/inc/class.infolog_ical.inc.php
index 13151e01d2..be2486e5e7 100644
--- a/infolog/inc/class.infolog_ical.inc.php
+++ b/infolog/inc/class.infolog_ical.inc.php
@@ -20,23 +20,42 @@ require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
class infolog_ical extends infolog_bo
{
/**
- * @var array conversion of the priority egw => ical
+ * @var array $priority_egw2ical conversion of the priority egw => ical
*/
- var $egw_priority2vcal_priority = array(
- 0 => 9, // low
- 1 => 5, // normal
- 2 => 3, // high
- 3 => 1, // urgent
+ var $priority_egw2ical = array(
+ 0 => 9, // low
+ 1 => 5, // normal
+ 2 => 3, // high
+ 3 => 1, // urgent
);
/**
- * @var array conversion of the priority ical => egw
+ * @var array $priority_ical2egw conversion of the priority ical => egw
*/
- var $vcal_priority2egw_priority = array(
- 9 => 0, 8 => 0, 7 => 0, // low
- 6 => 1, 5 => 1, 4 => 1, 0 => 1, // normal
- 3 => 2, 2 => 2, // high
- 1 => 3, // urgent
+ var $priority_ical2egw = array(
+ 9 => 0, 8 => 0, 7 => 0, // low
+ 6 => 1, 5 => 1, 4 => 1, 0 => 1, // normal
+ 3 => 2, 2 => 2, // high
+ 1 => 3, // urgent
+ );
+
+ /**
+ * @var array $priority_egw2funambol conversion of the priority egw => funambol
+ */
+ var $priority_egw2funambol = array(
+ 0 => 0, // low
+ 1 => 1, // normal
+ 2 => 2, // high
+ 3 => 2, // urgent
+ );
+
+ /**
+ * @var array $priority_funambol2egw conversion of the priority funambol => egw
+ */
+ var $priority_funambol2egw = array(
+ 0 => 0, // low
+ 1 => 1, // normal
+ 2 => 3, // high
);
/**
@@ -54,6 +73,13 @@ class infolog_ical extends infolog_bo
*/
var $uidExtension = false;
+ /**
+ * user preference: use server timezone for exports to device
+ *
+ * @var boolean
+ */
+ var $useServerTZ = false;
+
/**
* Client CTCap Properties
*
@@ -61,6 +87,15 @@ class infolog_ical extends infolog_bo
*/
var $clientProperties;
+ /**
+ * Set Logging
+ *
+ * @var boolean
+ */
+ var $log = false;
+ var $logfile="/tmp/log-infolog-vcal";
+
+
/**
* Constructor
*
@@ -69,7 +104,7 @@ class infolog_ical extends infolog_bo
function __construct(&$_clientProperties = array())
{
parent::__construct();
-
+ if ($this->log) $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-infolog-vcal";
$this->clientProperties = $_clientProperties;
}
@@ -83,7 +118,15 @@ class infolog_ical extends infolog_bo
*/
function exportVTODO($_taskID, $_version='2.0',$_method='PUBLISH')
{
- $taskData = $this->read($_taskID);
+ if ($this->useServerTZ)
+ {
+ $date_format = 'server';
+ }
+ else
+ {
+ $date_format = 'ts';
+ }
+ if (!($taskData = $this->read($_taskID, true, $date_format))) return false;
if ($taskData['info_id_parent'])
{
@@ -116,10 +159,27 @@ class infolog_ical extends infolog_bo
$taskData = $GLOBALS['egw']->translation->convert($taskData,
$GLOBALS['egw']->translation->charset(), 'UTF-8');
+
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($taskData)."\n",3,$this->logfile);
+ }
+
$vcal = new Horde_iCalendar;
$vcal->setAttribute('VERSION',$_version);
$vcal->setAttribute('METHOD',$_method);
+ if ($this->useServerTZ)
+ {
+ $event = array('start' => $taskData['info_startdate'] ? $taskData['info_startdate'] : time());
+ $serverTZ = calendar_ical::generate_vtimezone($event, $vcal);
+ }
+ else
+ {
+ $serverTZ = false;
+ }
+
$vevent = Horde_iCalendar::newComponent('VTODO',$vcal);
if (!isset($this->clientProperties['SUMMARY']['Size']))
@@ -142,8 +202,14 @@ class infolog_ical extends infolog_bo
{
$size = $this->clientProperties[$field]['Size'];
$noTruncate = $this->clientProperties[$field]['NoTruncate'];
- #Horde::logMessage("VTODO $field Size: $size, NoTruncate: " .
- # ($noTruncate ? 'TRUE' : 'FALSE'), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ if ($this->log && $size > 0)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ .
+ "() $field Size: $size, NoTruncate: " .
+ ($noTruncate ? 'TRUE' : 'FALSE') . "\n",3,$this->logfile);
+ }
+ //Horde::logMessage("VTODO $field Size: $size, NoTruncate: " .
+ // ($noTruncate ? 'TRUE' : 'FALSE'), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
else
{
@@ -155,55 +221,79 @@ class infolog_ical extends infolog_bo
{
if ($noTruncate)
{
- Horde::logMessage("VTODO $field omitted due to maximum size $size",
- __FILE__, __LINE__, PEAR_LOG_WARNING);
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ .
+ "() $field omitted due to maximum size $size\n",3,$this->logfile);
+ }
+ //Horde::logMessage("VTODO $field omitted due to maximum size $size",
+ // __FILE__, __LINE__, PEAR_LOG_WARNING);
continue; // skip field
}
// truncate the value to size
$value = substr($value, 0, $size -1);
- #Horde::logMessage("VTODO $field truncated to maximum size $size",
- # __FILE__, __LINE__, PEAR_LOG_INFO);
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ .
+ "() $field truncated to maximum size $size\n",3,$this->logfile);
+ }
+ //Horde::logMessage("VTODO $field truncated to maximum size $size",
+ // __FILE__, __LINE__, PEAR_LOG_INFO);
}
if (empty($value) && ($size < 0 || $noTruncate)) continue;
if ($field == 'RELATED-TO')
{
- $options = array('RELTYPE' => 'PARENT');
+ $options = array('RELTYPE' => 'PARENT');
}
else
{
$options = array();
}
- /*if(preg_match('/([\000-\012\015\016\020-\037\075])/', $value)) {
- $options['ENCODING'] = 'QUOTED-PRINTABLE';
- }*/
- if ($this->productManufacturer != 'groupdav'
- && preg_match('/([\177-\377])/',$value))
+ if (preg_match('/[^\x20-\x7F]/', $value))
{
- $options['CHARSET'] = 'UTF-8';
+ $options['CHARSET'] = 'UTF-8';
+ switch ($this->productManufacturer)
+ {
+ case 'groupdav':
+ if ($this->productName == 'kde')
+ {
+ $options['ENCODING'] = 'QUOTED-PRINTABLE';
+ }
+ else
+ {
+ $options['CHARSET'] = '';
+
+ if (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
+ {
+ $options['ENCODING'] = 'QUOTED-PRINTABLE';
+ }
+ else
+ {
+ $options['ENCODING'] = '';
+ }
+ }
+ break;
+ case 'funambol':
+ $options['ENCODING'] = 'FUNAMBOL-QP';
+ }
}
$vevent->setAttribute($field, $value, $options);
}
- $dateOnly = false;
-
if ($taskData['info_startdate'])
{
- $dateOnly = self::setDateOrTime($vevent, 'DTSTART', $taskData['info_startdate']);
+ self::setDateOrTime($vevent, 'DTSTART', $taskData['info_startdate'], $serverTZ);
}
-
if ($taskData['info_enddate'])
{
- $parts = @getdate($taskData['info_enddate']);
- $value = @mktime(12, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
- self::setDateOrTime($vevent, 'DUE', $value, $dateOnly);
+ self::setDateOrTime($vevent, 'DUE', $taskData['info_enddate'], $serverTZ);
}
-
if ($taskData['info_datecompleted'])
{
- self::setDateOrTime($vevent, 'COMPLETED', $taskData['info_datecompleted']);
+ self::setDateOrTime($vevent, 'COMPLETED', $taskData['info_datecompleted'], $serverTZ);
}
$vevent->setAttribute('DTSTAMP',time());
@@ -214,16 +304,69 @@ class infolog_ical extends infolog_bo
// we try to preserv the original infolog status as X-INFOLOG-STATUS, so we can restore it, if the user does not modify STATUS
$vevent->setAttribute('X-INFOLOG-STATUS',$taskData['info_status']);
$vevent->setAttribute('PERCENT-COMPLETE',$taskData['info_percent']);
- $vevent->setAttribute('PRIORITY',$this->egw_priority2vcal_priority[$taskData['info_priority']]);
-
+ if ($this->productManufacturer == 'funambol' &&
+ (strpos($this->productName, 'outlook') !== false
+ || strpos($this->productName, 'pocket pc') !== false))
+ {
+ $priority = (int) $this->priority_egw2funambol[$taskData['info_priority']];
+ }
+ else
+ {
+ $priority = (int) $this->priority_egw2ical[$taskData['info_priority']];
+ }
+ $vevent->setAttribute('PRIORITY', $priority);
$vcal->addComponent($vevent);
$retval = $vcal->exportvCalendar();
- Horde::logMessage("exportVTODO:\n" . print_r($retval, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($retval)."\n",3,$this->logfile);
+ }
+ // Horde::logMessage("exportVTODO:\n" . print_r($retval, true),
+ // __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $retval;
}
+ /**
+ * set date-time attribute to DATE or DATE-TIME depending on value
+ * 00:00 uses DATE else DATE-TIME
+ *
+ * @param Horde_iCalendar_* $vevent
+ * @param string $attr attribute name
+ * @param int $time as timestamp
+ * @param string $tzid=false timezone to use for client, false for user-time
+ */
+ static function setDateOrTime(&$vevent, $attr, $time, $tzid=false)
+ {
+ $params = array();
+
+ // check for date --> export it as such
+ if (date('Hi', $time) == '0000')
+ {
+ $value = array(
+ 'year' => date('Y', $time),
+ 'month' => date('m', $time),
+ 'mday' => date('d', $time));
+ $params['VALUE'] = 'DATE';
+ }
+ else
+ {
+ if (!$tzid || $tzid == 'UTC')
+ {
+ $value = $time;
+ }
+ else
+ {
+ $value = date('Ymd\THis', $time);
+ $params['TZID'] = $tzid;
+ }
+
+ }
+ $vevent->setAttribute($attr, $value, $params);
+ }
+
/**
* Import a VTODO component of an iCal
*
@@ -234,7 +377,7 @@ class infolog_ical extends infolog_bo
*/
function importVTODO(&$_vcalData, $_taskID=-1, $merge=false)
{
- if (!$taskData = $this->vtodotoegw($_vcalData,$_taskID)) return false;
+ if (!($taskData = $this->vtodotoegw($_vcalData,$_taskID))) return false;
// we suppose that a not set status in a vtodo means that the task did not started yet
if (empty($taskData['info_status']))
@@ -247,7 +390,13 @@ class infolog_ical extends infolog_bo
$taskData['info_datecompleted'] = 0;
}
- return $this->write($taskData);
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($taskData)."\n",3,$this->logfile);
+ }
+
+ return $this->write($taskData, true, true, false);
}
/**
@@ -256,18 +405,22 @@ class infolog_ical extends infolog_bo
* @param string $_vcalData VTODO
* @param int $contentID=null infolog_id (or null, if unkown)
* @param boolean $relax=false if true, a weaker match algorithm is used
- * @return infolog_id of a matching entry or false, if nothing was found
+ *
+ * @return array of infolog_ids of matching entries
*/
- function searchVTODO($_vcalData, $contentID=null, $relax=false) {
- $result = false;
+ function searchVTODO($_vcalData, $contentID=null, $relax=false)
+ {
+ $result = array();
- if (($egwData = $this->vtodotoegw($_vcalData, $contentID)))
+ $taskData = $this->vtodotoegw($_vcalData,$contentID);
+
+ if ($taskData)
{
if ($contentID)
{
- $egwData['info_id'] = $contentID;
+ $taskData['info_id'] = $contentID;
}
- $result = $this->findVTODO($egwData, $relax);
+ $result = $this->findInfo($taskData, $relax, !$this->useServerTZ);
}
return $result;
}
@@ -281,8 +434,24 @@ class infolog_ical extends infolog_bo
*/
function vtodotoegw($_vcalData, $_taskID=-1)
{
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_taskID)\n" .
+ array2string($_vcalData)."\n",3,$this->logfile);
+ }
+
$vcal = new Horde_iCalendar;
- if (!($vcal->parsevCalendar($_vcalData))) return false;
+ if (!($vcal->parsevCalendar($_vcalData)))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
+ "(): No vCalendar Container found!\n",3,$this->logfile);
+ }
+ return false;
+ }
+
+ $version = $vcal->getAttribute('VERSION');
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
@@ -293,126 +462,146 @@ class infolog_ical extends infolog_bo
$minimum_uid_length = 8;
}
- $components = $vcal->getComponents();
+ $taskData = false;
- foreach ($components as $component)
+ foreach ($vcal->getComponents() as $component)
{
- if (is_a($component, 'Horde_iCalendar_vtodo'))
+ if (!is_a($component, 'Horde_iCalendar_vtodo'))
{
- $taskData = array();
- $taskData['info_type'] = 'task';
-
- if ($_taskID > 0)
+ if ($this->log)
{
- $taskData['info_id'] = $_taskID;
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
+ "(): Not a vTODO container, skipping...\n",3,$this->logfile);
}
- foreach ($component->_attributes as $attributes)
+ continue;
+ }
+
+ $taskData = array();
+ $taskData['info_type'] = 'task';
+
+ if ($_taskID > 0)
+ {
+ $taskData['info_id'] = $_taskID;
+ }
+ foreach ($component->_attributes as $attribute)
+ {
+ //$attribute['value'] = trim($attribute['value']);
+ if (!strlen($attribute['value'])) continue;
+
+ switch ($attribute['name'])
{
- //$attributes['value'] = trim($attributes['value']);
- if (empty($attributes['value'])) continue;
- switch ($attributes['name'])
- {
- case 'CLASS':
- $taskData['info_access'] = strtolower($attributes['value']);
- break;
+ case 'CLASS':
+ $taskData['info_access'] = strtolower($attribute['value']);
+ break;
- case 'DESCRIPTION':
- $value = $attributes['value'];
- if (preg_match('/\s*\[UID:(.+)?\]/Usm', $value, $matches))
+ case 'DESCRIPTION':
+ $value = str_replace("\r\n", "\n", $attribute['value']);
+ if (preg_match('/\s*\[UID:(.+)?\]/Usm', $value, $matches))
+ {
+ if (!isset($taskData['info_uid'])
+ && strlen($matches[1]) >= $minimum_uid_length)
{
- if (!isset($taskData['info_uid'])
- && strlen($matches[1]) >= $minimum_uid_length)
- {
- $taskData['info_uid'] = $matches[1];
- }
- //$value = str_replace($matches[0], '', $value);
+ $taskData['info_uid'] = $matches[1];
}
- if (preg_match('/\s*\[PARENT_UID:(.+)?\]/Usm', $value, $matches))
+ //$value = str_replace($matches[0], '', $value);
+ }
+ if (preg_match('/\s*\[PARENT_UID:(.+)?\]/Usm', $value, $matches))
+ {
+ if (!isset($taskData['info_id_parent'])
+ && strlen($matches[1]) >= $minimum_uid_length)
{
- if (!isset($taskData['info_id_parent'])
- && strlen($matches[1]) >= $minimum_uid_length)
- {
- $taskData['info_id_parent'] = $this->getParentID($matches[1]);
- }
- //$value = str_replace($matches[0], '', $value);
+ $taskData['info_id_parent'] = $this->getParentID($matches[1]);
}
- $taskData['info_des'] = $value;
- break;
+ //$value = str_replace($matches[0], '', $value);
+ }
+ $taskData['info_des'] = $value;
+ break;
- case 'LOCATION':
- $taskData['info_location'] = $attributes['value'];
- break;
+ case 'LOCATION':
+ $taskData['info_location'] = str_replace("\r\n", "\n", $attribute['value']);
+ break;
- case 'DUE':
- // eGroupWare uses date only
- $parts = @getdate($attributes['value']);
- $value = @mktime(0, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
- $taskData['info_enddate'] = $value;
- break;
+ case 'DUE':
+ // eGroupWare uses date only
+ $parts = @getdate($attribute['value']);
+ $value = @mktime(0, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
+ $taskData['info_enddate'] = $value;
+ break;
- case 'COMPLETED':
- $taskData['info_datecompleted'] = $attributes['value'];
- break;
+ case 'COMPLETED':
+ $taskData['info_datecompleted'] = $attribute['value'];
+ break;
- case 'DTSTART':
- $taskData['info_startdate'] = $attributes['value'];
- break;
+ case 'DTSTART':
+ $taskData['info_startdate'] = $attribute['value'];
+ break;
- case 'PRIORITY':
- if (1 <= $attributes['value'] && $attributes['value'] <= 9)
+ case 'PRIORITY':
+ if (0 <= $attribute['value'] && $attribute['value'] <= 9)
+ {
+ if ($this->productManufacturer == 'funambol' &&
+ (strpos($this->productName, 'outlook') !== false
+ || strpos($this->productName, 'pocket pc') !== false))
{
- $taskData['info_priority'] = $this->vcal_priority2egw_priority[$attributes['value']];
+ $taskData['info_priority'] = (int) $this->priority_funambol2egw[$attribute['value']];
}
else
{
- $taskData['info_priority'] = 1; // default = normal
+ $taskData['info_priority'] = (int) $this->priority_ical2egw[$attribute['value']];
}
- break;
+ }
+ else
+ {
+ $taskData['info_priority'] = 1; // default = normal
+ }
+ break;
- case 'STATUS':
- // check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user)
- foreach($component->_attributes as $attr)
- {
- if ($attr['name'] == 'X-INFOLOG-STATUS') break;
- }
- $taskData['info_status'] = $this->vtodo2status($attributes['value'],
- $attr['name'] == 'X-INFOLOG-STATUS' ? $attr['value'] : null);
- break;
+ case 'STATUS':
+ // check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user)
+ foreach ($component->_attributes as $attr)
+ {
+ if ($attr['name'] == 'X-INFOLOG-STATUS') break;
+ }
+ $taskData['info_status'] = $this->vtodo2status($attribute['value'],
+ $attr['name'] == 'X-INFOLOG-STATUS' ? $attr['value'] : null);
+ break;
- case 'SUMMARY':
- $taskData['info_subject'] = $attributes['value'];
- break;
+ case 'SUMMARY':
+ $taskData['info_subject'] = str_replace("\r\n", "\n", $attribute['value']);
+ break;
- case 'RELATED-TO':
- $taskData['info_id_parent'] = $this->getParentID($attributes['value']);
- break;
+ case 'RELATED-TO':
+ $taskData['info_id_parent'] = $this->getParentID($attribute['value']);
+ break;
- case 'CATEGORIES':
- $cats = $this->find_or_add_categories(explode(',', $attributes['value']), $_taskID);
+ case 'CATEGORIES':
+ if (!empty($attribute['value']))
+ {
+ $cats = $this->find_or_add_categories(explode(',',$attribute['value']), $_taskID);
$taskData['info_cat'] = $cats[0];
- break;
+ }
+ break;
- case 'UID':
- if (strlen($attributes['value']) >= $minimum_uid_length) {
- $taskData['info_uid'] = $attributes['value'];
- }
- break;
+ case 'UID':
+ if (strlen($attribute['value']) >= $minimum_uid_length)
+ {
+ $taskData['info_uid'] = $attribute['value'];
+ }
+ break;
- case 'PERCENT-COMPLETE':
- $taskData['info_percent'] = (int) $attributes['value'];
- break;
- }
+ case 'PERCENT-COMPLETE':
+ $taskData['info_percent'] = (int) $attribute['value'];
+ break;
}
- # the horde ical class does already convert in parsevCalendar
- # do NOT convert here
- #$taskData = $GLOBALS['egw']->translation->convert($taskData, 'UTF-8');
-
- Horde::logMessage("vtodotoegw:\n" . print_r($taskData, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- return $taskData;
}
+ break;
}
- return false;
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_taskID)\n" .
+ ($taskData ? array2string($taskData) : 'FALSE') . "\n",3,$this->logfile);
+ }
+ return $taskData;
}
/**
@@ -424,7 +613,8 @@ class infolog_ical extends infolog_bo
*/
function exportVNOTE($_noteID, $_type)
{
- $note = $this->read($_noteID);
+ if(!($note = $this->read($_noteID, true, 'server'))) return false;
+
$note = $GLOBALS['egw']->translation->convert($note,
$GLOBALS['egw']->translation->charset(), 'UTF-8');
@@ -433,78 +623,75 @@ class infolog_ical extends infolog_bo
case 'text/plain':
$txt = $note['info_subject']."\n\n".$note['info_des'];
return $txt;
- break;
case 'text/x-vnote':
+ if (!empty($note['info_cat']))
+ {
+ $cats = $this->get_categories(array($note['info_cat']));
+ $note['info_cat'] = $GLOBALS['egw']->translation->convert($cats[0],
+ $GLOBALS['egw']->translation->charset(), 'UTF-8');
+ }
$vnote = new Horde_iCalendar_vnote();
- $options = array('CHARSET' => 'UTF-8');
$vNote->setAttribute('VERSION', '1.1');
- foreach (array( 'SUMMARY' => $note['info_subject'],
- 'BODY' => $note['info_des'],
+ foreach (array( 'SUMMARY' => $note['info_subject'],
+ 'BODY' => $note['info_des'],
+ 'CATEGORIES' => $note['info_cat'],
) as $field => $value)
{
- $vnote->setAttribute($field, $value);
- if ($this->productManufacturer != 'groupdav'
- && preg_match('/([\177-\377])/', $value))
+ $options = array();
+ if (preg_match('/[^\x20-\x7F]/', $value))
{
- $vevent->setParameter($field, $options);
+ $options['CHARSET'] = 'UTF-8';
+ switch ($this->productManufacturer)
+ {
+ case 'groupdav':
+ if ($this->productName == 'kde')
+ {
+ $options['ENCODING'] = 'QUOTED-PRINTABLE';
+ }
+ else
+ {
+ $options['CHARSET'] = '';
+
+ if (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
+ {
+ $options['ENCODING'] = 'QUOTED-PRINTABLE';
+ }
+ else
+ {
+ $options['ENCODING'] = '';
+ }
+ }
+ break;
+ case 'funambol':
+ $options['ENCODING'] = 'FUNAMBOL-QP';
+ }
}
+ $vevent->setAttribute($field, $value, $options);
}
if ($note['info_startdate'])
{
$vnote->setAttribute('DCREATED',$note['info_startdate']);
}
- $vnote->setAttribute('DCREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'add'));
- $vnote->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'modify'));
-
- if (!empty($note['info_cat']))
+ else
{
- $cats = $this->get_categories(array($note['info_cat']));
- $value = $cats[0];
- $vnote->setAttribute('CATEGORIES', $value);
- if ($this->productManufacturer != 'groupdav'
- && preg_match('/([\177-\377])/', $value))
- {
- $vevent->setParameter('CATEGORIES', $options);
- }
+ $vnote->setAttribute('DCREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'add'));
}
+ $vnote->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'modify'));
#$vnote->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE');
- return $vnote->exportvCalendar();
- break;
+ $retval = $vnote->exportvCalendar();
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($retval)."\n",3,$this->logfile);
+ }
+ return $retval;
}
return false;
}
- /**
- * Check whether to export a date or date+time
- *
- * @param Horde_iCalendar_* $vevent
- * @param string $attr attribute name
- * @param int $value timestamp
- * @param boolean $force=false, set DATE in any case
- * @return boolean true, if date only
- */
- static function setDateOrTime($vevent,$attr,$value,$force=false)
- {
- if ($force || date('Hi', $value) == '0000')
- {
- $vevent->setAttribute($attr, array(
- 'year' => date('Y', $value),
- 'month' => date('m', $value),
- 'mday' => date('d', $value),
- ), array('VALUE' => 'DATE'));
- return true;
- }
- else
- {
- $vevent->setAttribute($attr, $value);
- }
- return false;
- }
-
-
/**
* Import a VNOTE component of an iCal
*
@@ -516,14 +703,25 @@ class infolog_ical extends infolog_bo
*/
function importVNOTE(&$_vcalData, $_type, $_noteID=-1, $merge=false)
{
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($_vcalData)."\n",3,$this->logfile);
+ }
+
if (!($note = $this->vnotetoegw($_vcalData, $_type, $_noteID))) return false;
if($_noteID > 0) $note['info_id'] = $_noteID;
if (empty($note['info_status'])) $note['info_status'] = 'done';
- #_debug_array($taskData);exit;
- return $this->write($note);
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($note)."\n",3,$this->logfile);
+ }
+
+ return $this->write($note, true, true, false);
}
/**
@@ -531,40 +729,19 @@ class infolog_ical extends infolog_bo
*
* @param string $_vcalData VNOTE
* @param int $contentID=null infolog_id (or null, if unkown)
+ * @param boolean $relax=false if true, a weaker match algorithm is used
+ *
* @return infolog_id of a matching entry or false, if nothing was found
*/
- function searchVNOTE($_vcalData, $_type, $contentID=null)
+ function searchVNOTE($_vcalData, $_type, $contentID=null, $relax=false)
{
- if (!($note = $this->vnotetoegw($_vcalData,$_type,$contentID))) return false;
+ if (!($note = $this->vnotetoegw($_vcalData,$_type,$contentID))) return array();
if ($contentID) $note['info_id'] = $contentID;
unset($note['info_startdate']);
- $filter = array();
-
- if (!empty($note['info_des']))
- {
- $description = trim(preg_replace("/\r?\n?\\[[A-Z_]+:.*\\]/i", '', $note['info_des']));
- unset($note['info_des']);
- if (strlen($description))
- {
- $filter['search'] = $description;
- }
- }
-
- $filter['col_filter'] = $note;
-
- if (($foundItems = $this->search($filter)))
- {
- if (count($foundItems) > 0)
- {
- $itemIDs = array_keys($foundItems);
- return $itemIDs[0];
- }
- }
-
- return false;
+ return $this->findInfo($note, $relax, !$this->useServerTZ);
}
/**
@@ -577,6 +754,13 @@ class infolog_ical extends infolog_bo
*/
function vnotetoegw($_data, $_type, $_noteID=-1)
{
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_type, $_noteID)\n" .
+ array2string($_data)."\n",3,$this->logfile);
+ }
+ $note = false;
+
switch ($_type)
{
case 'text/plain':
@@ -586,24 +770,21 @@ class infolog_ical extends infolog_bo
$txt = $botranslation->convert($_data, 'utf-8');
$txt = str_replace("\r\n", "\n", $txt);
- if (preg_match("/^(^\n)\n\n(.*)$/", $txt, $match))
+ if (preg_match('/([^\n]+)\n\n(.*)/m', $txt, $match))
{
- $note['info_subject'] = $match[0];
- $note['info_des'] = $match[1];
+ $note['info_subject'] = $match[1];
+ $note['info_des'] = $match[2];
}
else
{
- // should better be imported as subject, but causes duplicates
- // TODO: should be examined
- $note['info_des'] = $txt;
+ $note['info_subject'] = $txt;
}
-
- return $note;
break;
case 'text/x-vnote':
$vnote = new Horde_iCalendar;
if (!$vcal->parsevCalendar($_data)) return false;
+ $version = $vcal->getAttribute('VERSION');
$components = $vnote->getComponent();
foreach ($components as $component)
@@ -618,24 +799,31 @@ class infolog_ical extends infolog_bo
switch ($attribute['name'])
{
case 'BODY':
- $note['info_des'] = $attribute['value'];
+ $note['info_des'] = str_replace("\r\n", "\n", $attribute['value']);
break;
case 'SUMMARY':
- $note['info_subject'] = $attribute['value'];
+ $note['info_subject'] = str_replace("\r\n", "\n", $attribute['value']);
break;
case 'CATEGORIES':
- $cats = $this->find_or_add_categories(explode(',', $attribute['value']), $_noteID);
- $note['info_cat'] = $cats[0];
+ if ($attribute['value'])
+ {
+ $cats = $this->find_or_add_categories(explode(',',$attribute['value']), $_noteID);
+ $note['info_cat'] = $cats[0];
+ }
break;
}
}
}
- return $note;
}
}
- return false;
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_type, $_noteID)\n" .
+ ($note ? array2string($note) : 'FALSE') ."\n",3,$this->logfile);
+ }
+ return $note;
}
/**
@@ -678,9 +866,31 @@ class infolog_ical extends infolog_bo
{
$this->uidExtension = true;
}
+ if (isset($deviceInfo['tzid']) &&
+ $deviceInfo['tzid'])
+ {
+ $this->useServerTZ = ($deviceInfo['tzid'] == 1);
+ }
}
- Horde::logMessage('setSupportedFields(' . $this->productManufacturer . ', ' . $this->productName .')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ if (in_array($this->productManufacturer,array('file','groupdav')))
+ {
+ $this->useServerTZ = true;
+ }
+
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
+ '(' . $this->productManufacturer .
+ ', '. $this->productName .', ' .
+ ($this->useServerTZ ? 'SERVERTIME' : 'USERTIME') .
+ ")\n" , 3, $this->logfile);
+ }
+
+ Horde::logMessage('setSupportedFields(' . $this->productManufacturer . ', '
+ . $this->productName .', ' .
+ ($this->useServerTZ ? 'SERVERTIME' : 'USERTIME') .')',
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
}
diff --git a/infolog/inc/class.infolog_sif.inc.php b/infolog/inc/class.infolog_sif.inc.php
index 602e7fca21..120a426662 100644
--- a/infolog/inc/class.infolog_sif.inc.php
+++ b/infolog/inc/class.infolog_sif.inc.php
@@ -90,6 +90,59 @@ class infolog_sif extends infolog_bo
*/
var $uidExtension = false;
+ /**
+ * user preference: use server timezone for exports to device
+ *
+ * @var boolean
+ */
+ var $useServerTZ = false;
+
+ /**
+ * Set Logging
+ *
+ * @var boolean
+ */
+ var $log = false;
+ var $logfile="/tmp/log-infolog-sif";
+
+ /**
+ * Constructor
+ *
+ */
+ function __construct()
+ {
+ parent::__construct();
+ if ($this->log) $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-infolog-sif";
+ $this->vCalendar = new Horde_iCalendar;
+ }
+
+ /**
+ * Get DateTime value for a given time and timezone
+ *
+ * @param int|string|DateTime $time in server-time as returned by calendar_bo for $data_format='server'
+ * @param string $servertime=false if true => export in server-time
+ *
+ */
+ function getDateTime($time, $servertime=false)
+ {
+ // check for date --> export it as such
+ if (date('Hi', $time) == '0000')
+ {
+ $value = date('Y-m-d', $time);
+ }
+ else
+ {
+ if ($servertime)
+ {
+ $value = date('Ymd\THis', $time);
+ }
+ else
+ {
+ $value = $this->vCalendar->_exportDateTime($time);
+ }
+ }
+ return $value;
+ }
function startElement($_parser, $_tag, $_attributes)
{
@@ -121,14 +174,15 @@ class infolog_sif extends infolog_bo
*/
function siftoegw($sifData, $_sifType, $_id=-1)
{
+
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_sifType, $_id)\n" .
+ array2string($sifData) . "\n", 3, $this->logfile);
+ }
+
$sysCharSet = $GLOBALS['egw']->translation->charset();
- #$tmpfname = tempnam('/tmp/sync/contents','sift_');
-
- #$handle = fopen($tmpfname, "w");
- #fwrite($handle, $sifData);
- #fclose($handle);
-
switch ($_sifType)
{
case 'note':
@@ -136,9 +190,12 @@ class infolog_sif extends infolog_bo
break;
case 'task':
- default:
$this->_currentSIFMapping = $this->_sifTaskMapping;
break;
+
+ default:
+ // we don't know how to handle this
+ return false;
}
$this->xml_parser = xml_parser_create('UTF-8');
@@ -156,16 +213,22 @@ class infolog_sif extends infolog_bo
return false;
}
- if (!array($this->_extractedSIFData)) return false;
+ if (!array($this->_extractedSIFData))
+ {
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()[PARSER FAILD]\n",
+ 3, $this->logfile);
+ }
+ return false;
+ }
+ $infoData = array();
switch ($_sifType)
{
case 'task':
- $taskData = array();
- $vcal = new Horde_iCalendar;
-
- $taskData['info_type'] = 'task';
- $taskData['info_status'] = 'not-started';
+ $infoData['info_type'] = 'task';
+ $infoData['info_status'] = 'not-started';
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
@@ -186,7 +249,7 @@ class infolog_sif extends infolog_bo
switch($key)
{
case 'info_access':
- $taskData[$key] = ((int)$value > 0) ? 'private' : 'public';
+ $infoData[$key] = ((int)$value > 0) ? 'private' : 'public';
break;
case 'info_datecompleted':
@@ -194,9 +257,9 @@ class infolog_sif extends infolog_bo
case 'info_startdate':
if (!empty($value))
{
- $taskData[$key] = $vcal->_parseDateTime($value);
+ $infoData[$key] = $this->vCalendar->_parseDateTime($value);
// somehow the client always deliver a timestamp about 3538 seconds, when no startdate set.
- if ($taskData[$key] < 10000) unset($taskData[$key]);
+ if ($infoData[$key] < 10000) unset($infoData[$key]);
}
break;
@@ -204,53 +267,49 @@ class infolog_sif extends infolog_bo
case 'info_cat':
if (!empty($value))
{
- $categories1 = explode(',', $value);
- $categories2 = explode(';', $value);
- $categories = count($categories1) > count($categories2) ? $categories1 : $categories2;
- $categories = $this->find_or_add_categories($categories, $_id);
- $taskData['info_cat'] = $categories[0];
+ $categories = $this->find_or_add_categories(explode(';', $value), $_id);
+ $infoData['info_cat'] = $categories[0];
}
break;
case 'info_priority':
- $taskData[$key] = (int)$value;
+ $infoData[$key] = (int)$value;
break;
case 'info_status':
switch ($value)
{
case '0':
- $taskData[$key] = 'not-started';
+ $infoData[$key] = 'not-started';
break;
case '1':
- $taskData[$key] = 'ongoing';
+ $infoData[$key] = 'ongoing';
break;
case '2':
- $taskData[$key] = 'done';
- $taskData['info_percent'] = 100;
+ $infoData[$key] = 'done';
+ $infoData['info_percent'] = 100;
break;
case '3':
- $taskData[$key] = 'waiting';
+ $infoData[$key] = 'waiting';
break;
case '4':
if ($this->productName == 'blackberry plug-in')
{
- $taskData[$key] = 'deferred';
+ $infoData[$key] = 'deferred';
}
else
{
- $taskData[$key] = 'cancelled';
+ $infoData[$key] = 'cancelled';
}
break;
default:
- $taskData[$key] = 'ongoing';
- break;
+ $infoData[$key] = 'ongoing';
}
break;
case 'complete':
- $taskData['info_status'] = 'done';
- $taskData['info_percent'] = 100;
+ $infoData['info_status'] = 'done';
+ $infoData['info_percent'] = 100;
break;
case 'info_des':
@@ -259,7 +318,7 @@ class infolog_sif extends infolog_bo
{
if (strlen($matches[1]) >= $minimum_uid_length)
{
- $taskData['info_uid'] = $matches[1];
+ $infoData['info_uid'] = $matches[1];
}
//$value = str_replace($matches[0], '', $value);
}
@@ -267,25 +326,28 @@ class infolog_sif extends infolog_bo
{
if (strlen($matches[1]) >= $minimum_uid_length)
{
- $taskData['info_id_parent'] = $this->getParentID($matches[1]);
+ $infoData['info_id_parent'] = $this->getParentID($matches[1]);
}
//$value = str_replace($matches[0], '', $value);
}
default:
- $taskData[$key] = $value;
- break;
+ $infoData[$key] = str_replace("\r\n", "\n", $value);
+ }
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ "key=$key => value=" . $infoData[$key] . "\n", 3, $this->logfile);
}
- #error_log("infolog task key=$key => value=" . $taskData[$key]);
}
-
- return $taskData;
+ if (empty($infoData['info_datecompleted']))
+ {
+ $infoData['info_datecompleted'] = 0;
+ }
break;
case 'note':
- $noteData = array();
- $noteData['info_type'] = 'note';
- $vcal = new Horde_iCalendar;
+ $infoData['info_type'] = 'note';
foreach ($this->_extractedSIFData as $key => $value)
{
@@ -298,40 +360,40 @@ class infolog_sif extends infolog_bo
case 'info_startdate':
if (!empty($value))
{
- $noteData[$key] = $vcal->_parseDateTime($value);
+ $infoData[$key] = $this->vCalendar->_parseDateTime($value);
// somehow the client always deliver a timestamp about 3538 seconds, when no startdate set.
- if ($noteData[$key] < 10000) $noteData[$key] = '';
+ if ($infoData[$key] < 10000) $infoData[$key] = '';
}
else
{
- $noteData[$key] = '';
+ $infoData[$key] = '';
}
break;
case 'info_cat':
if (!empty($value))
{
- $categories1 = explode(',', $value);
- $categories2 = explode(';', $value);
- $categories = count($categories1) > count($categories2) ? $categories1 : $categories2;
- $categories = $this->find_or_add_categories($categories, $_id);
- $noteData['info_cat'] = $categories[0];
+ $categories = $this->find_or_add_categories(explode(';', $value), $_id);
+ $infoData['info_cat'] = $categories[0];
}
break;
default:
- $noteData[$key] = $value;
- break;
+ $infoData[$key] = str_replace("\r\n", "\n", $value);
+ }
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ "key=$key => value=" . $infoData[$key] . "\n", 3, $this->logfile);
}
- #error_log("infolog note key=$key => value=".$noteData[$key]);
}
- return $noteData;
- break;
-
-
- default:
- return false;
}
+ if ($this->log)
+ {
+ error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
+ array2string($infoData) . "\n", 3, $this->logfile);
+ }
+ return $infoData;
}
/**
@@ -345,28 +407,13 @@ class infolog_sif extends infolog_bo
*/
function searchSIF($_sifData, $_sifType, $contentID=null, $relax=false)
{
- if (!($egwData = $this->siftoegw($_sifData, $_sifType, $contentID))) return false;
+ if (!($egwData = $this->siftoegw($_sifData, $_sifType, $contentID))) return array();
if ($contentID) $egwData['info_id'] = $contentID;
- if ($_sifType == 'task') return $this->findVTODO($egwData, $relax);
-
if ($_sifType == 'note') unset($egwData['info_startdate']);
- $filter = array();
-
- $filter['col_filter'] = $egwData;
-
- if ($foundItems = $this->search($filter))
- {
- if (count($foundItems) > 0)
- {
- $itemIDs = array_keys($foundItems);
- return $itemIDs[0];
- }
- }
-
- return false;
+ return $this->findInfo($egwData, $relax, $this->tzid);
}
/**
@@ -379,18 +426,12 @@ class infolog_sif extends infolog_bo
*/
function addSIF($_sifData, $_id, $_sifType, $merge=false)
{
+
if (!($egwData = $this->siftoegw($_sifData, $_sifType, $_id))) return false;
if ($_id > 0) $egwData['info_id'] = $_id;
- if (empty($taskData['info_datecompleted']))
- {
- $taskData['info_datecompleted'] = 0;
- }
-
- $egwID = $this->write($egwData, false);
-
- return $egwID;
+ return $this->write($egwData, true, true, false);
}
@@ -405,189 +446,161 @@ class infolog_sif extends infolog_bo
{
$sysCharSet = $GLOBALS['egw']->translation->charset();
+ if (!($infoData = $this->read($_id, true, 'server'))) return false;
+
switch($_sifType)
{
case 'task':
- if (($taskData = $this->read($_id)))
+ if ($infoData['info_id_parent'])
{
- $vcal = new Horde_iCalendar('1.0');
-
- if ($taskData['info_id_parent'])
- {
- $parent = $this->read($taskData['info_id_parent']);
- $taskData['info_id_parent'] = $parent['info_uid'];
- }
- else
- {
- $taskData['info_id_parent'] = '';
- }
-
- if (!preg_match('/\[UID:.+\]/m', $taskData['info_des']))
- {
- $taskData['info_des'] .= "\r\n[UID:" . $taskData['info_uid'] . "]";
- if ($taskData['info_id_parent'] != '')
- {
- $taskData['info_des'] .= "\r\n[PARENT_UID:" . $taskData['info_id_parent'] . "]";
- }
- }
-
- $sifTask = self::xml_decl . "\n" . self::SIF_decl;
-
- foreach ($this->_sifTaskMapping as $sifField => $egwField)
- {
- if (empty($egwField)) continue;
-
- $value = $GLOBALS['egw']->translation->convert($taskData[$egwField], $sysCharSet, 'utf-8');
-
- switch ($sifField)
- {
-
- case 'Complete':
- // is handled with DateCompleted
- break;
-
- case 'DateCompleted':
- if ($taskData[info_status] == 'done')
- {
- $sifTask .= "1";
- }
- else
- {
- $sifTask .= "0";
- continue;
- }
- case 'DueDate':
- if (!empty($value))
- {
- $hdate = new Horde_Date($value);
- $value = $vcal->_exportDate($hdate, '000000Z');
- $sifTask .= "<$sifField>$value$sifField>";
- }
- else
- {
- $sifTask .= "<$sifField>$sifField>";
- }
- break;
- case 'StartDate':
- if (!empty($value))
- {
- $value = $vcal->_exportDateTime($value);
- $sifTask .= "<$sifField>$value$sifField>";
- }
- else
- {
- $sifTask .= "<$sifField>$sifField>";
- }
- break;
-
- case 'Importance':
- if ($value > 3) $value = 3;
- $sifTask .= "<$sifField>$value$sifField>";
- break;
-
- case 'Sensitivity':
- $value = ($value == 'private' ? '2' : '0');
- $sifTask .= "<$sifField>$value$sifField>";
- break;
-
- case 'Status':
- switch ($value)
- {
- case 'cancelled':
- case 'deferred':
- $value = '4';
- break;
- case 'waiting':
- case 'nonactive':
- $value = '3';
- break;
- case 'done':
- case 'archive':
- case 'billed':
- $value = '2';
- break;
- case 'not-started':
- case 'template':
- $value = '0';
- break;
- default: //ongoing
- $value = 1;
- break;
- }
- $sifTask .= "<$sifField>$value$sifField>";
- break;
-
- case 'Categories':
- if (!empty($value) && $value)
- {
- $value = implode(', ', $this->get_categories(array($value)));
- $value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
- }
- else
- {
- break;
- }
-
- default:
- $value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
- $sifTask .= "<$sifField>$value$sifField>";
- break;
- }
- }
- $sifTask .= '00';
- return $sifTask;
+ $parent = $this->read($infoData['info_id_parent']);
+ $infoData['info_id_parent'] = $parent['info_uid'];
}
- break;
+ else
+ {
+ $infoData['info_id_parent'] = '';
+ }
+
+ if (!preg_match('/\[UID:.+\]/m', $infoData['info_des']))
+ {
+ $infoData['info_des'] .= "\r\n[UID:" . $infoData['info_uid'] . "]";
+ if ($infoData['info_id_parent'] != '')
+ {
+ $infoData['info_des'] .= "\r\n[PARENT_UID:" . $infoData['info_id_parent'] . "]";
+ }
+ }
+
+ $sifTask = self::xml_decl . "\n" . self::SIF_decl;
+
+ foreach ($this->_sifTaskMapping as $sifField => $egwField)
+ {
+ if (empty($egwField)) continue;
+
+ $value = $GLOBALS['egw']->translation->convert($infoData[$egwField], $sysCharSet, 'utf-8');
+
+ switch ($sifField)
+ {
+
+ case 'Complete':
+ // is handled with DateCompleted
+ break;
+
+ case 'DateCompleted':
+ if ($infoData[info_status] != 'done')
+ {
+ $sifTask .= "0";
+ continue;
+ }
+ $sifTask .= "1";
+
+ case 'DueDate':
+ case 'StartDate':
+ $sifTask .= "<$sifField>";
+ if (!empty($value))
+ {
+ $sifTask .= $this->getDateTime($value, $this->useServerTZ);
+ }
+ $sifTask .= "$sifField>";
+ break;
+
+ case 'Importance':
+ if ($value > 3) $value = 3;
+ $sifTask .= "<$sifField>$value$sifField>";
+ break;
+
+ case 'Sensitivity':
+ $value = ($value == 'private' ? '2' : '0');
+ $sifTask .= "<$sifField>$value$sifField>";
+ break;
+
+ case 'Status':
+ switch ($value)
+ {
+ case 'cancelled':
+ case 'deferred':
+ $value = '4';
+ break;
+ case 'waiting':
+ case 'nonactive':
+ $value = '3';
+ break;
+ case 'done':
+ case 'archive':
+ case 'billed':
+ $value = '2';
+ break;
+ case 'not-started':
+ case 'template':
+ $value = '0';
+ break;
+ default: //ongoing
+ $value = 1;
+ break;
+ }
+ $sifTask .= "<$sifField>$value$sifField>";
+ break;
+
+ case 'Categories':
+ if (!empty($value) && $value)
+ {
+ $value = implode('; ', $this->get_categories(array($value)));
+ $value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
+ }
+ else
+ {
+ break;
+ }
+
+ default:
+ $value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
+ $sifTask .= "<$sifField>$value$sifField>";
+ break;
+ }
+ }
+ $sifTask .= '00';
+ return $sifTask;
case 'note':
- if (($taskData = $this->read($_id)))
+ $sifNote = self::xml_decl . "\n" . self::SIF_decl;
+
+ foreach ($this->_sifNoteMapping as $sifField => $egwField)
{
- $vcal = new Horde_iCalendar('1.0');
+ if(empty($egwField)) continue;
- $sifNote = self::xml_decl . "\n" . self::SIF_decl;
+ $value = $GLOBALS['egw']->translation->convert($infoData[$egwField], $sysCharSet, 'utf-8');
- foreach ($this->_sifNoteMapping as $sifField => $egwField)
+ switch ($sifField)
{
- if(empty($egwField)) continue;
+ case 'Date':
+ $sifNote .= '<$sifField>';
+ if (!empty($value))
+ {
+ $sifNote .= $this->getDateTime($value, $this->useServerTZ);
+ }
+ $sifNote .= '$sifField>';
+ break;
- $value = $GLOBALS['egw']->translation->convert($taskData[$egwField], $sysCharSet, 'utf-8');
-
- switch ($sifField)
- {
- case 'Date':
- if (!empty($value))
- {
- $value = $vcal->_exportDateTime($value);
- }
- $sifNote .= "<$sifField>$value$sifField>";
+ case 'Categories':
+ if (!empty($value))
+ {
+ $value = implode('; ', $this->get_categories(array($value)));
+ $value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
+ }
+ else
+ {
break;
+ }
- case 'Categories':
- if (!empty($value))
- {
- $value = implode('; ', $this->get_categories(array($value)));
- $value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
- }
- else
- {
- break;
- }
-
- default:
- $value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
- $sifNote .= "<$sifField>$value$sifField>";
- break;
- }
+ default:
+ $value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
+ $sifNote .= "<$sifField>$value$sifField>";
+ break;
}
- $sifNote .= '';
- return $sifNote;
}
- break;
-
- default;
- return false;
+ $sifNote .= '';
+ return $sifNote;
}
-
+ return false;
}
/**
@@ -610,6 +623,11 @@ class infolog_sif extends infolog_bo
{
$this->uidExtension = true;
}
+ if (isset($deviceInfo['tzid']) &&
+ $deviceInfo['tzid'])
+ {
+ $this->useServerTZ = ($deviceInfo['tzid'] == 1);
+ }
}
// store product name and version, to be able to use it elsewhere
if ($_productName)
diff --git a/infolog/inc/class.infolog_so.inc.php b/infolog/inc/class.infolog_so.inc.php
index c57a1327ec..95684c8e20 100644
--- a/infolog/inc/class.infolog_so.inc.php
+++ b/infolog/inc/class.infolog_so.inc.php
@@ -5,7 +5,7 @@
* @link http://www.egroupware.org
* @author Ralf Becker
* @package infolog
- * @copyright (c) 2003-8 by Ralf Becker
+ * @copyright (c) 2003-9 by Ralf Becker
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@@ -139,19 +139,27 @@ class infolog_so
/**
* Filter for a given responsible user: info_responsible either contains a the user or one of his memberships
*
- * @param int $user
+ * @param int|array $users one or more account_ids
* @return string
*
* @todo make the responsible a second table and that filter a join with the responsible table
*/
- function responsible_filter($user)
+ function responsible_filter($users)
{
- if (!$user) return '0';
+ if (!$users) return '0';
- $responsible = $user > 0 ? $GLOBALS['egw']->accounts->memberships($user,true) :
- $GLOBALS['egw']->accounts->members($user,true);
-
- $responsible[] = $user;
+ $responsible = array();
+ foreach((array)$users as $user)
+ {
+ $responsible = array_merge($responsible,
+ $user > 0 ? $GLOBALS['egw']->accounts->memberships($user,true) :
+ $GLOBALS['egw']->accounts->members($user,true));
+ $responsible[] = $user;
+ }
+ if (is_array($users))
+ {
+ $responsible = array_unique($responsible);
+ }
foreach($responsible as $key => $uid)
{
$responsible[$key] = $this->db->concat("','",'info_responsible',"','")." LIKE '%,$uid,%'";
@@ -171,14 +179,18 @@ class infolog_so
*/
function aclFilter($filter = False)
{
- preg_match('/(my|responsible|delegated|own|privat|private|all|none|user)([0-9]*)/',$filter_was=$filter,$vars);
+ preg_match('/(my|responsible|delegated|own|privat|private|all|none|user)([0-9,-]*)/',$filter_was=$filter,$vars);
$filter = $vars[1];
- $f_user = intval($vars[2]);
+ $f_user = $vars[2];
if (isset($this->acl_filter[$filter.$f_user]))
{
return $this->acl_filter[$filter.$f_user]; // used cached filter if found
}
+ if ($f_user && strpos($f_user,',') !== false)
+ {
+ $f_user = explode(',',$f_user);
+ }
$filtermethod = " (info_owner=$this->user"; // user has all rights
@@ -236,9 +248,11 @@ class infolog_so
}
$filtermethod .= ') ';
- if ($filter == 'user' && $f_user > 0)
+ if ($filter == 'user' && $f_user)
{
- $filtermethod .= " AND (info_owner=$f_user AND info_responsible='0' OR ".$this->responsible_filter($f_user).')';
+ $filtermethod .= $this->db->expression($this->info_table,' AND (',array(
+ 'info_owner' => $f_user,
+ )," AND info_responsible='0' OR ",$this->responsible_filter($f_user),')');
}
}
//echo "aclFilter(filter='$filter_was',user='$user') = '$filtermethod', privat_user_list=".print_r($privat_user_list,True).", public_user_list=".print_r($public_user_list,True)."
\n";
@@ -724,18 +738,24 @@ class infolog_so
if (substr($col,0,5) != 'info_' && substr($col,0,1)!='#') $col = 'info_'.$col;
if (!empty($data) && preg_match('/^[a-z_0-9]+$/i',$col))
{
- if ($col == 'info_responsible')
+ switch ($col)
{
- $data = (int) $data;
- if (!$data) continue;
- $filtermethod .= " AND (".$this->responsible_filter($data)." OR info_responsible='0' AND ".
- $this->db->expression($this->info_table,array(
- 'info_owner' => $data > 0 ? $data : $GLOBALS['egw']->accounts->members($data,true)
- )).')';
- }
- else
- {
- $filtermethod .= ' AND '.$this->db->expression($this->info_table,array($col => $data));
+ case 'info_responsible':
+ $data = (int) $data;
+ if (!$data) continue;
+ $filtermethod .= ' AND ('.$this->responsible_filter($data)." OR info_responsible='0' AND ".
+ $this->db->expression($this->info_table,array(
+ 'info_owner' => $data > 0 ? $data : $GLOBALS['egw']->accounts->members($data,true)
+ )).')';
+ break;
+
+ case 'info_id': // info_id itself is ambigous
+ $filtermethod .= ' AND '.$this->db->expression($this->info_table,'main.',array('info_id' => $data));
+ break;
+
+ default:
+ $filtermethod .= ' AND '.$this->db->expression($this->info_table,array($col => $data));
+ break;
}
}
if ($col[0] == '#' && $query['custom_fields'] && $data)
diff --git a/infolog/inc/class.infolog_tracking.inc.php b/infolog/inc/class.infolog_tracking.inc.php
index 7871b58026..b4d7beed57 100644
--- a/infolog/inc/class.infolog_tracking.inc.php
+++ b/infolog/inc/class.infolog_tracking.inc.php
@@ -169,11 +169,11 @@ class infolog_tracking extends bo_tracking
{
return lang('%1 deleted by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
$GLOBALS['egw']->common->grab_owner_name($data['info_modifier']),
- $this->datetime($data['info_datemodified']-$this->infolog->tz_offset_s));
+ $this->datetime($data['info_datemodified']));
}
return lang('%1 modified by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
$GLOBALS['egw']->common->grab_owner_name($data['info_modifier']),
- $this->datetime($data['info_datemodified']-$this->infolog->tz_offset_s));
+ $this->datetime($data['info_datemodified']));
}
/**
@@ -207,10 +207,10 @@ class infolog_tracking extends bo_tracking
'info_owner' => $GLOBALS['egw']->common->grab_owner_name($data['info_owner']),
'info_status' => lang($data['info_status']=='deleted'?'deleted':$this->infolog->status[$data['info_type']][$data['info_status']]),
'info_percent' => (int)$data['info_percent'].'%',
- 'info_datecompleted' => $data['info_datecomplete'] ? $this->datetime($data['info_datecompleted']-$this->infolog->tz_offset_s) : '',
+ 'info_datecompleted' => $data['info_datecomplete'] ? $this->datetime($data['info_datecompleted']) : '',
'info_location' => $data['info_location'],
- 'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate']-$this->infolog->tz_offset_s,null) : '',
- 'info_enddate' => $data['info_enddate'] ? $this->datetime($data['info_enddate']-$this->infolog->tz_offset_s,false) : '',
+ 'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate'],null) : '',
+ 'info_enddate' => $data['info_enddate'] ? $this->datetime($data['info_enddate'],false) : '',
'info_responsible' => implode(', ',$responsible),
'info_subject' => $data['info_subject'],
) as $name => $value)