- merged SyncML-1.2 branch with trunk:

svn merge ^/trunk/infolog@27329 ^/branches/SyncML-1.2/infolog .
- completly reverted changes in inc/class.boinfolog.inc.php, as they are
  not related to SyncML at all:
svn revert inc/class.boinfolog.inc.php
This commit is contained in:
Ralf Becker 2009-07-15 20:04:17 +00:00
parent d333605510
commit 036b2cd14e
7 changed files with 895 additions and 439 deletions

View File

@ -4,6 +4,7 @@
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package infolog
* @copyright (c) 2003-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
@ -513,7 +514,6 @@ class infolog_bo
$GLOBALS['egw']->contenthistory->updateTimeStamp('infolog_'.$info['info_type'], $info_id, 'delete', time());
// send email notifications and do the history logging
require_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_tracking.inc.php');
if (!is_object($this->tracking))
{
$this->tracking = new infolog_tracking($this);
@ -533,7 +533,7 @@ class infolog_bo
* @param boolean $touch_modified=true touch the modification data and sets the modiefier's user-id
* @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)
{
//echo "boinfolog::write()values="; _debug_array($values);
if ($status_only = $values['info_id'] && !$this->check_access($values['info_id'],EGW_ACL_EDIT))
@ -578,7 +578,7 @@ class infolog_bo
if ($set_completed)
{
$values['info_datecompleted'] = $this->user_time_now;
$values['info_percent'] = '100%';
$values['info_percent'] = 100;
$forcestatus = true;
$status = 'done';
if (isset($values['info_type']) && !in_array($values['info_status'],array('done','billed','cancelled'))) {
@ -608,7 +608,7 @@ class infolog_bo
}
if (in_array($values['info_status'],array('done','billed')))
{
$values['info_percent'] = '100';
$values['info_percent'] = 100;
}
if ((int)$values['info_percent'] == 100 && !in_array($values['info_status'],array('done','billed','cancelled')))
{
@ -659,7 +659,7 @@ class infolog_bo
// 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!
$xmprpc = is_object($GLOBALS['server']) && $GLOBALS['server']->last_method;
$xmlrpc = is_object($GLOBALS['server']) && $GLOBALS['server']->last_method;
$check_modified = $values['info_datemodified'] && !$xmlrpc ? $values['info_datemodified']-$this->tz_offset_s : false;
$values['info_datemodified'] = $this->user_time_now;
}
@ -1080,7 +1080,7 @@ class infolog_bo
$cat_id = $this->categories->name2id($cat_name, 'X-');
if (!$cat_id)
{
$cat_id = $this->categories->add(array('name' => $cat_name,'descr' => $cat_name));
$cat_id = $this->categories->add(array('name' => $cat_name, 'descr' => $cat_name, 'access' => 'private'));
}
if ($cat_id)
@ -1172,32 +1172,32 @@ class infolog_bo
// check if we already send a notification for that infolog entry, eg. starting and due on same day
if (in_array($info['info_id'],$notified_info_ids)) continue;
if (is_null($tracking) || $tracking->user != $user)
if (is_null($this->tracking) || $this->tracking->user != $user)
{
require_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_tracking.inc.php');
$tracking = new infolog_tracking($this);
$this->tracking = new infolog_tracking($this);
}
switch($pref)
{
case 'notify_due_responsible':
$info['message'] = lang('%1 you are responsible for is due at %2',$this->enums['type'][$info['info_type']],
$tracking->datetime($info['info_enddate']-$this->tz_offset_s,false));
$this->tracking->datetime($info['info_enddate']-$this->tz_offset_s,false));
break;
case 'notify_due_delegated':
$info['message'] = lang('%1 you delegated is due at %2',$this->enums['type'][$info['info_type']],
$tracking->datetime($info['info_enddate']-$this->tz_offset_s,false));
$this->tracking->datetime($info['info_enddate']-$this->tz_offset_s,false));
break;
case 'notify_start_responsible':
$info['message'] = lang('%1 you are responsible for is starting at %2',$this->enums['type'][$info['info_type']],
$tracking->datetime($info['info_startdate']-$this->tz_offset_s,null));
$this->tracking->datetime($info['info_startdate']-$this->tz_offset_s,null));
break;
case 'notify_start_delegated':
$info['message'] = lang('%1 you delegated is starting at %2',$this->enums['type'][$info['info_type']],
$tracking->datetime($info['info_startdate']-$this->tz_offset_s,null));
$this->tracking->datetime($info['info_startdate']-$this->tz_offset_s,null));
break;
}
error_log("notifiying $user($email) about $info[info_subject]: $info[message]");
$tracking->send_notification($info,null,$email,$user,$pref);
$this->tracking->send_notification($info,null,$email,$user,$pref);
$notified_info_ids[] = $info['info_id'];
}
@ -1221,6 +1221,8 @@ class infolog_bo
'template' => 'CANCELLED',
'nonactive' => 'CANCELLED',
'archive' => 'CANCELLED',
'deferred' => 'NEEDS-ACTION',
'waiting' => 'IN-PROCESS',
);
/** conversion of vtodo status to infolog status
@ -1229,7 +1231,9 @@ class infolog_bo
*/
var $_vtodo2status = array(
'NEEDS-ACTION' => 'not-started',
'NEEDS ACTION' => 'not-started',
'IN-PROCESS' => 'ongoing',
'IN PROCESS' => 'ongoing',
'COMPLETED' => 'done',
'CANCELLED' => 'cancelled',
);
@ -1285,4 +1289,128 @@ class infolog_bo
}
return 'ongoing';
}
/**
* Get the Parent ID of an InfoLog entry
*
* @param string $_guid
* @return string parentID
*/
function getParentID($_guid)
{
#Horde::logMessage("getParentID($_guid)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$parentID = False;
$myfilter = array('col_filter' => array('info_uid'=>$_guid)) ;
if ($_guid && ($found=$this->search($myfilter)) && ($uidmatch = array_shift($found))) {
$parentID = $uidmatch['info_id'];
};
return $parentID;
}
/**
* Try to find a matching db entry
*
* @param array $egwData the vTODO 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)
*/
function findVTODO($egwData, $relax=false)
{
$myfilter = array('col_filter' => array('info_uid'=>$egwData['info_uid'])) ;
if ($egwData['info_uid']
&& ($found = $this->search($myfilter))
&& ($uidmatch = array_shift($found)))
{
return $uidmatch['info_id'];
};
unset($egwData['info_uid']);
$filter = array();
$description = '';
if (!empty($egwData['info_des'])) {
$description = trim(preg_replace("/\r?\n?\\[[A-Z_]+:.*\\]/i", '', $egwData['info_des']));
unset($egwData['info_des']);
// 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)
{
return $found['info_id'];
}
}
unset($egwData['info_id']);
// priority does not need to match
unset($egwData['info_priority']);
$filter['col_filter'] = $egwData;
if($foundItems = $this->search($filter)) {
if(count($foundItems) > 0) {
$itemIDs = array_keys($foundItems);
return $itemIDs[0];
}
}
$filter = array();
if (!$relax && strlen($description)) {
$filter['search'] = $description;
}
$filter['col_filter'] = $egwData;
// search for date only match
unset($filter['col_filter']['info_startdate']);
unset($filter['col_filter']['info_datecompleted']);
// 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;
}
// 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($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);
}
return false;
}
}

View File

@ -331,6 +331,7 @@ class infolog_hooks
private static function all_cats()
{
$categories = new categories('','infolog');
$accountId = $GLOBALS['egw_info']['user']['account_id'];
foreach((array)$categories->return_sorted_array(0,False,'','','',true) as $cat)
{
@ -340,6 +341,14 @@ class infolog_hooks
{
$s .= ' &#9830;';
}
elseif ($cat['owner'] != $accountId)
{
$s .= '&lt;' . $GLOBALS['egw']->accounts->id2name($cat['owner'], 'account_fullname') . '&gt;';
}
elseif ($cat['access'] == 'private')
{
$s .= ' &#9829;';
}
$sel_options[$cat['id']] = $s; // 0.9.14 only
}
return $sel_options;

View File

@ -4,15 +4,14 @@
*
* @link http://www.egroupware.org
* @author Lars Kneschke <lkneschke@egroupware.org>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package infolog
* @subpackage syncml
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
require_once EGW_API_INC.'/horde/Horde/iCalendar.php';
require_once EGW_API_INC.'/horde/Horde/iCalendar/vnote.php';
require_once EGW_API_INC.'/horde/Horde/iCalendar/vtodo.php';
require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
/**
* InfoLog: Create and parse iCal's
@ -20,17 +19,24 @@ require_once EGW_API_INC.'/horde/Horde/iCalendar/vtodo.php';
*/
class infolog_ical extends infolog_bo
{
/**
* @var array conversion of the priority egw => ical
*/
var $egw_priority2vcal_priority = array(
0 => 3,
1 => 2,
2 => 1,
3 => 1,
0 => 9, // low
1 => 5, // normal
2 => 3, // high
3 => 1, // urgent
);
/**
* @var array conversion of the priority ical => egw
*/
var $vcal_priority2egw_priority = array(
1 => 2,
2 => 1,
3 => 0,
9 => 0, 8 => 0, 7 => 0, // low
6 => 1, 5 => 1, 4 => 1, 0 => 1, // normal
3 => 2, 2 => 2, // high
1 => 3, // urgent
);
/**
@ -41,6 +47,32 @@ class infolog_ical extends infolog_bo
var $productManufacturer = 'file';
var $productName = '';
/**
* Shall we use the UID extensions of the description field?
*
* @var boolean
*/
var $uidExtension = false;
/**
* Client CTCap Properties
*
* @var array
*/
var $clientProperties;
/**
* Constructor
*
* @param array $_clientProperties client properties
*/
function __construct(&$_clientProperties = array())
{
parent::__construct();
$this->clientProperties = $_clientProperties;
}
/**
* Exports one InfoLog tast to an iCalendar VTODO
*
@ -53,9 +85,36 @@ class infolog_ical extends infolog_bo
{
$taskData = $this->read($_taskID);
$taskData = $GLOBALS['egw']->translation->convert($taskData, $GLOBALS['egw']->translation->charset(), 'UTF-8');
if ($taskData['info_id_parent'])
{
$parent = $this->read($taskData['info_id_parent']);
$taskData['info_id_parent'] = $parent['info_uid'];
}
else
{
$taskData['info_id_parent'] = '';
}
$taskGUID = $GLOBALS['egw']->common->generate_uid('infolog_task',$_taskID);
if ($this->uidExtension)
{
if (!preg_match('/\[UID:.+\]/m', $taskData['info_des']))
{
$taskData['info_des'] .= "\n[UID:" . $taskData['info_uid'] . "]";
if ($taskData['info_id_parent'] != '')
{
$taskData['info_des'] .= "\n[PARENT_UID:" . $taskData['info_id_parent'] . "]";
}
}
}
if (!empty($taskData['info_cat']))
{
$cats = $this->get_categories(array($taskData['info_cat']));
$taskData['info_cat'] = $cats[0];
}
$taskData = $GLOBALS['egw']->translation->convert($taskData,
$GLOBALS['egw']->translation->charset(), 'UTF-8');
$vcal = new Horde_iCalendar;
$vcal->setAttribute('VERSION',$_version);
@ -63,35 +122,91 @@ class infolog_ical extends infolog_bo
$vevent = Horde_iCalendar::newComponent('VTODO',$vcal);
if (!isset($this->clientProperties['SUMMARY']['Size']))
{
// make SUMMARY a required field
$this->clientProperties['SUMMARY']['Size'] = 0xFFFF;
$this->clientProperties['SUMMARY']['NoTruncate'] = false;
}
// set fields that may contain non-ascii chars and encode them if necessary
foreach(array(
foreach (array(
'SUMMARY' => $taskData['info_subject'],
'DESCRIPTION' => $taskData['info_des'],
'LOCATION' => $taskData['info_location'],
'RELATED-TO' => $taskData['info_id_parent'],
'UID' => $taskData['info_uid'],
'CATEGORIES' => $taskData['info_cat'],
) as $field => $value)
{
$vevent->setAttribute($field,$value);
$options = array();
if($this->productManufacturer != 'GroupDAV' && preg_match('/([\000-\012\015\016\020-\037\075])/',$value))
if (isset($this->clientProperties[$field]['Size']))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
$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->productManufacturer != 'GroupDAV' && preg_match('/([\177-\377])/',$value))
else
{
$size = -1;
$noTruncate = false;
}
$cursize = strlen($value);
if (($size > 0) && $cursize > $size)
{
if ($noTruncate)
{
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 (empty($value) && ($size < 0 || $noTruncate)) continue;
if ($field == 'RELATED-TO')
{
$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))
{
$options['CHARSET'] = 'UTF-8';
}
if ($options) $vevent->setParameter($field, $options);
$vevent->setAttribute($field, $value, $options);
}
if($taskData['info_startdate'])
if ($taskData['info_startdate'])
{
$vevent->setAttribute('DTSTART',$taskData['info_startdate']);
if($taskData['info_enddate'])
$vevent->setAttribute('DUE',$taskData['info_enddate']);
if($taskData['info_datecompleted'])
}
if ($taskData['info_enddate'])
{
$parts = @getdate($taskData['info_enddate']);
$value = @mktime(12, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
$vevent->setAttribute('DUE', $value);
}
if ($taskData['info_datecompleted'])
{
$vevent->setAttribute('COMPLETED',$taskData['info_datecompleted']);
}
$vevent->setAttribute('DTSTAMP',time());
$vevent->setAttribute('CREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$_taskID,'add'));
$vevent->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$_taskID,'modify'));
$vevent->setAttribute('UID',$taskData['info_uid']);
$vevent->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE');
$vevent->setAttribute('STATUS',$this->status2vtodo($taskData['info_status']));
// we try to preserv the original infolog status as X-INFOLOG-STATUS, so we can restore it, if the user does not modify STATUS
@ -99,16 +214,12 @@ class infolog_ical extends infolog_bo
$vevent->setAttribute('PERCENT-COMPLETE',$taskData['info_percent']);
$vevent->setAttribute('PRIORITY',$this->egw_priority2vcal_priority[$taskData['info_priority']]);
if (!empty($taskData['info_cat']))
{
$cats = $this->get_categories(array($taskData['info_cat']));
$vevent->setAttribute('CATEGORIES', $cats[0]);
}
//error_log("\n\nexportvcal\n". print_r($vcal,true));
//error_log("\n\nexportvcal\n". print_r($vevent,true));
$vcal->addComponent($vevent);
error_log("\n\nexportvcal from infolog\n");
return $vcal->exportvCalendar();
$retval = $vcal->exportvCalendar();
Horde::logMessage("exportVTODO:\n" . print_r($retval, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $retval;
}
/**
@ -116,104 +227,143 @@ class infolog_ical extends infolog_bo
*
* @param string $_vcalData
* @param int $_taskID=-1 info_id, default -1 = new entry
* @param boolean $merge=false merge data with existing entry
* @return int|boolean integer info_id or false on error
*/
function importVTODO(&$_vcalData, $_taskID=-1)
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']))
if (empty($taskData['info_status']))
{
$taskData['info_status'] = 'not-started';
}
if (empty($taskData['info_datecompleted']))
{
$taskData['info_datecompleted'] = 0;
}
return $this->write($taskData);
}
function searchVTODO($_vcalData, $contentID=null)
{
if(!$egwData = $this->vtodotoegw($_vcalData)) {
return false;
}
/**
* Search a matching infolog entry for the VTODO data
*
* @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
*/
function searchVTODO($_vcalData, $contentID=null, $relax=false) {
$result = false;
$myfilter = array('col_filter' => array('info_uid'=>$egwData['info_uid'])) ;
if ($egwData['info_uid'] && ($found=parent::search($myfilter)) && ($uidmatch = array_shift($found)))
if (($egwData = $this->vtodotoegw($_vcalData)))
{
if ($contentID)
{
return $uidmatch['info_id'];
};
unset($egwData['info_uid']);
if ($contentID) {
$egwData['info_id'] = $contentID;
}
#unset($egwData['info_priority']);
$filter = array('col_filter' => $egwData);
if($foundItems = $this->search($filter)) {
if(count($foundItems) > 0) {
$itemIDs = array_keys($foundItems);
return $itemIDs[0];
$result = $this->findVTODO($egwData, $relax);
}
return $result;
}
return false;
}
function vtodotoegw($_vcalData,$_taskID=-1)
/**
* Convert VTODO into a eGW infolog entry
*
* @param string $_vcalData VTODO data
* @param int $_taskID=-1 infolog_id of the entry
* @return array infolog entry or false on error
*/
function vtodotoegw($_vcalData, $_taskID=-1)
{
$vcal = new Horde_iCalendar;
if(!$vcal->parsevCalendar($_vcalData))
if (!($vcal->parsevCalendar($_vcalData))) return false;
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
return FALSE;
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
}
else
{
$minimum_uid_length = 8;
}
$components = $vcal->getComponents();
if(count($components) > 0)
foreach ($components as $component)
{
$component = $components[0];
if(is_a($component, 'Horde_iCalendar_vtodo'))
if (is_a($component, 'Horde_iCalendar_vtodo'))
{
$taskData = array();
if($_taskID > 0)
$taskData['info_type'] = 'task';
if ($_taskID > 0)
{
$taskData['info_id'] = $_taskID;
}
foreach($component->_attributes as $attributes)
foreach ($component->_attributes as $attributes)
{
switch($attributes['name'])
//$attributes['value'] = trim($attributes['value']);
if (empty($attributes['value'])) continue;
switch ($attributes['name'])
{
case 'CLASS':
$taskData['info_access'] = strtolower($attributes['value']);
break;
case 'DESCRIPTION':
$taskData['info_des'] = $attributes['value'];
$value = $attributes['value'];
if (preg_match('/\s*\[UID:(.+)?\]/Usm', $value, $matches))
{
if (!isset($taskData['info_uid'])
&& strlen($matches[1]) >= $minimum_uid_length)
{
$taskData['info_uid'] = $matches[1];
}
//$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)
{
$taskData['info_id_parent'] = $this->getParentID($matches[1]);
}
//$value = str_replace($matches[0], '', $value);
}
$taskData['info_des'] = $value;
break;
case 'LOCATION':
$taskData['info_location'] = $attributes['value'];
break;
case 'DUE':
$taskData['info_enddate'] = $attributes['value'];
// 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 'COMPLETED':
$taskData['info_datecompleted'] = $attributes['value'];
break;
case 'DTSTART':
$taskData['info_startdate'] = $attributes['value'];
break;
case 'PRIORITY':
if (1 <= $attributes['value'] && $attributes['value'] <= 3)
{
if (1 <= $attributes['value'] && $attributes['value'] <= 3) {
$taskData['info_priority'] = $this->vcal_priority2egw_priority[$attributes['value']];
}
else
{
} 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)
@ -223,28 +373,26 @@ class infolog_ical extends infolog_bo
$taskData['info_status'] = $this->vtodo2status($attributes['value'],
$attr['name'] == 'X-INFOLOG-STATUS' ? $attr['value'] : null);
break;
case 'SUMMARY':
$taskData['info_subject'] = $attributes['value'];
break;
case 'RELATED-TO':
$taskData['info_id_parent'] = $this->getParentID($attributes['value']);
break;
case 'CATEGORIES':
$cats = $this->find_or_add_categories(explode(',', $attributes['value']));
$taskData['info_cat'] = $cats[0];
break;
case 'UID':
$taskData['info_uid'] = $attributes['value'];
if ($_taskID <= 0 && !empty($attributes['value']) && ($uid_task = $this->read($attributes['value'])))
{
$taskData['info_id'] = $uid_task['id'];
unset($uid_task);
}
// not use weak uids that might come from syncml clients
if (isset($event['uid']) && (strlen($event['uid']) < 20 || is_numeric($event['uid'])))
{
unset ($event['uid']);
}
case 'UID':
if (strlen($attributes['value']) >= $minimum_uid_length) {
$taskData['info_uid'] = $attributes['value'];
}
break;
case 'PERCENT-COMPLETE':
$taskData['info_percent'] = (int) $attributes['value'];
break;
@ -254,18 +402,28 @@ class infolog_ical extends infolog_bo
# 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;
}
}
return FALSE;
return false;
}
/**
* Export an infolog entry as VNOTE
*
* @param int $_noteID the infolog_id of the entry
* @param string $_type content type (e.g. text/plain)
* @return string VNOTE representation of the infolog entry
*/
function exportVNOTE($_noteID, $_type)
{
$note = $this->read($_noteID);
$note = $GLOBALS['egw']->translation->convert($note, $GLOBALS['egw']->translation->charset(), 'UTF-8');
$note = $GLOBALS['egw']->translation->convert($note,
$GLOBALS['egw']->translation->charset(), 'UTF-8');
switch($_type)
switch ($_type)
{
case 'text/plain':
$txt = $note['info_subject']."\n\n".$note['info_des'];
@ -273,66 +431,101 @@ class infolog_ical extends infolog_bo
break;
case 'text/x-vnote':
$noteGUID = $GLOBALS['egw']->common->generate_uid('infolog_note',$_noteID);
$vnote = new Horde_iCalendar_vnote();
$options = array('CHARSET' => 'UTF-8');
$vNote->setAttribute('VERSION', '1.1');
$vnote->setAttribute('SUMMARY',$note['info_subject']);
$vnote->setAttribute('BODY',$note['info_des']);
if($note['info_startdate'])
foreach (array( 'SUMMARY' => $note['info_subject'],
'BODY' => $note['info_des'],
) as $field => $value)
{
$vnote->setAttribute($field, $value);
if ($this->productManufacturer != 'groupdav'
&& preg_match('/([\177-\377])/', $value))
{
$vevent->setParameter($field, $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']))
{
$cats = $this->get_categories(array($note['info_cat']));
$vnote->setAttribute('CATEGORIES', $cats[0]);
$value = $cats[0];
$vnote->setAttribute('CATEGORIES', $value);
if ($this->productManufacturer != 'groupdav'
&& preg_match('/([\177-\377])/', $value))
{
$vevent->setParameter('CATEGORIES', $options);
}
}
#$vnote->setAttribute('UID',$noteGUID);
#$vnote->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE');
#$options = array('CHARSET' => 'UTF-8','ENCODING' => 'QUOTED-PRINTABLE');
#$vnote->setParameter('SUMMARY', $options);
#$vnote->setParameter('DESCRIPTION', $options);
return $vnote->exportvCalendar();
break;
}
return false;
}
function importVNOTE(&$_vcalData, $_type, $_noteID = -1)
/**
* Import a VNOTE component of an iCal
*
* @param string $_vcalData
* @param string $_type content type (eg.g text/plain)
* @param int $_taskID=-1 info_id, default -1 = new entry
* @param boolean $merge=false merge data with existing entry
* @return int|boolean integer info_id or false on error
*/
function importVNOTE(&$_vcalData, $_type, $_noteID = -1, $merge=false)
{
if(!$note = $this->vnotetoegw($_vcalData, $_type))
{
return false;
}
if (!($note = $this->vnotetoegw($_vcalData, $_type))) return false;
if($_noteID > 0)
{
$note['info_id'] = $_noteID;
}
if($_noteID > 0) $note['info_id'] = $_noteID;
if(empty($note['info_status'])) {
$note['info_status'] = 'done';
}
if (empty($note['info_status'])) $note['info_status'] = 'done';
#_debug_array($taskData);exit;
return $this->write($note);
}
/**
* Search a matching infolog entry for the VNOTE data
*
* @param string $_vcalData VNOTE
* @param int $contentID=null infolog_id (or null, if unkown)
* @return infolog_id of a matching entry or false, if nothing was found
*/
function searchVNOTE($_vcalData, $_type, $contentID=null)
{
if(!$note = $this->vnotetoegw($_vcalData,$_type)) {
return false;
if (!($note = $this->vnotetoegw($_vcalData,$_type))) return false;
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;
}
if ($contentID) {
$note['info_id'] = $contentID;
}
$filter = array('col_filter' => $note);
if($foundItems = $this->search($filter)) {
if(count($foundItems) > 0) {
$filter['col_filter'] = $note;
if (($foundItems = $this->search($filter)))
{
if (count($foundItems) > 0)
{
$itemIDs = array_keys($foundItems);
return $itemIDs[0];
}
@ -341,9 +534,16 @@ class infolog_ical extends infolog_bo
return false;
}
/**
* Convert VTODO into a eGW infolog entry
*
* @param string $_data VNOTE data
* @param string $_type content type (eg.g text/plain)
* @return array infolog entry or false on error
*/
function vnotetoegw($_data, $_type)
{
switch($_type)
switch ($_type)
{
case 'text/plain':
$note = array();
@ -360,7 +560,7 @@ class infolog_ical extends infolog_bo
else
{
// should better be imported as subject, but causes duplicates
// TODO: should be qexamined
// TODO: should be examined
$note['info_des'] = $txt;
}
@ -369,34 +569,31 @@ class infolog_ical extends infolog_bo
case 'text/x-vnote':
$vnote = new Horde_iCalendar;
if (!$vcal->parsevCalendar($_data))
{
return FALSE;
}
if (!$vcal->parsevCalendar($_data)) return false;
$components = $vnote->getComponent();
if(count($components) > 0)
foreach ($components as $component)
{
$component = $components[0];
if(is_a($component, 'Horde_iCalendar_vnote'))
if (is_a($component, 'Horde_iCalendar_vnote'))
{
$note = array();
$note['info_type'] = 'note';
foreach($component->_attributes as $attribute)
foreach ($component->_attributes as $attribute)
{
switch ($attribute['name'])
{
case 'BODY':
$note['info_des'] = $attribute['value'];
break;
case 'SUMMARY':
$note['info_subject'] = $attribute['value'];
break;
case 'CATEGORIES':
{
$cats = $this->find_or_add_categories(explode(',', $attribute['value']));
$note['info_cat'] = $cats[0];
}
break;
}
}
@ -404,7 +601,7 @@ class infolog_ical extends infolog_bo
return $note;
}
}
return FALSE;
return false;
}
/**
@ -415,11 +612,41 @@ class infolog_ical extends infolog_bo
* @param string $_productManufacturer
* @param string $_productName
*/
function setSupportedFields($_productManufacturer='file', $_productName='')
function setSupportedFields($_productManufacturer='', $_productName='')
{
// save them vor later use
$this->productManufacturer = $_productManufacturer;
$this->productName = $_productName;
$state = &$_SESSION['SyncML.state'];
if (isset($state))
{
$deviceInfo = $state->getClientDeviceInfo();
}
// store product manufacturer and name, to be able to use it elsewhere
if ($_productManufacturer)
{
$this->productManufacturer = strtolower($_productManufacturer);
$this->productName = strtolower($_productName);
}
if (isset($deviceInfo) && is_array($deviceInfo))
{
if (!isset($this->productManufacturer)
|| $this->productManufacturer == ''
|| $this->productManufacturer == 'file')
{
$this->productManufacturer = strtolower($deviceInfo['manufacturer']);
}
if (!isset($this->productName) || $this->productName == '')
{
$this->productName = strtolower($deviceInfo['model']);
}
if (isset($deviceInfo['uidExtension'])
&& $deviceInfo['uidExtension'])
{
$this->uidExtension = true;
}
}
Horde::logMessage('setSupportedFields(' . $this->productManufacturer . ', ' . $this->productName .')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
}

View File

@ -4,13 +4,14 @@
*
* @link http://www.egroupware.org
* @author Lars Kneschke <lkneschke@egroupware.org>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package infolog
* @subpackage syncml
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
require_once EGW_API_INC.'/horde/Horde/iCalendar.php';
require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
/**
* InfoLog: Create and parse SIF
@ -43,7 +44,7 @@ class infolog_sif extends infolog_bo
'Body' => 'info_des',
'Categories' => 'info_cat',
'Companies' => '',
'Complete' => '',
'Complete' => 'complete',
'DateCompleted' => 'info_datecompleted',
'DueDate' => 'info_enddate',
'Importance' => 'info_priority',
@ -70,26 +71,56 @@ class infolog_sif extends infolog_bo
'Occurrences' => '',
);
// standard headers
const xml_decl = '<?xml version="1.0" encoding="UTF-8"?>';
const SIF_decl = '<SIFVersion>1.1</SIFVersion>';
/**
* name and version of the sync-client
*
* @var string
*/
var $productName = 'mozilla plugin';
var $productSoftwareVersion = '0.3';
/**
* Shall we use the UID extensions of the description field?
*
* @var boolean
*/
var $uidExtension = false;
function startElement($_parser, $_tag, $_attributes) {
function startElement($_parser, $_tag, $_attributes)
{
// nothing to do
}
function endElement($_parser, $_tag) {
error_log("infolog: tag=$_tag data=".trim($this->sifData));
if(!empty($this->_currentSIFMapping[$_tag])) {
function endElement($_parser, $_tag)
{
#error_log("infolog: tag=$_tag data=".trim($this->sifData));
if (!empty($this->_currentSIFMapping[$_tag]))
{
$this->_extractedSIFData[$this->_currentSIFMapping[$_tag]] = trim($this->sifData);
}
unset($this->sifData);
}
function characterData($_parser, $_data) {
function characterData($_parser, $_data)
{
$this->sifData .= $_data;
}
function siftoegw($_sifData, $_sifType) {
/**
* Convert SIF data into a eGW infolog entry
*
* @param string $sifData the SIF data
* @param string $_sifType type (note/task)
* @return array infolog entry or false on error
*/
function siftoegw($sifData, $_sifType)
{
$sysCharSet = $GLOBALS['egw']->translation->charset();
$sifData = base64_decode($_sifData);
#$tmpfname = tempnam('/tmp/sync/contents','sift_');
@ -115,29 +146,44 @@ class infolog_sif extends infolog_bo
xml_set_element_handler($this->xml_parser, "startElement", "endElement");
xml_set_character_data_handler($this->xml_parser, "characterData");
$this->strXmlData = xml_parse($this->xml_parser, $sifData);
if(!$this->strXmlData) {
if (!$this->strXmlData)
{
error_log(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->xml_parser)),
xml_get_current_line_number($this->xml_parser)));
return false;
}
if(!array($this->_extractedSIFData)) {
return false;
}
if (!array($this->_extractedSIFData)) return false;
switch($_sifType) {
switch ($_sifType)
{
case 'task':
$taskData = array();
$vcal = new Horde_iCalendar;
$taskData['info_type'] = 'task';
$taskData['info_status'] = 'not-started';
foreach($this->_extractedSIFData as $key => $value) {
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
}
else
{
$minimum_uid_length = 8;
}
foreach ($this->_extractedSIFData as $key => $value)
{
$value = preg_replace('/<\!\[CDATA\[(.+)\]\]>/Usim', '$1', $value);
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
error_log("infolog key=$key => value=$value");
#error_log("infolog key=$key => value=$value");
if (empty($value)) continue;
switch($key) {
switch($key)
{
case 'info_access':
$taskData[$key] = ((int)$value > 0) ? 'private' : 'public';
break;
@ -145,19 +191,18 @@ class infolog_sif extends infolog_bo
case 'info_datecompleted':
case 'info_enddate':
case 'info_startdate':
if(!empty($value)) {
if (!empty($value))
{
$taskData[$key] = $vcal->_parseDateTime($value);
// somehow the client always deliver a timestamp about 3538 seconds, when no startdate set.
if($taskData[$key] < 10000)
$taskData[$key] = '';
} else {
$taskData[$key] = '';
if ($taskData[$key] < 10000) unset($taskData[$key]);
}
break;
case 'info_cat':
if (!empty($value)) {
if (!empty($value))
{
$categories = $this->find_or_add_categories(explode(';', $value));
$taskData['info_cat'] = $categories[0];
}
@ -168,8 +213,8 @@ class infolog_sif extends infolog_bo
break;
case 'info_status':
$taskData[$key] = ((int)$value == 2) ? 'done' : 'ongoing';
switch($value) {
switch ($value)
{
case '0':
$taskData[$key] = 'not-started';
break;
@ -178,9 +223,20 @@ class infolog_sif extends infolog_bo
break;
case '2':
$taskData[$key] = 'done';
$taskData['info_percent'] = 100;
break;
case '3':
$taskData[$key] = 'waiting';
break;
case '4':
if ($this->productName == 'blackberry plug-in')
{
$taskData[$key] = 'deferred';
}
else
{
$taskData[$key] = 'cancelled';
}
break;
default:
$taskData[$key] = 'ongoing';
@ -188,11 +244,35 @@ class infolog_sif extends infolog_bo
}
break;
case 'complete':
$taskData['info_status'] = 'done';
$taskData['info_percent'] = 100;
break;
case 'info_des':
// extract our UID and PARENT_UID information
if (preg_match('/\s*\[UID:(.+)?\]/Usm', $value, $matches))
{
if (strlen($matches[1]) >= $minimum_uid_length)
{
$taskData['info_uid'] = $matches[1];
}
//$value = str_replace($matches[0], '', $value);
}
if (preg_match('/\s*\[PARENT_UID:(.+)?\]/Usm', $value, $matches))
{
if (strlen($matches[1]) >= $minimum_uid_length)
{
$taskData['info_id_parent'] = $this->getParentID($matches[1]);
}
//$value = str_replace($matches[0], '', $value);
}
default:
$taskData[$key] = $value;
break;
}
error_log("infolog task key=$key => value=".$taskData[$key]);
#error_log("infolog task key=$key => value=" . $taskData[$key]);
}
return $taskData;
@ -203,28 +283,32 @@ class infolog_sif extends infolog_bo
$noteData['info_type'] = 'note';
$vcal = new Horde_iCalendar;
foreach($this->_extractedSIFData as $key => $value)
foreach ($this->_extractedSIFData as $key => $value)
{
$value = preg_replace('/<\!\[CDATA\[(.+)\]\]>/Usim', '$1', $value);
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
error_log("infolog client key=$key => value=".$value);
#error_log("infolog client key=$key => value=" . $value);
switch ($key)
{
case 'info_startdate':
if(!empty($value)) {
if (!empty($value))
{
$noteData[$key] = $vcal->_parseDateTime($value);
// somehow the client always deliver a timestamp about 3538 seconds, when no startdate set.
if($noteData[$key] < 10000)
$noteData[$key] = '';
} else {
if ($noteData[$key] < 10000) $noteData[$key] = '';
}
else
{
$noteData[$key] = '';
}
break;
case 'info_cat':
if (!empty($value)) {
if (!empty($value))
{
$categories = $this->find_or_add_categories(explode(';', $value));
$taskData['info_cat'] = $categories[0];
$noteData['info_cat'] = $categories[0];
}
break;
@ -232,7 +316,7 @@ class infolog_sif extends infolog_bo
$noteData[$key] = $value;
break;
}
error_log("infolog note key=$key => value=".$noteData[$key]);
#error_log("infolog note key=$key => value=".$noteData[$key]);
}
return $noteData;
break;
@ -243,17 +327,33 @@ class infolog_sif extends infolog_bo
}
}
function searchSIF($_sifData, $_sifType, $contentID=null) {
if(!$egwData = $this->siftoegw($_sifData, $_sifType)) {
return false;
}
if ($contentID) {
$egwData['info_id'] = $contentID;
}
/**
* Search for SIF data a matching infolog entry
*
* @param string $sifData the SIF data
* @param string $_sifType type (note/task)
* @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 searchSIF($_sifData, $_sifType, $contentID=null, $relax=false)
{
if (!($egwData = $this->siftoegw($_sifData, $_sifType))) return false;
$filter = array('col_filter' => $egwData);
if($foundItems = $this->search($filter)) {
if(count($foundItems) > 0) {
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];
}
@ -262,46 +362,119 @@ class infolog_sif extends infolog_bo
return false;
}
function addSIF($_sifData, $_id, $_sifType) {
if(!$egwData = $this->siftoegw($_sifData, $_sifType)) {
return false;
}
/**
* Add SIF data entry
*
* @param string $sifData the SIF data
* @param string $_sifType type (note/task)
* @param boolean $merge=false reserved for future use
* @return infolog_id of the new entry or false, for errors
*/
function addSIF($_sifData, $_id, $_sifType, $merge=false)
{
if (!($egwData = $this->siftoegw($_sifData, $_sifType))) return false;
if($_id > 0)
$egwData['info_id'] = $_id;
if ($_id > 0) $egwData['info_id'] = $_id;
if (empty($taskData['info_datecompleted']))
{
$taskData['info_datecompleted'] = 0;
}
$egwID = $this->write($egwData, false);
return $egwID;
}
function getSIF($_id, $_sifType) {
switch($_sifType) {
case 'task':
if($taskData = $this->read($_id)) {
$sysCharSet = $GLOBALS['egw']->translation->charset();
$vcal = new Horde_iCalendar;
$sifTask = '<task>';
foreach($this->_sifTaskMapping as $sifField => $egwField)
/**
* Export an infolog entry as SIF data
*
* @param int $_id the infolog_id of the entry
* @param string $_sifType type (note/task)
* @return string SIF representation of the infolog entry
*/
function getSIF($_id, $_sifType)
{
if(empty($egwField)) continue;
$sysCharSet = $GLOBALS['egw']->translation->charset();
switch($_sifType)
{
case 'task':
if (($taskData = $this->read($_id)))
{
$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<task>" . 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) {
switch ($sifField)
{
case 'Complete':
// is handled with DateCompleted
break;
case 'DateCompleted':
case 'DueDate':
case 'StartDate':
if(!empty($value)) {
$value = $vcal->_exportDateTime($value);
if ($taskData[info_status] == 'done')
{
$sifTask .= "<Complete>1</Complete>";
}
else
{
$sifTask .= "<DateCompleted></DateCompleted><Complete>0</Complete>";
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;
if ($value > 3) $value = 3;
$sifTask .= "<$sifField>$value</$sifField>";
break;
@ -311,20 +484,26 @@ class infolog_sif extends infolog_bo
break;
case 'Status':
switch($value) {
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;
case 'ongoing':
$value = '1';
break;
default:
default: //ongoing
$value = 1;
break;
}
@ -332,102 +511,69 @@ class infolog_sif extends infolog_bo
break;
case 'Categories':
if (!empty($value))
if (!empty($value) && $value)
{
$value = implode('; ', $this->get_categories(array($value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifTask .= "<$sifField>$value</$sifField>";
else
{
break;
}
default:
$value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
$sifTask .= "<$sifField>$value</$sifField>";
break;
}
}
$sifTask .= '<ActualWork>0</ActualWork><IsRecurring>0</IsRecurring>';
return base64_encode($sifTask);
/* return base64_encode("<task>
<ActualWork>0</ActualWork>
<BillingInformation></BillingInformation>
<Body></Body>
<Categories></Categories>
<Companies></Companies>
<Complete>0</Complete>
<DateCompleted></DateCompleted>
<DueDate></DueDate>
<Importance>1</Importance>
<IsRecurring>0</IsRecurring>
<Mileage></Mileage>
<PercentComplete>0</PercentComplete>
<ReminderSet>0</ReminderSet>
<ReminderTime></ReminderTime>
<Sensitivity>0</Sensitivity>
<StartDate>45001231T230000Z</StartDate>
<Status>3</Status>
<Subject>TARAAA3</Subject>
<TeamTask>0</TeamTask>
<TotalWork>0</TotalWork>
<RecurrenceType>1</RecurrenceType>
<Interval>1</Interval>
<MonthOfYear>0</MonthOfYear>
<DayOfMonth>0</DayOfMonth>
<DayOfWeekMask>4</DayOfWeekMask>
<Instance>0</Instance>
<PatternStartDate>20060320T230000Z</PatternStartDate>
<NoEndDate>1</NoEndDate>
<PatternEndDate></PatternEndDate>
<Occurrences>10</Occurrences>
</task>
"); */
$sifTask .= '<ActualWork>0</ActualWork><IsRecurring>0</IsRecurring></task>';
return $sifTask;
}
break;
case 'note':
if($taskData = $this->read($_id)) {
$sysCharSet = $GLOBALS['egw']->translation->charset();
$vcal = new Horde_iCalendar;
if (($taskData = $this->read($_id)))
{
$vcal = new Horde_iCalendar('1.0');
$sifNote = '<note>';
$sifNote = self::xml_decl . "\n<note>" . self::SIF_decl;
foreach($this->_sifNoteMapping as $sifField => $egwField)
foreach ($this->_sifNoteMapping as $sifField => $egwField)
{
if(empty($egwField)) continue;
$value = $GLOBALS['egw']->translation->convert($taskData[$egwField], $sysCharSet, 'utf-8');
switch($sifField) {
switch ($sifField)
{
case 'Date':
if(!empty($value)) {
if (!empty($value))
{
$value = $vcal->_exportDateTime($value);
}
$sifNote .= "<$sifField>$value</$sifField>";
break;
case 'Body':
$value = $GLOBALS['egw']->translation->convert($taskData['info_subject'], $sysCharSet, 'utf-8') . "\n" . $value;
$sifNote .= "<$sifField>$value</$sifField>";
break;
case 'Categories':
if (!empty($value))
{
$value = implode('; ', $this->get_categories(array($value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifNote .= "<$sifField>$value</$sifField>";
else
{
break;
}
default:
$value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
$sifNote .= "<$sifField>$value</$sifField>";
break;
}
}
return base64_encode($sifNote);
$sifNote .= '</note>';
return $sifNote;
}
break;
@ -437,117 +583,35 @@ class infolog_sif extends infolog_bo
}
function exportVTODO($_taskID, $_version)
/**
* Set the supported fields
*
* Currently we only store name and version, manucfacturer is always Funambol
*
* @param string $_productName
* @param string $_productSoftwareVersion
*/
function setSupportedFields($_productName='', $_productSoftwareVersion='')
{
error_log(__METHOD__."called : $_version ,$_taskID");
$taskData = $this->read($_taskID);
$state = &$_SESSION['SyncML.state'];
$deviceInfo = $state->getClientDeviceInfo();
$taskData = $GLOBALS['egw']->translation->convert($taskData,$GLOBALS['egw']->translation->charset(),'UTF-8');
//_debug_array($taskData);
$taskGUID = $GLOBALS['phpgw']->common->generate_uid('infolog_task',$_taskID);
$vcal = new Horde_iCalendar;
$vcal->setAttribute('VERSION',$_version);
$vcal->setAttribute('METHOD','PUBLISH');
$vevent = Horde_iCalendar::newComponent('VTODO',$vcal);
$options = array();
$vevent->setAttribute('SUMMARY',$taskData['info_subject']);
$vevent->setAttribute('DESCRIPTION',$taskData['info_des']);
if($taskData['info_startdate'])
$vevent->setAttribute('DTSTART',$taskData['info_startdate']);
if($taskData['info_enddate'])
$vevent->setAttribute('DUE',$taskData['info_enddate']);
$vevent->setAttribute('DTSTAMP',time());
$vevent->setAttribute('CREATED',$GLOBALS['phpgw']->contenthistory->getTSforAction('infolog_task',$_taskID,'add'));
$vevent->setAttribute('LAST-MODIFIED',$GLOBALS['phpgw']->contenthistory->getTSforAction('infolog_task',$_taskID,'modify'));
$vevent->setAttribute('UID',$taskGUID);
$vevent->setAttribute('CLASS',(($taskData['info_access'] == 'public')?'PUBLIC':'PRIVATE'));
$vevent->setAttribute('STATUS',(($taskData['info_status'] == 'completed')?'COMPLETED':'NEEDS-ACTION'));
// 3=urgent => 1, 2=high => 2, 1=normal => 3, 0=low => 4
$vevent->setAttribute('PRIORITY',4-$taskData['info_priority']);
#$vevent->setAttribute('TRANSP','OPAQUE');
# status
# ATTENDEE
$options = array('CHARSET' => 'UTF-8','ENCODING' => 'QUOTED-PRINTABLE');
$vevent->setParameter('SUMMARY', $options);
$vevent->setParameter('DESCRIPTION', $options);
$vcal->addComponent($vevent);
#print "<pre>";
#print $vcal->exportvCalendar();
#print "</pre>";
return $vcal->exportvCalendar();
}
function importVTODO(&$_vcalData, $_taskID=-1)
if (isset($deviceInfo) && is_array($deviceInfo))
{
$botranslation = CreateObject('phpgwapi.translation');
$vcal = new Horde_iCalendar;
if(!$vcal->parsevCalendar($_vcalData))
if (isset($deviceInfo['uidExtension']) &&
$deviceInfo['uidExtension'])
{
return FALSE;
}
$components = $vcal->getComponents();
if(count($components) > 0)
{
$component = $components[0];
if(is_a($component, 'Horde_iCalendar_vtodo'))
{
if($_taskID>0)
$taskData['info_id'] = $_taskID;
foreach($component->_attributes as $attributes)
{
#print $attributes['name'].' - '.$attributes['value'].'<br>';
#$attributes['value'] = $GLOBALS['egw']->translation->convert($attributes['value'],'UTF-8');
switch($attributes['name'])
{
case 'CLASS':
$taskData['info_access'] = strtolower($attributes['value']);
break;
case 'DESCRIPTION':
$taskData['info_des'] = $attributes['value'];
break;
case 'DUE':
$taskData['info_enddate'] = $attributes['value'];
break;
case 'DTSTART':
$taskData['info_startdate'] = $attributes['value'];
break;
case 'PRIORITY':
// 1 => 3=urgent, 2 => 2=high, 3 => 1=normal, 4 => 0=low
if (1 <= $attributes['value'] && $attributes['value'] <= 4)
{
$taskData['info_priority'] = 4 - $attributes['value'];
}
else
{
$taskData['info_priority'] = 1; // default = normal
}
break;
case 'STATUS':
$taskData['info_status'] = (strtolower($attributes['value']) == 'completed') ? 'done' : 'ongoing';
break;
case 'SUMMARY':
$taskData['info_subject'] = $attributes['value'];
break;
$this->uidExtension = true;
}
}
#_debug_array($eventData);exit;
return $this->write($taskData);
// store product name and version, to be able to use it elsewhere
if ($_productName)
{
$this->productName = strtolower($_productName);
if (preg_match('/^[^\d]*(\d+\.?\d*)[\.|\d]*$/', $_productSoftwareVersion, $matches))
{
$this->productSoftwareVersion = $matches[1];
}
}
return FALSE;
}
}

View File

@ -344,6 +344,12 @@ class infolog_so
*/
function read($info_id) // did _not_ ensure ACL
{
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {
$minimum_uid_length = 8;
}
//echo "<p>read($info_id) ".function_backtrace()."</p>\n";
if ($info_id && ((int)$info_id == $this->data['info_id'] || $info_id == $this->data['info_uid']))
{
@ -356,6 +362,14 @@ class infolog_so
$this->init( );
return False;
}
if (!$this->data['info_uid'] || strlen($this->data['info_uid']) < $minimum_uid_length) {
// entry without uid --> create one based on our info_id and save it
$this->data['info_uid'] = $GLOBALS['egw']->common->generate_uid('infolog', $info_id);
$this->db->update($this->info_table,
array('info_uid' => $this->data['info_uid']),
array('info_id' => $this->data['info_id']), __LINE__,__FILE__);
}
if (!is_array($this->data['info_responsible']))
{
$this->data['info_responsible'] = $this->data['info_responsible'] ? explode(',',$this->data['info_responsible']) : array();
@ -499,6 +513,12 @@ class infolog_so
*/
function write($values,$check_modified=0) // did _not_ ensure ACL
{
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {
$minimum_uid_length = 8;
}
//echo "soinfolog::write(,$check_modified) values="; _debug_array($values);
$info_id = (int) $values['info_id'];
@ -540,12 +560,17 @@ class infolog_so
$this->db->insert($this->info_table,$to_write,false,__LINE__,__FILE__);
$info_id = $this->data['info_id'] = $this->db->get_last_insert_id($this->info_table,'info_id');
if (!$this->data['info_uid']) // new entry without uid --> create one based on our info_id and save it
{
$this->data['info_uid'] = $GLOBALS['egw']->common->generate_uid('infolog',$info_id);
$this->db->update($this->info_table,array('info_uid'=>$this->data['info_uid']),array('info_id'=>$info_id),__LINE__,__FILE__);
}
if (!$this->data['info_uid'] || strlen($this->data['info_uid']) < $minimum_uid_length) {
// entry without uid --> create one based on our info_id and save it
$this->data['info_uid'] = $GLOBALS['egw']->common->generate_uid('infolog', $info_id);
$this->db->update($this->info_table,
array('info_uid' => $this->data['info_uid']),
array('info_id' => $info_id), __LINE__,__FILE__);
}
//echo "<p>soinfolog.write values= "; _debug_array($values);
// write customfields now
@ -775,6 +800,7 @@ class infolog_so
if ($action == '' || $action == 'sp' || count($links))
{
$sql_query = "FROM $this->info_table main $join WHERE ($filtermethod $pid $sql_query) $link_extra";
#error_log("infolog.so.search:\n" . print_r($sql_query, true));
if ($this->db->Type == 'mysql' && $this->db->ServerInfo['version'] >= 4.0)
{

View File

@ -186,6 +186,7 @@ class infolog_tracking extends bo_tracking
*/
function get_details($data)
{
$header_done = false;
$responsible = array();
if ($data['info_responsible'])
{

View File

@ -654,6 +654,7 @@ class infolog_ui
*
* @param int|array $values=0 info_id (default _GET[info_id])
* @param string $referer=''
* @param boolean $closesingle=false
*/
function close($values=0,$referer='',$closesingle=false)
{
@ -973,6 +974,7 @@ class infolog_ui
}
$parent = $this->bo->so->data;
$content['info_id'] = $info_id = 0;
$content['info_uid'] = ''; // ensure that we have our own UID
$content['info_owner'] = $this->user;
$content['info_id_parent'] = $parent['info_id'];
/*
@ -1432,7 +1434,6 @@ class infolog_ui
#continue;
}
$message .= $bofelamimail->wordwrap($value,75,"\n");
#$message .= $bodyAppend;
}
}