forked from extern/egroupware
Major SyncML Calendar update
- SIFE support improved - various vCalendar 1.0 issues fixed - device specific timezone support for recurring events - pseudo exception handling improvements
This commit is contained in:
parent
61d26df913
commit
32639bd47e
@ -664,18 +664,19 @@ class calendar_bo
|
|||||||
{
|
{
|
||||||
$this->debug_message('bocal::set_recurrences(%1,%2)',true,$event,$start);
|
$this->debug_message('bocal::set_recurrences(%1,%2)',true,$event,$start);
|
||||||
}
|
}
|
||||||
// check if the caller gave the event start and end times and if not read them from the DB
|
// check if the caller gave us enough information and if not read it from the DB
|
||||||
|
if (!isset($event['participants']) || !isset($event['start']) || !isset($event['end']))
|
||||||
|
{
|
||||||
|
list(,$event_read) = each($this->so->read($event['id']));
|
||||||
|
if (!isset($event['participants']))
|
||||||
|
{
|
||||||
|
$event['participants'] = $event_read['participants'];
|
||||||
|
}
|
||||||
if (!isset($event['start']) || !isset($event['end']))
|
if (!isset($event['start']) || !isset($event['end']))
|
||||||
{
|
{
|
||||||
$event_read=$this->read($event['id']);
|
|
||||||
$event['start'] = $event_read['start'];
|
$event['start'] = $event_read['start'];
|
||||||
$event['end'] = $event_read['end'];
|
$event['end'] = $event_read['end'];
|
||||||
}
|
}
|
||||||
// check if the caller gave the participants and if not read them from the DB
|
|
||||||
if (!isset($event['participants']))
|
|
||||||
{
|
|
||||||
list(,$event_read) = each($this->so->read($event['id']));
|
|
||||||
$event['participants'] = $event_read['participants'];
|
|
||||||
}
|
}
|
||||||
if (!$start) $start = $event['start'];
|
if (!$start) $start = $event['start'];
|
||||||
|
|
||||||
@ -708,7 +709,6 @@ class calendar_bo
|
|||||||
foreach($events as $id => &$event)
|
foreach($events as $id => &$event)
|
||||||
{
|
{
|
||||||
// convert timezone id of event to tzid (iCal id like 'Europe/Berlin')
|
// convert timezone id of event to tzid (iCal id like 'Europe/Berlin')
|
||||||
unset($event_timezone);
|
|
||||||
if (!$event['tz_id'] || !($event['tzid'] = calendar_timezones::id2tz($event['tz_id'])))
|
if (!$event['tz_id'] || !($event['tzid'] = calendar_timezones::id2tz($event['tz_id'])))
|
||||||
{
|
{
|
||||||
$event['tzid'] = egw_time::$server_timezone->getName();
|
$event['tzid'] = egw_time::$server_timezone->getName();
|
||||||
|
@ -61,9 +61,11 @@ class calendar_boupdate extends calendar_bo
|
|||||||
var $debug;
|
var $debug;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|boolean $log_file filename to enable the login or false for no update-logging
|
* Set Logging
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
var $log_file = false;
|
var $log = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -1042,6 +1044,11 @@ class calendar_boupdate extends calendar_bo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
calendar_so::split_status($status, $quantity, $role);
|
calendar_so::split_status($status, $quantity, $role);
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"($cal_id, $uid, $status, $recur_date)");
|
||||||
|
}
|
||||||
if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],is_numeric($uid)?$uid:substr($uid,1),$status,$recur_date ? $this->date2ts($recur_date,true) : 0,$role)))
|
if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],is_numeric($uid)?$uid:substr($uid,1),$status,$recur_date ? $this->date2ts($recur_date,true) : 0,$role)))
|
||||||
{
|
{
|
||||||
if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time());
|
if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time());
|
||||||
@ -1407,111 +1414,430 @@ class calendar_boupdate extends calendar_bo
|
|||||||
* Try to find a matching db entry
|
* Try to find a matching db entry
|
||||||
*
|
*
|
||||||
* @param array $event the vCalendar data we try to find
|
* @param array $event the vCalendar data we try to find
|
||||||
* @param boolean $relax=false if asked to relax, we only match against some key fields
|
* @param string filter='exact' exact -> check for identical matches
|
||||||
* @return the calendar_id of the matching entry or false (if none matches)
|
* relax -> be more tolerant
|
||||||
|
* master -> try to find a releated series master
|
||||||
|
* @return array calendar_id's of matching entries
|
||||||
*/
|
*/
|
||||||
function find_event($event, $relax=false)
|
function find_event($event, $filter='exact')
|
||||||
{
|
{
|
||||||
|
$matchingEvents = array();
|
||||||
$query = array();
|
$query = array();
|
||||||
if (isset($event['start']))
|
// unset($event['uid']);
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
{
|
{
|
||||||
$query[] = 'cal_start='.$event['start'];
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
}
|
"($filter)[EVENT]:" . array2string($event));
|
||||||
if (isset($event['end']))
|
|
||||||
{
|
|
||||||
$query[] = 'cal_end='.$event['end'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (array('title', 'location',
|
if ($filter == 'master')
|
||||||
'public', 'non_blocking', 'category') as $key)
|
|
||||||
{
|
{
|
||||||
if (!empty($event[$key])) $query['cal_'.$key] = $event[$key];
|
$recur_date = 0;
|
||||||
}
|
$event['recurrence'] = 0;
|
||||||
|
|
||||||
if ($event['uid'] && ($uidmatch = $this->read($event['uid'])))
|
|
||||||
{
|
|
||||||
if ($event['recurrence'])
|
|
||||||
{
|
|
||||||
// Let's try to find a real exception first
|
|
||||||
$query['cal_uid'] = $event['uid'];
|
|
||||||
$query['cal_recurrence'] = $event['recurrence'];
|
|
||||||
|
|
||||||
if ($foundEvents = parent::search(array(
|
|
||||||
'query' => $query,
|
|
||||||
)))
|
|
||||||
{
|
|
||||||
if(is_array($foundEvents))
|
|
||||||
{
|
|
||||||
$event = array_shift($foundEvents);
|
|
||||||
return $event['id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Let's try the "status only" (pseudo) exceptions now
|
|
||||||
if (($egw_event = $this->read($uidmatch['id'], $event['recurrence'])))
|
|
||||||
{
|
|
||||||
// Do we work with a pseudo exception here?
|
|
||||||
$match = true;
|
|
||||||
foreach (array('start', 'end', 'title', 'priority',
|
|
||||||
'location', 'public', 'non_blocking') as $key)
|
|
||||||
{
|
|
||||||
if (isset($event[$key])
|
|
||||||
&& $event[$key] != $egw_event[$key])
|
|
||||||
{
|
|
||||||
$match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($match && is_array($event['participants']))
|
|
||||||
{
|
|
||||||
foreach ($event['participants'] as $attendee => $status)
|
|
||||||
{
|
|
||||||
if (!isset($egw_event['participants'][$attendee])
|
|
||||||
|| $egw_event['participants'][$attendee] != $status)
|
|
||||||
{
|
|
||||||
$match = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unset($egw_event['participants'][$attendee]);
|
if ($event['recur_type'] == MCAL_RECUR_NONE)
|
||||||
}
|
{
|
||||||
}
|
if (empty($event['uid']) || !isset($event['recurrence']))
|
||||||
if ($match && !empty($egw_event['participants'])) $match = false;
|
{
|
||||||
}
|
$event['recurrence'] = $event['start'];
|
||||||
if ($match) return ($uidmatch['id'] . ':' . $event['recurrence']);
|
|
||||||
|
|
||||||
return false; // We need to create a new pseudo exception
|
|
||||||
}
|
}
|
||||||
|
$recur_date = $event['recurrence'];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return $uidmatch['id'];
|
$event['recurrence'] = 0;
|
||||||
}
|
$recur_date = $event['start'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event['id'] && ($found = $this->read($event['id'])))
|
$recur_date = $this->date2usertime($recur_date);
|
||||||
{
|
|
||||||
// We only do a simple consistency check
|
|
||||||
if ($found['title'] == $event['title']
|
|
||||||
&& $found['start'] == $event['start']
|
|
||||||
&& $found['end'] == $event['end'])
|
|
||||||
{
|
|
||||||
return $found['id'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($event['id'])
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'(' . $event['id'] . ")[EventID]");
|
||||||
|
}
|
||||||
|
if (($egwEvent = $this->read($event['id'], $recur_date, false, 'server')))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'()[FOUND]:' . array2string($egwEvent));
|
||||||
|
}
|
||||||
|
// Just a simple consistency check
|
||||||
|
if ($filter == 'master' && $egwEvent['recur_type'] != MCAL_RECUR_NONE ||
|
||||||
|
$egwEvent['title'] == $event['title'] &&
|
||||||
|
abs($egwEvent['start'] - $event['start']) < 60 &&
|
||||||
|
abs($egwEvent['end'] - $event['end']) < 120)
|
||||||
|
{
|
||||||
|
$retval = $egwEvent['id'];
|
||||||
|
if ($egwEvent['recur_type'] != MCAL_RECUR_NONE &&
|
||||||
|
$event['recur_type'] == MCAL_RECUR_NONE && $event['recurrence'] != 0)
|
||||||
|
{
|
||||||
|
$retval .= ':' . (int)$event['recurrence'];
|
||||||
|
}
|
||||||
|
$matchingEvents[] = $retval;
|
||||||
|
return $matchingEvents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($filter == 'exact') return array();
|
||||||
}
|
}
|
||||||
unset($event['id']);
|
unset($event['id']);
|
||||||
|
|
||||||
if($foundEvents = parent::search(array(
|
if (isset($event['whole_day']) && $event['whole_day'])
|
||||||
'query' => $query,
|
|
||||||
)))
|
|
||||||
{
|
{
|
||||||
if(is_array($foundEvents))
|
if ($filter == 'relax')
|
||||||
{
|
{
|
||||||
$event = array_shift($foundEvents);
|
$delta = 1800;
|
||||||
return $event['id'];
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$delta = 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check length with some tolerance
|
||||||
|
$length = $event['end'] - $event['start'] - $delta;
|
||||||
|
$query[] = ('(cal_end-cal_start)>' . $length);
|
||||||
|
$length += 2 * $delta;
|
||||||
|
$query[] = ('(cal_end-cal_start)<' . $length);
|
||||||
|
}
|
||||||
|
elseif (isset($event['start']))
|
||||||
|
{
|
||||||
|
if ($filter != 'master')
|
||||||
|
{
|
||||||
|
if ($filter == 'relax')
|
||||||
|
{
|
||||||
|
$query[] = ('cal_start>' . ($event['start'] - 3600));
|
||||||
|
$query[] = ('cal_start<' . ($event['start'] + 3600));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we accept a tiny tolerance
|
||||||
|
$query[] = ('cal_start>' . ($event['start'] - 2));
|
||||||
|
$query[] = ('cal_start<' . ($event['start'] + 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
if ($filter == 'master')
|
||||||
|
{
|
||||||
|
$query[] = 'recur_type!='. MCAL_RECUR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only query calendars of users, we have READ-grants from
|
||||||
|
$users = array();
|
||||||
|
foreach(array_keys($this->grants) as $user)
|
||||||
|
{
|
||||||
|
$user = trim($user);
|
||||||
|
if ($this->check_perms(EGW_ACL_READ|EGW_ACL_READ_FOR_PARTICIPANTS|EGW_ACL_FREEBUSY,0,$user))
|
||||||
|
{
|
||||||
|
if ($user && !in_array($user,$users)) // already added?
|
||||||
|
{
|
||||||
|
$users[] = $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($GLOBALS['egw']->accounts->get_type($user) != 'g')
|
||||||
|
{
|
||||||
|
continue; // for non-groups (eg. users), we stop here if we have no read-rights
|
||||||
|
}
|
||||||
|
// the further code is only for real users
|
||||||
|
if (!is_numeric($user)) continue;
|
||||||
|
|
||||||
|
// for groups we have to include the members
|
||||||
|
if ($GLOBALS['egw']->accounts->get_type($user) == 'g')
|
||||||
|
{
|
||||||
|
$members = $GLOBALS['egw']->accounts->member($user);
|
||||||
|
if (is_array($members))
|
||||||
|
{
|
||||||
|
foreach($members as $member)
|
||||||
|
{
|
||||||
|
// use only members which gave the user a read-grant
|
||||||
|
if (!in_array($member['account_id'],$users) &&
|
||||||
|
$this->check_perms(EGW_ACL_READ|EGW_ACL_FREEBUSY,0,$member['account_id']))
|
||||||
|
{
|
||||||
|
$users[] = $member['account_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // for users we have to include all the memberships, to get the group-events
|
||||||
|
{
|
||||||
|
$memberships = $GLOBALS['egw']->accounts->membership($user);
|
||||||
|
if (is_array($memberships))
|
||||||
|
{
|
||||||
|
foreach($memberships as $group)
|
||||||
|
{
|
||||||
|
if (!in_array($group['account_id'],$users))
|
||||||
|
{
|
||||||
|
$users[] = $group['account_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($event['uid']))
|
||||||
|
{
|
||||||
|
$matchFields = array('title', 'priority', 'public', 'non_blocking');
|
||||||
|
switch ($filter)
|
||||||
|
{
|
||||||
|
case 'relax':
|
||||||
|
$matchFields[] = 'location';
|
||||||
|
case 'master':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$matchFields[] = 'description';
|
||||||
|
$matchFields[] = 'location';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($matchFields as $key)
|
||||||
|
{
|
||||||
|
if (!empty($event[$key])) $query['cal_'.$key] = $event[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$query['cal_uid'] = $event['uid'];
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'(' . $event['uid'] . ')[EventUID]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[QUERY]: ' . array2string($query));
|
||||||
|
}
|
||||||
|
if (!count($users) || !($foundEvents =
|
||||||
|
$this->so->search(null, null, $users, 0, 'owner', $query)))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[NO MATCH]');
|
||||||
|
}
|
||||||
|
return $matchingEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pseudos = array();
|
||||||
|
|
||||||
|
foreach($foundEvents as $egwEvent)
|
||||||
|
{
|
||||||
|
if (in_array($egwEvent['id'], $matchingEvents)) continue;
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[FOUND]: ' . array2string($egwEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert timezone id of event to tzid (iCal id like 'Europe/Berlin')
|
||||||
|
if (!$egwEvent['tz_id'] || !($egwEvent['tzid'] = calendar_timezones::id2tz($egwEvent['tz_id'])))
|
||||||
|
{
|
||||||
|
$egwEvent['tzid'] = egw_time::$server_timezone->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check times
|
||||||
|
if ($filter != 'relax')
|
||||||
|
{
|
||||||
|
if (isset($event['whole_day'])&& $event['whole_day'])
|
||||||
|
{
|
||||||
|
if (!$this->so->isWholeDay($egwEvent))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() egwEvent is not a whole-day event!');
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($filter != 'master')
|
||||||
|
{
|
||||||
|
if (abs($event['end'] - $egwEvent['end']) >= 120)
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() egwEvent length does not match!');
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filter != 'master')
|
||||||
|
{
|
||||||
|
// check categories
|
||||||
|
if (!is_array($event['category'])) $event['category'] = array();
|
||||||
|
$egwCategories = explode(',', $egwEvent['category']);
|
||||||
|
foreach ($egwCategories as $cat_id)
|
||||||
|
{
|
||||||
|
if ($this->categories->check_perms(EGW_ACL_READ, $cat_id) &&
|
||||||
|
!in_array($cat_id, $event['category']))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() egwEvent category $cat_id is missing!");
|
||||||
|
}
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$newCategories = array_diff($event['category'], $egwCategories);
|
||||||
|
if (!empty($newCategories))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() event has additional categories:' . array2string($newCategories));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// check information
|
||||||
|
$matchFields = array();
|
||||||
|
foreach ($matchFields as $key)
|
||||||
|
{
|
||||||
|
if (isset($event[$key])
|
||||||
|
&& $event[$key] != $egwEvent[$key])
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() events[$key] differ: " . $event[$key] .
|
||||||
|
' <> ' . $egwEvent[$key]);
|
||||||
|
}
|
||||||
|
continue 2; // next foundEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ($filter != 'relax' && $filter != 'master')
|
||||||
|
{
|
||||||
|
// check participants
|
||||||
|
if (is_array($event['participants']))
|
||||||
|
{
|
||||||
|
foreach ($event['participants'] as $attendee => $status)
|
||||||
|
{
|
||||||
|
if (!isset($egwEvent['participants'][$attendee]) &&
|
||||||
|
$attendee != $egwEvent['owner']) // ||
|
||||||
|
//(!$relax && $egw_event['participants'][$attendee] != $status))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() additional event['participants']: $attendee");
|
||||||
|
}
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unset($egwEvent['participants'][$attendee]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ORGANIZER is maybe missing
|
||||||
|
unset($egwEvent['participants'][$egwEvent['owner']]);
|
||||||
|
if (!empty($egwEvent['participants']))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() missing event[participants]: ' .
|
||||||
|
array2string($egwEvent['participants']));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filter != 'master')
|
||||||
|
{
|
||||||
|
if ($event['recur_type'] == MCAL_RECUR_NONE)
|
||||||
|
{
|
||||||
|
if ($egwEvent['recur_type'] != MCAL_RECUR_NONE)
|
||||||
|
{
|
||||||
|
// We found a pseudo Exception
|
||||||
|
$pseudos[] = $egwEvent['id'] . ':' . $event['start'];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($filter != 'relax')
|
||||||
|
{
|
||||||
|
// check exceptions
|
||||||
|
// $exceptions[$remote_ts] = $egw_ts
|
||||||
|
$exceptions = $this->so->get_recurrence_exceptions($egwEvent, $event['$tzid'], 0, 0, 'map');
|
||||||
|
// remove leading exceptions
|
||||||
|
foreach ($exceptions as $key => $day)
|
||||||
|
{
|
||||||
|
if ($day <= $event['start']) unset($exceptions['key']);
|
||||||
|
}
|
||||||
|
if (is_array($event['recur_excpetion']))
|
||||||
|
{
|
||||||
|
foreach ($event['recur_excpetion'] as $key => $day)
|
||||||
|
{
|
||||||
|
if (isset($exceptions[$day]))
|
||||||
|
{
|
||||||
|
unset($exceptions[$day]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() additional event['recur_exception']: $day");
|
||||||
|
}
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($exceptions))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() missing event[recur_exception]: ' .
|
||||||
|
array2string($event['recur_excpetion']));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check recurrence information
|
||||||
|
foreach (array('recur_type', 'recur_interval') as $key)
|
||||||
|
{
|
||||||
|
if (isset($event[$key])
|
||||||
|
&& $event[$key] != $egwEvent[$key])
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() events[$key] differ: " . $event[$key] .
|
||||||
|
' <> ' . $egwEvent[$key]);
|
||||||
|
}
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$matchingEvents[] = $egwEvent['id']; // exact match
|
||||||
|
}
|
||||||
|
// append pseudos as last entries
|
||||||
|
$matchingEvents = array_merge($matchingEvents, $pseudos);
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[MATCHES]:' . array2string($matchingEvents));
|
||||||
|
}
|
||||||
|
return $matchingEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1528,17 +1854,19 @@ class calendar_boupdate extends calendar_bo
|
|||||||
* SINGLE a single event
|
* SINGLE a single event
|
||||||
* SERIES-MASTER the series master
|
* SERIES-MASTER the series master
|
||||||
* SERIES-EXCEPTION event is a real exception
|
* SERIES-EXCEPTION event is a real exception
|
||||||
* SERIES-EXCEPTION-STATUS event is a status only exception
|
* SERIES-PSEUDO-EXCEPTION event is a status only exception
|
||||||
* SERIES-EXCEPTION-PROPAGATE event was a status only exception in the past and is now a real exception
|
* SERIES-EXCEPTION-PROPAGATE event was a status only exception in the past and is now a real exception
|
||||||
* stored_event => if event already exists in the database array with event data or false
|
* stored_event => if event already exists in the database array with event data or false
|
||||||
* master_event => for event type SERIES-EXCEPTION, SERIES-EXCEPTION-STATUS or SERIES-EXCEPTION-PROPAGATE
|
* master_event => for event type SERIES-EXCEPTION, SERIES-PSEUDO-EXCEPTION or SERIES-EXCEPTION-PROPAGATE
|
||||||
* the corresponding series master event array
|
* the corresponding series master event array
|
||||||
* NOTE: this param is false if event is of type SERIES-MASTER
|
* NOTE: this param is false if event is of type SERIES-MASTER
|
||||||
*/
|
*/
|
||||||
function get_event_info($event)
|
function get_event_info($event)
|
||||||
{
|
{
|
||||||
$type = 'SINGLE'; // default
|
$type = 'SINGLE'; // default
|
||||||
$return_master = false; //default
|
$master_event = false; //default
|
||||||
|
$stored_event = false;
|
||||||
|
$recurrence_event = false;
|
||||||
|
|
||||||
if ($event['recur_type'] != MCAL_RECUR_NONE)
|
if ($event['recur_type'] != MCAL_RECUR_NONE)
|
||||||
{
|
{
|
||||||
@ -1547,44 +1875,114 @@ class calendar_boupdate extends calendar_bo
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// SINGLE, SERIES-EXCEPTION OR SERIES-EXCEPTON-STATUS
|
// SINGLE, SERIES-EXCEPTION OR SERIES-EXCEPTON-STATUS
|
||||||
if (empty($event['uid']) && $event['id'] > 0 && ($stored_event = $this->read($event['id'])))
|
if (($foundEvents = $this->find_event($event, 'exact')))
|
||||||
{
|
{
|
||||||
$event['uid'] = $stored_event['uid']; // restore the UID if it was not delivered
|
// We found the exact match
|
||||||
|
$eventID = array_shift($foundEvents);
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"()[EVENT]: $eventID");
|
||||||
}
|
}
|
||||||
|
if (strstr($eventID, ':'))
|
||||||
if (isset($event['uid'])
|
|
||||||
&& $event['recurrence']
|
|
||||||
&& ($master_event = $this->read($event['uid']))
|
|
||||||
&& isset($master_event['recur_type'])
|
|
||||||
&& $master_event['recur_type'] != MCAL_RECUR_NONE)
|
|
||||||
{
|
{
|
||||||
// SERIES-EXCEPTION OR SERIES-EXCEPTON-STATUS
|
$type = 'SERIES-PSEUDO-EXCEPTION';
|
||||||
$return_master = true; // we have a valid master and can return it
|
list($eventID, $recur_date) = explode(':', $eventID);
|
||||||
|
$recur_date = $this->date2usertime($recur_date);
|
||||||
if (isset($event['id']) && $master_event['id'] != $event['id'])
|
$stored_event = $this->read($eventID, $recur_date, false, 'server');
|
||||||
{
|
$master_event = $this->read($eventID, 0, false, 'server');
|
||||||
$type = 'SERIES-EXCEPTION'; // this is an existing exception
|
$recurrence_event = $stored_event;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$type = 'SERIES-EXCEPTION-STATUS'; // default if we cannot find a proof for a fundamental change
|
$stored_event = $this->read($eventID, 0, false, 'server');
|
||||||
|
}
|
||||||
|
if (empty($event['uid']))
|
||||||
|
{
|
||||||
|
$event['uid'] = $stored_event['uid']; // restore the UID if it was not delivered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type == 'SINGLE' &&
|
||||||
|
($foundEvents = $this->find_event($event, 'master')))
|
||||||
|
{
|
||||||
|
// Let's try to find a related series
|
||||||
|
foreach ($foundEvents as $eventID)
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"()[MASTER]: $eventID");
|
||||||
|
}
|
||||||
|
if (($master_event = $this->read($eventID, 0, false, 'server')))
|
||||||
|
{
|
||||||
|
if (isset($stored_event['id']) && $master_event['id'] != $stored_event['id'])
|
||||||
|
{
|
||||||
|
$type = 'SERIES-EXCEPTION'; // this is an existing exception
|
||||||
|
}
|
||||||
|
elseif (in_array($event['start'], $master_event['recur_exception']))
|
||||||
|
{
|
||||||
|
$type='SERIES-PSEUDO-EXCEPTION'; // new pseudo exception?
|
||||||
|
$recurrence_event = $event;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elseif (isset($event['recurrence']) &&
|
||||||
|
in_array($event['recurrence'], $master_event['recur_exception']))
|
||||||
|
{
|
||||||
|
$type = 'SERIES-EXCEPTION';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// try to find a suitable pseudo exception date
|
||||||
|
$egw_rrule = calendar_rrule::event2rrule($master_event, false);
|
||||||
|
$egw_rrule->rewind();
|
||||||
|
while ($egw_rrule->valid())
|
||||||
|
{
|
||||||
|
$occurrence = egw_time::to($egw_rrule->current(), 'server');
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() try occurrence ' . $egw_rrule->current() . " ($occurrence)");
|
||||||
|
}
|
||||||
|
if ($event['start'] == $occurrence)
|
||||||
|
{
|
||||||
|
$type = 'SERIES-PSEUDO-EXCEPTION'; // let's try a pseudo exception
|
||||||
|
$recurrence_event = $event;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
if (isset($event['recurrence']) && $event['recurrence'] == $occurrence)
|
||||||
|
{
|
||||||
|
$type = 'SERIES-EXCEPTION-PROPAGATE';
|
||||||
|
if ($stored_event)
|
||||||
|
{
|
||||||
|
unset($stored_event['id']); // signal the true exception
|
||||||
|
$stored_event['recur_type'] = MCAL_RECUR_NONE;
|
||||||
|
}
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
$egw_rrule->next_no_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check pseudo exception propagation
|
||||||
|
if ($recurrence_event)
|
||||||
|
{
|
||||||
|
// default if we cannot find a proof for a fundamental change
|
||||||
// the recurrence_event is the master event with start and end adjusted to the recurrence
|
// the recurrence_event is the master event with start and end adjusted to the recurrence
|
||||||
$recurrence_event = $master_event;
|
|
||||||
$recurrence_event['start'] = $event['recurrence'];
|
|
||||||
$recurrence_event['end'] = $event['recurrence'] + ($master_event['end'] - $master_event['start']);
|
|
||||||
// check for changed data
|
// check for changed data
|
||||||
foreach (array('start','end','uid','title','location',
|
foreach (array('start','end','uid','title','location','description',
|
||||||
'priority','public','special','non_blocking') as $key)
|
'priority','public','special','non_blocking') as $key)
|
||||||
{
|
{
|
||||||
if (!empty($event[$key]) && $recurrence_event[$key] != $event[$key])
|
if (!empty($event[$key]) && $recurrence_event[$key] != $event[$key])
|
||||||
{
|
|
||||||
if (isset($event['id']))
|
|
||||||
{
|
{
|
||||||
$type = 'SERIES-EXCEPTION-PROPAGATE';
|
$type = 'SERIES-EXCEPTION-PROPAGATE';
|
||||||
}
|
if ($stored_event)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
$type = 'SERIES-EXCEPTION'; // this is a new exception
|
unset($stored_event['id']); // signal the true exception
|
||||||
|
$stored_event['recur_type'] = MCAL_RECUR_NONE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1594,21 +1992,9 @@ class calendar_boupdate extends calendar_bo
|
|||||||
unset($event['id']);
|
unset($event['id']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// SINGLE
|
|
||||||
$type = 'SINGLE';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read existing event
|
|
||||||
if (isset($event['id']))
|
|
||||||
{
|
|
||||||
$stored_event = $this->read($event['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check ACL
|
// check ACL
|
||||||
if ($return_master)
|
if (is_array($master_event))
|
||||||
{
|
{
|
||||||
$acl_edit = $this->check_perms(EGW_ACL_EDIT, $master_event['id']);
|
$acl_edit = $this->check_perms(EGW_ACL_EDIT, $master_event['id']);
|
||||||
}
|
}
|
||||||
@ -1627,8 +2013,40 @@ class calendar_boupdate extends calendar_bo
|
|||||||
return array(
|
return array(
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'acl_edit' => $acl_edit,
|
'acl_edit' => $acl_edit,
|
||||||
'stored_event' => is_array($stored_event) ? $stored_event : false,
|
'stored_event' => $stored_event,
|
||||||
'master_event' => $return_master ? $master_event : false,
|
'master_event' => $master_event,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translates all timestamps for a given event from servert-ime to user-time.
|
||||||
|
* The update() and save() methods expect timestamps in user-time.
|
||||||
|
* @param &$event the event we are working on
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function server2usertime (&$event)
|
||||||
|
{
|
||||||
|
// we run all dates through date2usertime, to adjust to user-time
|
||||||
|
foreach(array('start','end','recur_enddate','recurrence') as $ts)
|
||||||
|
{
|
||||||
|
// we convert here from server-time to timestamps in user-time!
|
||||||
|
if (isset($event[$ts])) $event[$ts] = $event[$ts] ? $this->date2usertime($event[$ts]) : 0;
|
||||||
|
}
|
||||||
|
// same with the recur exceptions
|
||||||
|
if (isset($event['recur_exception']) && is_array($event['recur_exception']))
|
||||||
|
{
|
||||||
|
foreach($event['recur_exception'] as $n => $date)
|
||||||
|
{
|
||||||
|
$event['recur_exception'][$n] = $this->date2usertime($date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// same with the alarms
|
||||||
|
if (isset($event['alarm']) && is_array($event['alarm']))
|
||||||
|
{
|
||||||
|
foreach($event['alarm'] as $id => $alarm)
|
||||||
|
{
|
||||||
|
$event['alarm'][$id]['time'] = $this->date2usertime($alarm['time']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@
|
|||||||
* @link http://www.egroupware.org
|
* @link http://www.egroupware.org
|
||||||
* @package calendar
|
* @package calendar
|
||||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
|
* @author Joerg Lehrke <jlehrke@noc.de>
|
||||||
* @copyright (c) 2009 by RalfBecker-At-outdoor-training.de
|
* @copyright (c) 2009 by RalfBecker-At-outdoor-training.de
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
@ -192,6 +193,12 @@ class calendar_rrule implements Iterator
|
|||||||
*/
|
*/
|
||||||
protected $lastdayofweek;
|
protected $lastdayofweek;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached timezone data
|
||||||
|
*
|
||||||
|
* @var array id => data
|
||||||
|
*/
|
||||||
|
protected static $tz_cache = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -426,6 +433,12 @@ class calendar_rrule implements Iterator
|
|||||||
public function rewind()
|
public function rewind()
|
||||||
{
|
{
|
||||||
$this->current = clone $this->time;
|
$this->current = clone $this->time;
|
||||||
|
while ($this->valid() &&
|
||||||
|
$this->exceptions &&
|
||||||
|
in_array($this->current->format('Ymd'),$this->exceptions))
|
||||||
|
{
|
||||||
|
$this->next_no_exception();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -517,18 +530,21 @@ class calendar_rrule implements Iterator
|
|||||||
/**
|
/**
|
||||||
* Generate a VEVENT RRULE
|
* Generate a VEVENT RRULE
|
||||||
* @param string $version='1.0' could be '2.0', too
|
* @param string $version='1.0' could be '2.0', too
|
||||||
|
*
|
||||||
|
* $return array vCalendar RRULE
|
||||||
*/
|
*/
|
||||||
public function generate_rrule($version='1.0')
|
public function generate_rrule($version='1.0')
|
||||||
{
|
{
|
||||||
static $utc;
|
|
||||||
$repeat_days = array();
|
$repeat_days = array();
|
||||||
$rrule = array();
|
$rrule = array();
|
||||||
|
|
||||||
if (is_null($utc))
|
if (!isset(self::$tz_cache['UTC']))
|
||||||
{
|
{
|
||||||
$utc = calendar_timezones::DateTimeZone('UTC');
|
self::$tz_cache['UTC'] = calendar_timezones::DateTimeZone('UTC');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$utc = self::$tz_cache['UTC'];
|
||||||
|
|
||||||
if ($this->type == self::NONE) return false; // no recuring event
|
if ($this->type == self::NONE) return false; // no recuring event
|
||||||
|
|
||||||
if ($version == '1.0')
|
if ($version == '1.0')
|
||||||
@ -609,13 +625,24 @@ class calendar_rrule implements Iterator
|
|||||||
*
|
*
|
||||||
* @param array $event
|
* @param array $event
|
||||||
* @param boolean $usertime=true true: event timestamps are usertime (default for calendar_bo::(read|search), false: servertime
|
* @param boolean $usertime=true true: event timestamps are usertime (default for calendar_bo::(read|search), false: servertime
|
||||||
|
* @param string $to_tz timezone for exports (null for event's timezone)
|
||||||
|
*
|
||||||
* @return calendar_rrule
|
* @return calendar_rrule
|
||||||
*/
|
*/
|
||||||
public static function event2rrule(array $event,$usertime=true)
|
public static function event2rrule(array $event,$usertime=true,$to_tz=null)
|
||||||
{
|
{
|
||||||
|
if (!$to_tz) $to_tz = $event['tzid'];
|
||||||
$timestamp_tz = $usertime ? egw_time::$user_timezone : egw_time::$server_timezone;
|
$timestamp_tz = $usertime ? egw_time::$user_timezone : egw_time::$server_timezone;
|
||||||
$time = is_a($event['start'],'DateTime') ? $event['start'] : new egw_time($event['start'],$timestamp_tz);
|
$time = is_a($event['start'],'DateTime') ? $event['start'] : new egw_time($event['start'],$timestamp_tz);
|
||||||
$time->setTimezone(new DateTimeZone($event['tzid']));
|
|
||||||
|
if (!isset(self::$tz_cache[$to_tz]))
|
||||||
|
{
|
||||||
|
self::$tz_cache[$to_tz] = calendar_timezones::DateTimeZone($to_tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::rrule2tz($event, $time, $to_tz);
|
||||||
|
|
||||||
|
$time->setTimezone(self::$tz_cache[$to_tz]);
|
||||||
|
|
||||||
if ($event['recur_enddate'])
|
if ($event['recur_enddate'])
|
||||||
{
|
{
|
||||||
@ -646,6 +673,64 @@ class calendar_rrule implements Iterator
|
|||||||
'recur_exception' => $this->exceptions,
|
'recur_exception' => $this->exceptions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift a recurrence rule to a new timezone
|
||||||
|
*
|
||||||
|
* @param array $event recurring event
|
||||||
|
* @param DateTime/string starttime of the event (in servertime)
|
||||||
|
* @param string $to_tz new timezone
|
||||||
|
*/
|
||||||
|
public static function rrule2tz(array &$event,$starttime,$to_tz)
|
||||||
|
{
|
||||||
|
// We assume that the difference between timezones can result
|
||||||
|
// in a maximum of one day
|
||||||
|
|
||||||
|
if (!is_array($event) ||
|
||||||
|
!isset($event['recur_type']) ||
|
||||||
|
$event['recur_type'] == MCAL_RECUR_NONE ||
|
||||||
|
empty($event['recur_data']) || $event['recur_data'] == ALLDAYS ||
|
||||||
|
empty($event['tzid']) || empty($to_tz) ||
|
||||||
|
$event['tzid'] == $to_tz) return;
|
||||||
|
|
||||||
|
if (!isset(self::$tz_cache[$event['tzid']]))
|
||||||
|
{
|
||||||
|
self::$tz_cache[$event['tzid']] = calendar_timezones::DateTimeZone($event['tzid']);
|
||||||
|
}
|
||||||
|
if (!isset(self::$tz_cache[$to_tz]))
|
||||||
|
{
|
||||||
|
self::$tz_cache[$to_tz] = calendar_timezones::DateTimeZone($to_tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = is_a($starttime,'DateTime') ?
|
||||||
|
$starttime : new egw_time($starttime, egw_time::$server_timezone);
|
||||||
|
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
||||||
|
$remote = clone $time;
|
||||||
|
$remote->setTimezone(self::$tz_cache[$to_tz]);
|
||||||
|
$delta = (int)$remote->format('w') - (int)$time->format('w');
|
||||||
|
if ($delta)
|
||||||
|
{
|
||||||
|
// We have to generate a shifted rrule
|
||||||
|
switch ($event['recur_type'])
|
||||||
|
{
|
||||||
|
case self::MONTHLY_WDAY:
|
||||||
|
case self::WEEKLY:
|
||||||
|
$mask = (int)$event['recur_data'];
|
||||||
|
|
||||||
|
if ($delta == 1 || $delta == -6)
|
||||||
|
{
|
||||||
|
$mask = $mask << 1;
|
||||||
|
if ($mask & 128) $mask = $mask - 127; // overflow
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($mask & 1) $mask = $mask + 128; // underflow
|
||||||
|
$mask = $mask >> 1;
|
||||||
|
}
|
||||||
|
$event['recur_data'] = $mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) // some tests
|
if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) // some tests
|
||||||
|
@ -195,7 +195,7 @@ class calendar_sif extends calendar_boupdate
|
|||||||
* @param string $tzid TZID of event or 'UTC' or NULL for palmos timestamps in usertime
|
* @param string $tzid TZID of event or 'UTC' or NULL for palmos timestamps in usertime
|
||||||
* @return mixed attribute value to set: integer timestamp if $tzid == 'UTC' otherwise Ymd\THis string IN $tzid
|
* @return mixed attribute value to set: integer timestamp if $tzid == 'UTC' otherwise Ymd\THis string IN $tzid
|
||||||
*/
|
*/
|
||||||
function getDateTime($time,$tzid)
|
function getDateTime($time, $tzid)
|
||||||
{
|
{
|
||||||
if (empty($tzid) || $tzid == 'UTC')
|
if (empty($tzid) || $tzid == 'UTC')
|
||||||
{
|
{
|
||||||
@ -223,19 +223,10 @@ class calendar_sif extends calendar_boupdate
|
|||||||
if ($this->tzid)
|
if ($this->tzid)
|
||||||
{
|
{
|
||||||
// enforce device settings
|
// enforce device settings
|
||||||
|
date_default_timezone_set($this->tzid);
|
||||||
$finalEvent['tzid'] = $this->tzid;
|
$finalEvent['tzid'] = $this->tzid;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
$finalEvent['tzid'] = egw_time::$user_timezone->getName(); // default to user timezone
|
|
||||||
}
|
|
||||||
#error_log($sifData);
|
|
||||||
|
|
||||||
#$tmpfname = tempnam('/tmp/sync/contents','sife_');
|
|
||||||
|
|
||||||
#$handle = fopen($tmpfname, "w");
|
|
||||||
#fwrite($handle, $sifData);
|
|
||||||
#fclose($handle);
|
|
||||||
|
|
||||||
$this->xml_parser = xml_parser_create('UTF-8');
|
$this->xml_parser = xml_parser_create('UTF-8');
|
||||||
xml_set_object($this->xml_parser, $this);
|
xml_set_object($this->xml_parser, $this);
|
||||||
@ -248,20 +239,24 @@ class calendar_sif extends calendar_boupdate
|
|||||||
error_log(sprintf("XML error: %s at line %d",
|
error_log(sprintf("XML error: %s at line %d",
|
||||||
xml_error_string(xml_get_error_code($this->xml_parser)),
|
xml_error_string(xml_get_error_code($this->xml_parser)),
|
||||||
xml_get_current_line_number($this->xml_parser)));
|
xml_get_current_line_number($this->xml_parser)));
|
||||||
|
if ($this->tzid)
|
||||||
|
{
|
||||||
|
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#error_log(print_r($this->event, true));
|
|
||||||
|
|
||||||
foreach ($this->event as $key => $value)
|
foreach ($this->event as $key => $value)
|
||||||
{
|
{
|
||||||
$value = preg_replace('/<\!\[CDATA\[(.+)\]\]>/Usim', '$1', $value);
|
$value = preg_replace('/<\!\[CDATA\[(.+)\]\]>/Usim', '$1', $value);
|
||||||
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
|
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
|
||||||
|
/*
|
||||||
if ($this->log)
|
if ($this->log)
|
||||||
{
|
{
|
||||||
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
"() $key => $value\n",3,$this->logfile);
|
"() $key => $value\n",3,$this->logfile);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
switch ($key)
|
switch ($key)
|
||||||
{
|
{
|
||||||
case 'alldayevent':
|
case 'alldayevent':
|
||||||
@ -269,17 +264,17 @@ class calendar_sif extends calendar_boupdate
|
|||||||
{
|
{
|
||||||
$finalEvent['whole_day'] = true;
|
$finalEvent['whole_day'] = true;
|
||||||
$startParts = explode('-',$this->event['start']);
|
$startParts = explode('-',$this->event['start']);
|
||||||
$finalEvent['start']['hour'] = $finalEvent['start']['minute'] = $finalEvent['start']['second'] = 0;
|
$finalEvent['startdate']['hour'] = $finalEvent['startdate']['minute'] = $finalEvent['startdate']['second'] = 0;
|
||||||
$finalEvent['start']['year'] = $startParts[0];
|
$finalEvent['startdate']['year'] = $startParts[0];
|
||||||
$finalEvent['start']['month'] = $startParts[1];
|
$finalEvent['startdate']['month'] = $startParts[1];
|
||||||
$finalEvent['start']['day'] = $startParts[2];
|
$finalEvent['startdate']['day'] = $startParts[2];
|
||||||
$finalEvent['start'] = $this->date2ts($finalEvent['start']);
|
$finalEvent['start'] = $this->date2ts($finalEvent['startdate']);
|
||||||
$endParts = explode('-',$this->event['end']);
|
$endParts = explode('-',$this->event['end']);
|
||||||
$finalEvent['end']['hour'] = 23; $finalEvent['end']['minute'] = $finalEvent['end']['second'] = 59;
|
$finalEvent['enddate']['hour'] = 23; $finalEvent['enddate']['minute'] = $finalEvent['enddate']['second'] = 59;
|
||||||
$finalEvent['end']['year'] = $endParts[0];
|
$finalEvent['enddate']['year'] = $endParts[0];
|
||||||
$finalEvent['end']['month'] = $endParts[1];
|
$finalEvent['enddate']['month'] = $endParts[1];
|
||||||
$finalEvent['end']['day'] = $endParts[2];
|
$finalEvent['enddate']['day'] = $endParts[2];
|
||||||
$finalEvent['end'] = $this->date2ts($finalEvent['end']);
|
$finalEvent['end'] = $this->date2ts($finalEvent['enddate']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -293,7 +288,7 @@ class calendar_sif extends calendar_boupdate
|
|||||||
$categories1 = explode(',', $value);
|
$categories1 = explode(',', $value);
|
||||||
$categories2 = explode(';', $value);
|
$categories2 = explode(';', $value);
|
||||||
$categories = count($categories1) > count($categories2) ? $categories1 : $categories2;
|
$categories = count($categories1) > count($categories2) ? $categories1 : $categories2;
|
||||||
$finalEvent[$key] = implode(',', $this->find_or_add_categories($categories, $_calID));
|
$finalEvent[$key] = $this->find_or_add_categories($categories, $_calID);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -321,7 +316,14 @@ class calendar_sif extends calendar_boupdate
|
|||||||
$finalEvent['recur_data'] = 0;
|
$finalEvent['recur_data'] = 0;
|
||||||
if ($this->event['recur_noenddate'] == 0)
|
if ($this->event['recur_noenddate'] == 0)
|
||||||
{
|
{
|
||||||
$finalEvent['recur_enddate'] = $this->vCalendar->_parseDateTime($this->event['recur_enddate']);
|
$recur_enddate = $this->vCalendar->_parseDateTime($this->event['recur_enddate']);
|
||||||
|
$finalEvent['recur_enddate'] = mktime(
|
||||||
|
date('H', 23),
|
||||||
|
date('i', 59),
|
||||||
|
date('s', 59),
|
||||||
|
date('m', $recur_enddate),
|
||||||
|
date('d', $recur_enddate),
|
||||||
|
date('Y', $recur_enddate));
|
||||||
}
|
}
|
||||||
switch ($this->event['recur_type'])
|
switch ($this->event['recur_type'])
|
||||||
{
|
{
|
||||||
@ -382,6 +384,15 @@ class calendar_sif extends calendar_boupdate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->calendarOwner) $finalEvent['owner'] = $this->calendarOwner;
|
||||||
|
|
||||||
|
if ($this->tzid)
|
||||||
|
{
|
||||||
|
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_calID > 0) $finalEvent['id'] = $_calID;
|
||||||
|
|
||||||
if ($this->log)
|
if ($this->log)
|
||||||
{
|
{
|
||||||
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
|
||||||
@ -393,14 +404,15 @@ class calendar_sif extends calendar_boupdate
|
|||||||
|
|
||||||
function search($_sifdata, $contentID=null, $relax=false)
|
function search($_sifdata, $contentID=null, $relax=false)
|
||||||
{
|
{
|
||||||
$result = false;
|
$result = array();
|
||||||
|
$filter = $relax ? 'relax' : 'exact';
|
||||||
|
|
||||||
if ($event = $this->siftoegw($_sifdata, $contentID))
|
if ($event = $this->siftoegw($_sifdata, $contentID))
|
||||||
{
|
{
|
||||||
if ($contentID) {
|
if ($contentID) {
|
||||||
$event['id'] = $contentID;
|
$event['id'] = $contentID;
|
||||||
}
|
}
|
||||||
$result = $this->find_event($event, $relax);
|
$result = $this->find_event($event, $filter);
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@ -410,9 +422,11 @@ class calendar_sif extends calendar_boupdate
|
|||||||
* @param string $_sifdata the SIFE data
|
* @param string $_sifdata the SIFE data
|
||||||
* @param int $_calID=-1 the internal addressbook id
|
* @param int $_calID=-1 the internal addressbook id
|
||||||
* @param boolean $merge=false merge data with existing entry
|
* @param boolean $merge=false merge data with existing entry
|
||||||
|
* @param int $recur_date=0 if set, import the recurrence at this timestamp,
|
||||||
|
* default 0 => import whole series (or events, if not recurring)
|
||||||
* @desc import a SIFE into the calendar
|
* @desc import a SIFE into the calendar
|
||||||
*/
|
*/
|
||||||
function addSIF($_sifdata, $_calID=-1, $merge=false)
|
function addSIF($_sifdata, $_calID=-1, $merge=false, $recur_date=0)
|
||||||
{
|
{
|
||||||
if ($this->log)
|
if ($this->log)
|
||||||
{
|
{
|
||||||
@ -424,24 +438,167 @@ class calendar_sif extends calendar_boupdate
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($event['recur_type'] != MCAL_RECUR_NONE)
|
||||||
|
{
|
||||||
|
// Adjust the event start -- no exceptions before and at the start
|
||||||
|
$length = $event['end'] - $event['start'];
|
||||||
|
$rriter = calendar_rrule::event2rrule($event, false);
|
||||||
|
$rriter->rewind();
|
||||||
|
if (!$rriter->valid()) continue; // completely disolved into exceptions
|
||||||
|
|
||||||
|
$newstart = egw_time::to($rriter->current, 'server');
|
||||||
|
if ($newstart != $event['start'])
|
||||||
|
{
|
||||||
|
// leading exceptions skiped
|
||||||
|
$event['start'] = $newstart;
|
||||||
|
$event['end'] = $newstart + $length;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exceptions = $event['recur_exception'];
|
||||||
|
foreach($exceptions as $key => $day)
|
||||||
|
{
|
||||||
|
// remove leading exceptions
|
||||||
|
if ($day <= $event['start'])
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'(): event SERIES-MASTER skip leading exception ' .
|
||||||
|
$day . "\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
unset($exceptions[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$event['recur_exception'] = $exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recur_date) $event['recurrence'] = $recur_date;
|
||||||
|
$event_info = $this->get_event_info($event);
|
||||||
|
|
||||||
|
// common adjustments for existing events
|
||||||
|
if (is_array($event_info['stored_event']))
|
||||||
|
{
|
||||||
|
if (empty($event['uid']))
|
||||||
|
{
|
||||||
|
$event['uid'] = $event_info['stored_event']['uid']; // restore the UID if it was not delivered
|
||||||
|
}
|
||||||
|
if ($merge)
|
||||||
|
{
|
||||||
|
// overwrite with server data for merge
|
||||||
|
foreach ($event_info['stored_event'] as $key => $value)
|
||||||
|
{
|
||||||
|
if (!empty($value)) $event[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// not merge
|
||||||
|
// SIF clients do not support participants => add them back
|
||||||
|
$event['participants'] = $event_info['stored_event']['participants'];
|
||||||
|
$event['participant_types'] = $event_info['stored_event']['participant_types'];
|
||||||
|
if ($event['whole_day'] && $event['tzid'] != $event_info['stored_event']['tzid'])
|
||||||
|
{
|
||||||
|
if (!isset(self::$tz_cache[$event_info['stored_event']['tzid']]))
|
||||||
|
{
|
||||||
|
self::$tz_cache[$event_info['stored_event']['tzid']] =
|
||||||
|
calendar_timezones::DateTimeZone($event_info['stored_event']['tzid']);
|
||||||
|
}
|
||||||
|
// Adjust dates to original TZ
|
||||||
|
$time = new egw_time($event['startdate'],self::$tz_cache[$event_info['stored_event']['tzid']]);
|
||||||
|
$event['start'] = egw_time::to($time, 'server');
|
||||||
|
$time = new egw_time($event['enddate'],self::$tz_cache[$event_info['stored_event']['tzid']]);
|
||||||
|
$event['end'] = egw_time::to($time, 'server');
|
||||||
|
if ($event['recur_type'] != MCAL_RECUR_NONE)
|
||||||
|
{
|
||||||
|
foreach ($event['recur_exception'] as $key => $day)
|
||||||
|
{
|
||||||
|
$time = new egw_time($day,egw_time::$server_timezone);
|
||||||
|
$time =& $this->so->startOfDay($time, $event_info['stored_event']['tzid']);
|
||||||
|
$event['recur_exception'][$key] = egw_time::to($time,'server');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar_rrule::rrule2tz($event, $event_info['stored_event']['start'],
|
||||||
|
$event_info['stored_event']['tzid']);
|
||||||
|
|
||||||
|
$event['tzid'] = $event_info['stored_event']['tzid'];
|
||||||
|
// avoid that iCal changes the organizer, which is not allowed
|
||||||
|
$event['owner'] = $event_info['stored_event']['owner'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // common adjustments for new events
|
||||||
|
{
|
||||||
|
// set non blocking all day depending on the user setting
|
||||||
|
if (isset($event['whole_day'])
|
||||||
|
&& $event['whole_day']
|
||||||
|
&& $this->nonBlockingAllday)
|
||||||
|
{
|
||||||
|
$event['non_blocking'] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if an owner is set and the current user has add rights
|
||||||
|
// for that owners calendar; if not set the current user
|
||||||
|
if (!isset($event['owner'])
|
||||||
|
|| !$this->check_perms(EGW_ACL_ADD, 0, $event['owner']))
|
||||||
|
{
|
||||||
|
$event['owner'] = $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = $event['owner'] == $this->user ? 'A' : 'U';
|
||||||
|
$status = calendar_so::combine_status($status, 1, 'CHAIR');
|
||||||
|
$event['participants'] = array($event['owner'] => $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($event['startdate']);
|
||||||
|
unset($event['enddate']);
|
||||||
|
|
||||||
|
$alarmData = array();
|
||||||
if (isset($event['alarm']))
|
if (isset($event['alarm']))
|
||||||
{
|
{
|
||||||
$alarmData = array();
|
|
||||||
$alarmData['offset'] = $event['alarm'] * 60;
|
$alarmData['offset'] = $event['alarm'] * 60;
|
||||||
$alarmData['time'] = $event['start'] - $alarmData['offset'];
|
$alarmData['time'] = $event['start'] - $alarmData['offset'];
|
||||||
$alarmData['owner'] = $this->user;
|
$alarmData['owner'] = $this->user;
|
||||||
$alarmData['all'] = false;
|
$alarmData['all'] = false;
|
||||||
$event['alarm'] = $alarmData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_calID > 0 && ($storedEvent = $this->read($_calID)))
|
// update alarms depending on the given event type
|
||||||
|
if (!empty($alarmData) || isset($this->supportedFields['alarm']))
|
||||||
{
|
{
|
||||||
// update entry
|
switch ($event_info['type'])
|
||||||
$event['id'] = $_calID;
|
|
||||||
// delete existing alarms
|
|
||||||
if (count($storedEvent['alarm']) > 0)
|
|
||||||
{
|
{
|
||||||
foreach ($storedEvent['alarm'] as $alarm_id => $alarm_data)
|
case 'SINGLE':
|
||||||
|
case 'SERIES-MASTER':
|
||||||
|
case 'SERIES-EXCEPTION':
|
||||||
|
case 'SERIES-EXCEPTION-PROPAGATE':
|
||||||
|
if (isset($event['alarm']))
|
||||||
|
{
|
||||||
|
if (is_array($event_info['stored_event'])
|
||||||
|
&& count($event_info['stored_event']['alarm']) > 0)
|
||||||
|
{
|
||||||
|
foreach ($event_info['stored_event']['alarm'] as $alarm_id => $alarm_data)
|
||||||
|
{
|
||||||
|
if ($alarmData['time'] == $alarm_data['time'] &&
|
||||||
|
($alarm_data['all'] || $alarm_data['owner'] == $this->user))
|
||||||
|
{
|
||||||
|
unset($alarmData);
|
||||||
|
unset($event_info['stored_event']['alarm'][$alarm_id]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($alarmData)) $event['alarm'][] = $alarmData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SERIES-PSEUDO-EXCEPTION':
|
||||||
|
// nothing to do here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_array($event_info['stored_event'])
|
||||||
|
&& count($event_info['stored_event']['alarm']) > 0)
|
||||||
|
{
|
||||||
|
foreach ($event_info['stored_event']['alarm'] as $alarm_id => $alarm_data)
|
||||||
{
|
{
|
||||||
// only touch own alarms
|
// only touch own alarms
|
||||||
if ($alarm_data['all'] == false && $alarm_data['owner'] == $this->user)
|
if ($alarm_data['all'] == false && $alarm_data['owner'] == $this->user)
|
||||||
@ -451,72 +608,269 @@ class calendar_sif extends calendar_boupdate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save event depending on the given event type
|
||||||
|
switch ($event_info['type'])
|
||||||
|
{
|
||||||
|
case 'SINGLE':
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"(): event SINGLE\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the event
|
||||||
|
if ($event_info['acl_edit'])
|
||||||
|
{
|
||||||
|
// Force SINGLE
|
||||||
|
unset($event['recurrence']);
|
||||||
|
$event['reference'] = 0;
|
||||||
|
$event_to_store = $event; // prevent $event from being changed by the update method
|
||||||
|
$updated_id = $this->update($event_to_store, true);
|
||||||
|
unset($event_to_store);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SERIES-MASTER':
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"(): event SERIES-MASTER\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove all known pseudo exceptions and update the event
|
||||||
|
if ($event_info['acl_edit'])
|
||||||
|
{
|
||||||
|
$days = $this->so->get_recurrence_exceptions($event_info['stored_event'], $this->tzid, 0, 0, 'tz_map');
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."(EXCEPTIONS MAPPING):\n" .
|
||||||
|
array2string($days)."\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
if (is_array($days))
|
||||||
|
{
|
||||||
|
$exceptions = array();
|
||||||
|
foreach ($event['recur_exception'] as $recur_exception)
|
||||||
|
{
|
||||||
|
if (isset($days[$recur_exception]))
|
||||||
|
{
|
||||||
|
$exceptions[] = $days[$recur_exception];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$event['recur_exception'] = $exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event_to_store = $event; // prevent $event from being changed by the update method
|
||||||
|
$updated_id = $this->update($event_to_store, true);
|
||||||
|
unset($event_to_store);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SERIES-EXCEPTION':
|
||||||
|
case 'SERIES-EXCEPTION-PROPAGATE':
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"(): event SERIES-EXCEPTION\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update event
|
||||||
|
if ($event_info['acl_edit'])
|
||||||
|
{
|
||||||
|
if (isset($event_info['stored_event']['id']))
|
||||||
|
{
|
||||||
|
// We update an existing exception
|
||||||
|
$event['id'] = $event_info['stored_event']['id'];
|
||||||
|
$event['category'] = $event_info['stored_event']['category'];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (isset($event['whole_day'])
|
// We create a new exception
|
||||||
&& $event['whole_day']
|
unset($event['id']);
|
||||||
&& $this->nonBlockingAllday)
|
unset($event_info['stored_event']);
|
||||||
|
$event['recur_type'] = MCAL_RECUR_NONE;
|
||||||
|
$event_info['master_event']['recur_exception'] =
|
||||||
|
array_unique(array_merge($event_info['master_event']['recur_exception'],
|
||||||
|
array($event['recurrence'])));
|
||||||
|
|
||||||
|
// Adjust the event start -- must not be an exception
|
||||||
|
$length = $event_info['master_event']['end'] - $event_info['master_event']['start'];
|
||||||
|
$rriter = calendar_rrule::event2rrule($event_info['master_event'], false);
|
||||||
|
$rriter->rewind();
|
||||||
|
if ($rriter->valid())
|
||||||
{
|
{
|
||||||
$event['non_blocking'] = 1;
|
$newstart = egw_time::to($rriter->current, 'server');
|
||||||
|
foreach($event_info['master_event']['recur_exception'] as $key => $day)
|
||||||
|
{
|
||||||
|
// remove leading exceptions
|
||||||
|
if ($day < $newstart)
|
||||||
|
{
|
||||||
|
unset($event_info['master_event']['recur_exception'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($event_info['master_event']['start'] < $newstart)
|
||||||
|
{
|
||||||
|
$event_info['master_event']['start'] = $newstart;
|
||||||
|
$event_info['master_event']['end'] = $newstart + $length;
|
||||||
|
$event_to_store = $event_info['master_event']; // prevent the master_event from being changed by the update method
|
||||||
|
$this->server2usertime($event_to_store);
|
||||||
|
$this->update($event_to_store, true);
|
||||||
|
unset($event_to_store);
|
||||||
|
}
|
||||||
|
$event['reference'] = $event_info['master_event']['id'];
|
||||||
|
$event['category'] = $event_info['master_event']['category'];
|
||||||
|
$event['owner'] = $event_info['master_event']['owner'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$event_to_store = $event; // prevent $event from being changed by update method
|
||||||
|
$updated_id = $this->update($event_to_store, true);
|
||||||
|
unset($event_to_store);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SERIES-PSEUDO-EXCEPTION':
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"(): event SERIES-PSEUDO-EXCEPTION\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event_info['acl_edit'])
|
||||||
|
{
|
||||||
|
// truncate the status only exception from the series master
|
||||||
|
$recur_exceptions = array();
|
||||||
|
foreach ($event_info['master_event']['recur_exception'] as $recur_exception)
|
||||||
|
{
|
||||||
|
if ($recur_exception != $event['recurrence'])
|
||||||
|
{
|
||||||
|
$recur_exceptions[] = $recur_exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$event_info['master_event']['recur_exception'] = $recur_exceptions;
|
||||||
|
|
||||||
|
// save the series master with the adjusted exceptions
|
||||||
|
$event_to_store = $event_info['master_event']; // prevent the master_event from being changed by the update method
|
||||||
|
$updated_id = $this->update($event_to_store, true, true, false, false);
|
||||||
|
unset($event_to_store);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$eventID = $this->update($event, true);
|
// read stored event into info array for fresh stored (new) events
|
||||||
|
if (!is_array($event_info['stored_event']) && $updated_id > 0)
|
||||||
if ($eventID && $this->log)
|
|
||||||
{
|
{
|
||||||
$storedEvent = $this->read($eventID);
|
$event_info['stored_event'] = $this->read($updated_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose which id to return to the client
|
||||||
|
switch ($event_info['type'])
|
||||||
|
{
|
||||||
|
case 'SINGLE':
|
||||||
|
case 'SERIES-MASTER':
|
||||||
|
case 'SERIES-EXCEPTION':
|
||||||
|
$return_id = $updated_id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SERIES-PSEUDO-EXCEPTION':
|
||||||
|
$return_id = is_array($event_info['master_event']) ? $event_info['master_event']['id'] . ':' . $event['recurrence'] : false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SERIES-EXCEPTION-PROPAGATE':
|
||||||
|
if ($event_info['acl_edit'] && is_array($event_info['stored_event']))
|
||||||
|
{
|
||||||
|
// we had sufficient rights to propagate the status only exception to a real one
|
||||||
|
$return_id = $event_info['stored_event']['id'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we did not have sufficient rights to propagate the status only exception to a real one
|
||||||
|
// we have to keep the SERIES-PSEUDO-EXCEPTION id and keep the event untouched
|
||||||
|
$return_id = $event_info['master_event']['id'] . ':' . $event['recurrence'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
$recur_date = $this->date2usertime($event_info['stored_event']['start']);
|
||||||
|
$event_info['stored_event'] = $this->read($event_info['stored_event']['id'], $recur_date);
|
||||||
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
|
||||||
array2string($storedEvent)."\n",3,$this->logfile);
|
array2string($event_info['stored_event'])."\n",3,$this->logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $eventID;
|
return $return_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return a sife
|
* return a sife
|
||||||
*
|
*
|
||||||
* @param int $_id the id of the event
|
* @param int $_id the id of the event
|
||||||
|
* @param int $recur_date=0 if set export the next recurrence at or after the timestamp,
|
||||||
|
* default 0 => export whole series (or events, if not recurring)
|
||||||
* @return string containing the SIFE
|
* @return string containing the SIFE
|
||||||
*/
|
*/
|
||||||
function getSIF($_id)
|
function getSIF($_id, $recur_date=0)
|
||||||
{
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"($_id, $recur_date)\n",3,$this->logfile);
|
||||||
|
}
|
||||||
$sysCharSet = $GLOBALS['egw']->translation->charset();
|
$sysCharSet = $GLOBALS['egw']->translation->charset();
|
||||||
|
|
||||||
$fields = array_unique(array_values($this->sifMapping));
|
$fields = array_unique(array_values($this->sifMapping));
|
||||||
sort($fields);
|
sort($fields);
|
||||||
|
$tzid = null;
|
||||||
|
|
||||||
#$event = $this->read($_id,null,false,'server');
|
if (!($event = $this->read($_id, $recur_date, false, 'server')))
|
||||||
#error_log("FOUND EVENT: ". print_r($event, true));
|
|
||||||
|
|
||||||
if (($event = $this->read($_id,null,false,'server')))
|
|
||||||
{
|
{
|
||||||
|
if ($this->read($_id, $recur_date, true, 'server'))
|
||||||
|
{
|
||||||
|
$retval = -1; // Permission denied
|
||||||
|
if($this->xmlrpc)
|
||||||
|
{
|
||||||
|
$GLOBALS['server']->xmlrpc_error($GLOBALS['xmlrpcerr']['no_access'],
|
||||||
|
$GLOBALS['xmlrpcstr']['no_access']);
|
||||||
|
}
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() User does not have the permission to read event $_id.\n",
|
||||||
|
3,$this->logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$retval = false; // Entry does not exist
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() Event $_id not found.\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $retval;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->log)
|
if ($this->log)
|
||||||
{
|
{
|
||||||
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
|
||||||
array2string($event)."\n",3,$this->logfile);
|
array2string($event)."\n",3,$this->logfile);
|
||||||
}
|
}
|
||||||
if ($this->uidExtension)
|
|
||||||
{
|
|
||||||
if (!preg_match('/\[UID:.+\]/m', $event['description']))
|
|
||||||
{
|
|
||||||
$event['description'] .= "\n[UID:" . $event['uid'] . "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->tzid === false)
|
if ($this->tzid)
|
||||||
{
|
|
||||||
$tzid = null;
|
|
||||||
}
|
|
||||||
elseif ($this->tzid)
|
|
||||||
{
|
{
|
||||||
|
// explicit device timezone
|
||||||
$tzid = $this->tzid;
|
$tzid = $this->tzid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// use event's timezone
|
||||||
$tzid = $event['tzid'];
|
$tzid = $event['tzid'];
|
||||||
}
|
}
|
||||||
if ($tzid && $tzid != 'UTC')
|
|
||||||
|
if ($this->so->isWholeDay($event)) $event['whole_day'] = true;
|
||||||
|
|
||||||
|
if ($tzid)
|
||||||
{
|
{
|
||||||
if (!isset(self::$tz_cache[$tzid]))
|
if (!isset(self::$tz_cache[$tzid]))
|
||||||
{
|
{
|
||||||
@ -528,16 +882,78 @@ class calendar_sif extends calendar_boupdate
|
|||||||
self::$tz_cache[$event['tzid']] = calendar_timezones::DateTimeZone($event['tzid']);
|
self::$tz_cache[$event['tzid']] = calendar_timezones::DateTimeZone($event['tzid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($recur_date && ($master = $this->read($_id, 0, true, 'server')))
|
||||||
|
{
|
||||||
|
$days = $this->so->get_recurrence_exceptions($master, $tzid, 0, 0, 'tz_rrule');
|
||||||
|
if (isset($days[$recur_date]))
|
||||||
|
{
|
||||||
|
$recur_date = $days[$recur_date]; // use remote representation
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"($_id, $recur_date) Unsupported status only exception, skipped ...\n",
|
||||||
|
3,$this->logfile);
|
||||||
|
}
|
||||||
|
return false; // unsupported pseudo exception
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
$time = new egw_time($master['start'], egw_time::$server_timezone);
|
||||||
|
$time->setTimezone(self::$tz_cache[$tzid]);
|
||||||
|
$first_start = $time->format('His');
|
||||||
|
$time = new egw_time($event['start'], egw_time::$server_timezone);
|
||||||
|
$time->setTimezone(self::$tz_cache[$tzid]);
|
||||||
|
$recur_start = $time->format('His');
|
||||||
|
if ($first_start == $recur_start) return false; // Nothing to export
|
||||||
|
*/
|
||||||
|
$event['recur_type'] = MCAL_RECUR_NONE;
|
||||||
|
}
|
||||||
|
elseif (!$recur_date &&
|
||||||
|
$event['recur_type'] != MCAL_RECUR_NONE &&
|
||||||
|
!isset($event['whole_day'])) // whole-day events are not shifted
|
||||||
|
{
|
||||||
|
// Add the timezone transition related pseudo exceptions
|
||||||
|
$exceptions = $this->so->get_recurrence_exceptions($event, $tzid, 0, 0, 'tz_rrule');
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."(EXCEPTIONS)\n" .
|
||||||
|
array2string($exceptions)."\n",3,$this->logfile);
|
||||||
|
}
|
||||||
|
$event['recur_exception'] = $exceptions;
|
||||||
|
// Adjust the event start -- must not be an exception
|
||||||
|
$length = $event['end'] - $event['start'];
|
||||||
|
$rriter = calendar_rrule::event2rrule($event, false, $tzid);
|
||||||
|
$rriter->rewind();
|
||||||
|
if (!$rriter->valid()) return false; // completely disolved into exceptions
|
||||||
|
|
||||||
|
$event['start'] = egw_time::to($rriter->current, 'server');
|
||||||
|
$event['end'] = $event['start'] + $length;
|
||||||
|
foreach($exceptions as $key => $day)
|
||||||
|
{
|
||||||
|
// remove leading exceptions
|
||||||
|
if ($day <= $event['start']) unset($exceptions[$key]);
|
||||||
|
}
|
||||||
|
$event['recur_exception'] = $exceptions;
|
||||||
|
calendar_rrule::rrule2tz($event, $event['start'], $tzid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->uidExtension)
|
||||||
|
{
|
||||||
|
if (!preg_match('/\[UID:.+\]/m', $event['description']))
|
||||||
|
{
|
||||||
|
$event['description'] .= "\n[UID:" . $event['uid'] . "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$sifEvent = self::xml_decl . "<appointment>" . self::SIF_decl;
|
$sifEvent = self::xml_decl . "<appointment>" . self::SIF_decl;
|
||||||
|
|
||||||
foreach ($this->sifMapping as $sifField => $egwField)
|
foreach ($this->sifMapping as $sifField => $egwField)
|
||||||
{
|
{
|
||||||
if (empty($egwField)) continue;
|
if (empty($egwField)) continue;
|
||||||
|
|
||||||
#error_log("$sifField => $egwField");
|
|
||||||
#error_log('VALUE1: '.$event[$egwField]);
|
|
||||||
$value = $GLOBALS['egw']->translation->convert($event[$egwField], $sysCharSet, 'utf-8');
|
$value = $GLOBALS['egw']->translation->convert($event[$egwField], $sysCharSet, 'utf-8');
|
||||||
#error_log('VALUE2: '.$value);
|
|
||||||
|
|
||||||
switch ($sifField)
|
switch ($sifField)
|
||||||
{
|
{
|
||||||
@ -570,16 +986,37 @@ class calendar_sif extends calendar_boupdate
|
|||||||
// all calculations in the event's timezone
|
// all calculations in the event's timezone
|
||||||
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
||||||
$time->setTime(23, 59, 59);
|
$time->setTime(23, 59, 59);
|
||||||
$recurEndDate = $this->date2ts($time);
|
|
||||||
|
if ($tzid)
|
||||||
|
{
|
||||||
|
$time->setTimezone(self::$tz_cache[$tzid]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$time->setTimezone(egw_time::$user_timezone);
|
||||||
|
}
|
||||||
|
$recurEndDate = egw_time::to($time,'server');
|
||||||
$sifEvent .= '<NoEndDate>0</NoEndDate>';
|
$sifEvent .= '<NoEndDate>0</NoEndDate>';
|
||||||
$sifEvent .= '<PatternEndDate>'. $this->vCalendar->_exportDateTime($recurEndDate) .'</PatternEndDate>';
|
$sifEvent .= '<PatternEndDate>'. $this->vCalendar->_exportDateTime($recurEndDate) .'</PatternEndDate>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$time = new egw_time($event['start'],egw_time::$server_timezone);
|
$time = new egw_time($event['start'],egw_time::$server_timezone);
|
||||||
|
|
||||||
// all calculations in the event's timezone
|
// all calculations in the event's timezone
|
||||||
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
||||||
$time->setTime(0, 0, 0);
|
$time->setTime(0, 0, 0);
|
||||||
$recurStartDate = $this->date2ts($time);
|
|
||||||
|
if ($tzid)
|
||||||
|
{
|
||||||
|
$time->setTimezone(self::$tz_cache[$tzid]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$time->setTimezone(egw_time::$user_timezone);
|
||||||
|
}
|
||||||
|
$recurStartDate = egw_time::to($time,'server');
|
||||||
$eventInterval = ($event['recur_interval'] > 1 ? $event['recur_interval'] : 1);
|
$eventInterval = ($event['recur_interval'] > 1 ? $event['recur_interval'] : 1);
|
||||||
|
|
||||||
switch ($event['recur_type'])
|
switch ($event['recur_type'])
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -606,7 +1043,7 @@ class calendar_sif extends calendar_boupdate
|
|||||||
{
|
{
|
||||||
$daysPerWeek = substr_count(decbin($event['recur_data']),'1');
|
$daysPerWeek = substr_count(decbin($event['recur_data']),'1');
|
||||||
$totalWeeks = floor(($recurEndDate - $recurStartDate) / (86400*7));
|
$totalWeeks = floor(($recurEndDate - $recurStartDate) / (86400*7));
|
||||||
$occurrences = ($totalWeeks / $eventInterval) * $daysPerWeek;
|
$occurrences = ceil($totalWeeks / $eventInterval) * $daysPerWeek;
|
||||||
for($i = $recurEndDate; $i > $recurStartDate + ($totalWeeks * 86400*7); $i = $i - 86400)
|
for($i = $recurEndDate; $i > $recurStartDate + ($totalWeeks * 86400*7); $i = $i - 86400)
|
||||||
{
|
{
|
||||||
switch (date('w', $i-1))
|
switch (date('w', $i-1))
|
||||||
@ -668,14 +1105,23 @@ class calendar_sif extends calendar_boupdate
|
|||||||
$sifEvent .= '<Exceptions>';
|
$sifEvent .= '<Exceptions>';
|
||||||
foreach ($event['recur_exception'] as $day)
|
foreach ($event['recur_exception'] as $day)
|
||||||
{
|
{
|
||||||
if ($this->isWholeDay($event))
|
if (isset($event['whole_day']))
|
||||||
{
|
{
|
||||||
$time = new egw_time($day,egw_time::$server_timezone);
|
if (!is_a($day,'DateTime'))
|
||||||
$time->setTimezone(self::$tz_cache[$tzid]);
|
{
|
||||||
$sifEvent .= '<ExcludeDate>' . $time->format('Y-m-d') . '</ExcludeDate>';
|
$day = new egw_time($day,egw_time::$server_timezone);
|
||||||
|
$day->setTimezone(self::$tz_cache[$event['tzid']]);
|
||||||
|
}
|
||||||
|
$sifEvent .= '<ExcludeDate>' . $day->format('Y-m-d') . '</ExcludeDate>';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if ($this->log && is_a($day,'DateTime'))
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'() exception[' . $day->getTimezone()->getName() . ']: ' .
|
||||||
|
$day->format('Ymd\THis') . "\n",3,$this->logfile);
|
||||||
|
}
|
||||||
$sifEvent .= '<ExcludeDate>' . self::getDateTime($day,$tzid) . '</ExcludeDate>';
|
$sifEvent .= '<ExcludeDate>' . self::getDateTime($day,$tzid) . '</ExcludeDate>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -699,13 +1145,14 @@ class calendar_sif extends calendar_boupdate
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Start':
|
case 'Start':
|
||||||
if ($this->isWholeDay($event))
|
if (isset($event['whole_day']))
|
||||||
{
|
{
|
||||||
|
// for whole-day events we use the date in event timezone
|
||||||
$time = new egw_time($event['start'],egw_time::$server_timezone);
|
$time = new egw_time($event['start'],egw_time::$server_timezone);
|
||||||
$time->setTimezone(self::$tz_cache[$tzid]);
|
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
||||||
$sifEvent .= '<Start>' . $time->format('Y-m-d') . '</Start>';
|
$sifEvent .= '<Start>' . $time->format('Y-m-d') . '</Start>';
|
||||||
$time = new egw_time($event['end'],egw_time::$server_timezone);
|
$time = new egw_time($event['end'],egw_time::$server_timezone);
|
||||||
$time->setTimezone(self::$tz_cache[$tzid]);
|
$time->setTimezone(self::$tz_cache[$event['tzid']]);
|
||||||
$sifEvent .= '<End>' . $time->format('Y-m-d') . '</End>';
|
$sifEvent .= '<End>' . $time->format('Y-m-d') . '</End>';
|
||||||
$sifEvent .= "<AllDayEvent>1</AllDayEvent>";
|
$sifEvent .= "<AllDayEvent>1</AllDayEvent>";
|
||||||
}
|
}
|
||||||
@ -751,7 +1198,6 @@ class calendar_sif extends calendar_boupdate
|
|||||||
default:
|
default:
|
||||||
$value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
|
$value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
|
||||||
$sifEvent .= "<$sifField>$value</$sifField>";
|
$sifEvent .= "<$sifField>$value</$sifField>";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sifEvent .= "</appointment>";
|
$sifEvent .= "</appointment>";
|
||||||
@ -767,13 +1213,6 @@ class calendar_sif extends calendar_boupdate
|
|||||||
return $sifEvent;
|
return $sifEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->xmlrpc)
|
|
||||||
{
|
|
||||||
$GLOBALS['server']->xmlrpc_error($GLOBALS['xmlrpcerr']['no_access'],$GLOBALS['xmlrpcstr']['no_access']);
|
|
||||||
}
|
|
||||||
return False;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the supported fields
|
* Set the supported fields
|
||||||
*
|
*
|
||||||
|
@ -157,6 +157,7 @@ class calendar_so
|
|||||||
// We want only the parents to match
|
// We want only the parents to match
|
||||||
$where['cal_uid'] = $ids;
|
$where['cal_uid'] = $ids;
|
||||||
$where['cal_reference'] = 0;
|
$where['cal_reference'] = 0;
|
||||||
|
$where['cal_recurrence'] = 0;
|
||||||
}
|
}
|
||||||
if ((int) $recur_date)
|
if ((int) $recur_date)
|
||||||
{
|
{
|
||||||
@ -358,7 +359,7 @@ class calendar_so
|
|||||||
'cal_user_type' => $type,
|
'cal_user_type' => $type,
|
||||||
'cal_user_id' => $ids,
|
'cal_user_id' => $ids,
|
||||||
));
|
));
|
||||||
if ($type == 'u' && ($filter == 'owner' || $filter == 'all'))
|
if ($type == 'u' && ($filter == 'owner'))
|
||||||
{
|
{
|
||||||
$cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
|
$cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
|
||||||
$to_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
|
$to_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
|
||||||
@ -377,6 +378,7 @@ class calendar_so
|
|||||||
case 'rejected':
|
case 'rejected':
|
||||||
$where[] = "cal_status='R'"; break;
|
$where[] = "cal_status='R'"; break;
|
||||||
case 'all':
|
case 'all':
|
||||||
|
case 'owner':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
//if (!$show_rejected) // not longer used
|
//if (!$show_rejected) // not longer used
|
||||||
@ -473,7 +475,7 @@ class calendar_so
|
|||||||
$events[$id] = egw_db::strip_array_keys($row,'cal_');
|
$events[$id] = egw_db::strip_array_keys($row,'cal_');
|
||||||
}
|
}
|
||||||
//_debug_array($events);
|
//_debug_array($events);
|
||||||
if (count($ids))
|
if (count($events))
|
||||||
{
|
{
|
||||||
// now ready all users with the given cal_id AND (cal_recur_date=0 or the fitting recur-date)
|
// now ready all users with the given cal_id AND (cal_recur_date=0 or the fitting recur-date)
|
||||||
// This will always read the first entry of each recuring event too, we eliminate it later
|
// This will always read the first entry of each recuring event too, we eliminate it later
|
||||||
@ -669,7 +671,19 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
unset($event[$col]);
|
unset($event[$col]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_array($event['cal_category'])) $event['cal_category'] = implode(',',$event['cal_category']);
|
// ensure that we find mathing entries later on
|
||||||
|
if (!is_array($event['cal_category']))
|
||||||
|
{
|
||||||
|
$categories = array_unique(explode(',',$event['cal_category']));
|
||||||
|
sort($categories);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$categories = array_unique($event['cal_category']);
|
||||||
|
}
|
||||||
|
sort($categories, SORT_NUMERIC);
|
||||||
|
|
||||||
|
$event['cal_category'] = implode(',',$categories);
|
||||||
|
|
||||||
if ($cal_id)
|
if ($cal_id)
|
||||||
{
|
{
|
||||||
@ -1566,94 +1580,333 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
* @param string tz_id=null timezone for exports (null for event's timezone)
|
* @param string tz_id=null timezone for exports (null for event's timezone)
|
||||||
* @param int $start=0 if != 0: startdate of the search/list (servertime)
|
* @param int $start=0 if != 0: startdate of the search/list (servertime)
|
||||||
* @param int $end=0 if != 0: enddate of the search/list (servertime)
|
* @param int $end=0 if != 0: enddate of the search/list (servertime)
|
||||||
* @param string $filter='all' string filter-name: all (not rejected), accepted, unknown, tentative,
|
* @param string $filter='all' string filter-name: all (not rejected),
|
||||||
* rejected, tz_transitions (return only by timezone transition affected entries)
|
* accepted, unknown, tentative, rejected,
|
||||||
|
* rrule return array of remote exceptions in servertime
|
||||||
|
* tz_rrule/tz_only, return (only by) timezone transition affected entries
|
||||||
|
* map return array of dates with no pseudo exception
|
||||||
|
* key remote occurrence date
|
||||||
|
* tz_map return array of all dates with no tz pseudo exception
|
||||||
*
|
*
|
||||||
* @return array Array of exception days (false for non-recurring events).
|
* @return array Array of exception days (false for non-recurring events).
|
||||||
*/
|
*/
|
||||||
function get_recurrence_exceptions(&$event, $tz_id=null, $start=0, $end=0, $filter='all')
|
function get_recurrence_exceptions($event, $tz_id=null, $start=0, $end=0, $filter='all')
|
||||||
{
|
{
|
||||||
|
if (!is_array($event)) return false;
|
||||||
$cal_id = (int) $event['id'];
|
$cal_id = (int) $event['id'];
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// "($cal_id, $tz_id, $filter): " . $event['tzid']);
|
||||||
if (!$cal_id || $event['recur_type'] == MCAL_RECUR_NONE) return false;
|
if (!$cal_id || $event['recur_type'] == MCAL_RECUR_NONE) return false;
|
||||||
|
|
||||||
$days = array();
|
$days = array();
|
||||||
$first_start = $recur_start = '';
|
|
||||||
|
|
||||||
if (!empty($tz_id))
|
$expand_all = (!$this->isWholeDay($event) && $tz_id && $tz_id != $event['tzid']);
|
||||||
|
|
||||||
|
if ($filter == 'tz_only' && !$expand_all) return $days;
|
||||||
|
|
||||||
|
$remote = in_array($filter, array('tz_rrule', 'rrule'));
|
||||||
|
|
||||||
|
$egw_rrule = calendar_rrule::event2rrule($event, false);
|
||||||
|
$egw_rrule->rewind();
|
||||||
|
if ($expand_all)
|
||||||
{
|
{
|
||||||
// set export timezone
|
unset($event['recur_excpetion']);
|
||||||
if(!isset(self::$tz_cache[$tz_id]))
|
$remote_rrule = calendar_rrule::event2rrule($event, false, $tz_id);
|
||||||
{
|
$remote_rrule->rewind();
|
||||||
self::$tz_cache[$tz_id] = calendar_timezones::DateTimeZone($tz_id);
|
|
||||||
}
|
}
|
||||||
$starttime = new egw_time($event['start'], egw_time::$server_timezone);
|
while ($egw_rrule->valid())
|
||||||
$starttime->setTimezone(self::$tz_cache[$tz_id]);
|
{
|
||||||
$first_start = $starttime->format('His');
|
$day = $egw_rrule->current();
|
||||||
|
$locts = (int)egw_time::to($day,'server');
|
||||||
|
$tz_exception = ($filter == 'tz_rrule');
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// '()[EVENT Server]: ' . $day->format('Ymd\THis') . " ($locts)");
|
||||||
|
if ($expand_all)
|
||||||
|
{
|
||||||
|
$remote_day = $remote_rrule->current();
|
||||||
|
$remts = (int)egw_time::to($remote_day,'server');
|
||||||
|
// error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// '()[EVENT Device]: ' . $remote_day->format('Ymd\THis') . " ($remts)");
|
||||||
}
|
}
|
||||||
|
|
||||||
$participants = $this->get_participants($event['id'], 0);
|
|
||||||
|
|
||||||
// Check if the stati for all participants are identical for all recurrences
|
if (!($end && $end < $locts) && $start <= $locts)
|
||||||
foreach ($participants as $uid => $attendee)
|
|
||||||
{
|
{
|
||||||
switch ($attendee['type'])
|
// we are within the relevant time period
|
||||||
|
if ($expand_all && $day->format('U') != $remote_day->format('U'))
|
||||||
|
{
|
||||||
|
$tz_exception = true;
|
||||||
|
if ($filter != 'map' && $filter != 'tz_map')
|
||||||
|
{
|
||||||
|
// timezone pseudo exception
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// '() tz exception: ' . $day->format('Ymd\THis'));
|
||||||
|
if ($remote)
|
||||||
|
{
|
||||||
|
$days[$locts]= $remts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$days[$remts]= $locts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($filter != 'tz_map' && (!$tz_exception || $filter == 'tz_only') &&
|
||||||
|
$this->status_pseudo_exception($event['id'], $locts, $filter))
|
||||||
|
{
|
||||||
|
// status pseudo exception
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// '() status exception: ' . $day->format('Ymd\THis'));
|
||||||
|
if ($expand_all)
|
||||||
|
{
|
||||||
|
$remts = (int)egw_time::to($remote_day,'server');
|
||||||
|
if ($filter == 'tz_only')
|
||||||
|
{
|
||||||
|
unset($days[$remts]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($filter != 'map')
|
||||||
|
{
|
||||||
|
if ($remote)
|
||||||
|
{
|
||||||
|
$days[$locts]= $remts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$days[$remts]= $locts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($filter != 'map')
|
||||||
|
{
|
||||||
|
$days[$locts]= $locts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (($filter == 'map' || filter == 'tz_map') &&
|
||||||
|
!$tz_exception)
|
||||||
|
{
|
||||||
|
// no pseudo exception date
|
||||||
|
if ($expand_all)
|
||||||
|
{
|
||||||
|
|
||||||
|
$days[$remts]= $locts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$days[$locts]= $locts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do
|
||||||
|
{
|
||||||
|
$egw_rrule->next_no_exception();
|
||||||
|
$day = $egw_rrule->current();
|
||||||
|
if ($expand_all)
|
||||||
|
{
|
||||||
|
$remote_rrule->next_no_exception();
|
||||||
|
$remts = (int)egw_time::to($remote_rrule->current(),'server');
|
||||||
|
}
|
||||||
|
$exception = $egw_rrule->exceptions &&
|
||||||
|
in_array($day->format('Ymd'),$egw_rrule->exceptions);
|
||||||
|
if (in_array($filter, array('map','tz_map','rrule','tz_rrule'))
|
||||||
|
&& $exception)
|
||||||
|
{
|
||||||
|
// real exception
|
||||||
|
$locts = (int)egw_time::to($day,'ts');
|
||||||
|
if ($expand_all)
|
||||||
|
{
|
||||||
|
if ($remote)
|
||||||
|
{
|
||||||
|
$days[$locts]= $remts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$days[$remts]= $locts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$days[$locts]= $locts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ($exception);
|
||||||
|
}
|
||||||
|
return $days;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for status only pseudo exceptions
|
||||||
|
*
|
||||||
|
* @param int $cal_id event id
|
||||||
|
* @param int $recur_date occurrence to check
|
||||||
|
* @param string $filter status filter criteria for user
|
||||||
|
*
|
||||||
|
* @return boolean true, if stati don't match with defaults
|
||||||
|
*/
|
||||||
|
function status_pseudo_exception($cal_id, $recur_date, $filter)
|
||||||
|
{
|
||||||
|
static $recurrence_zero;
|
||||||
|
static $cached_id;
|
||||||
|
static $user;
|
||||||
|
|
||||||
|
if (!isset($cached_id) || $cached_id != $cal_id)
|
||||||
|
{
|
||||||
|
// get default stati
|
||||||
|
$recurrence_zero = array();
|
||||||
|
$user = $GLOBALS['egw_info']['user']['account_id'];
|
||||||
|
$where = array('cal_id' => $cal_id,
|
||||||
|
'cal_recur_date' => 0);
|
||||||
|
foreach ($this->db->select($this->user_table,'cal_user_id,cal_user_type,cal_status',$where,
|
||||||
|
__LINE__,__FILE__,false,'','calendar') as $row)
|
||||||
|
{
|
||||||
|
switch ($row['cal_user_type'])
|
||||||
{
|
{
|
||||||
case 'u': // account
|
case 'u': // account
|
||||||
case 'c': // contact
|
case 'c': // contact
|
||||||
case 'e': // email address
|
case 'e': // email address
|
||||||
$recurrences = $this->get_recurrences($event['id'], $uid, $start, $end);
|
$uid = self::combine_user($row['cal_user_type'], $row['cal_user_id']);
|
||||||
foreach ($recurrences as $recur_date => $recur_status)
|
$recurrence_zero[$uid] = $row['cal_status'];
|
||||||
{
|
|
||||||
if ($recur_date)
|
|
||||||
{
|
|
||||||
if (!empty($tz_id))
|
|
||||||
{
|
|
||||||
$time = new egw_time($recur_date, egw_time::$server_timezone);
|
|
||||||
$time->setTimezone(self::$tz_cache[$tz_id]);
|
|
||||||
$recur_start = $time->format('His');
|
|
||||||
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
|
||||||
// "() first=$first_start <> current=$recur_start");
|
|
||||||
}
|
}
|
||||||
if ($attendee['type'] = 'u' &&
|
}
|
||||||
$attendee['id'] == $GLOBALS['egw_info']['user']['account_id'])
|
$cached_id = $cal_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// "($cal_id, $recur_date, $filter)[DEFAULTS]: " .
|
||||||
|
// array2string($recurrence_zero));
|
||||||
|
|
||||||
|
$participants = array();
|
||||||
|
$where = array('cal_id' => $cal_id,
|
||||||
|
'cal_recur_date' => $recur_date);
|
||||||
|
foreach ($this->db->select($this->user_table,'cal_user_id,cal_user_type,cal_status',$where,
|
||||||
|
__LINE__,__FILE__,false,'','calendar') as $row)
|
||||||
|
{
|
||||||
|
switch ($row['cal_user_type'])
|
||||||
|
{
|
||||||
|
case 'u': // account
|
||||||
|
case 'c': // contact
|
||||||
|
case 'e': // email address
|
||||||
|
$uid = self::combine_user($row['cal_user_type'], $row['cal_user_id']);
|
||||||
|
$participants[$uid] = $row['cal_status'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($participants)) return false; // occurrence does not exist at all yet
|
||||||
|
|
||||||
|
foreach ($recurrence_zero as $uid => $status)
|
||||||
|
{
|
||||||
|
if ($uid == $user)
|
||||||
{
|
{
|
||||||
// handle filter for current user
|
// handle filter for current user
|
||||||
switch ($filter)
|
switch ($filter)
|
||||||
{
|
{
|
||||||
case 'unknown':
|
case 'unknown':
|
||||||
if ($recur_status != 'U') continue;
|
if ($status != 'U')
|
||||||
|
{
|
||||||
|
unset($participants[$uid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'accepted':
|
case 'accepted':
|
||||||
if ($recur_status != 'A') continue;
|
if ($status != 'A')
|
||||||
|
{
|
||||||
|
unset($participants[$uid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'tentative':
|
case 'tentative':
|
||||||
if ($recur_status != 'T') continue;
|
if ($status != 'T')
|
||||||
|
{
|
||||||
|
unset($participants[$uid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
if ($recur_status != 'R') continue;
|
if ($status != 'R')
|
||||||
|
{
|
||||||
|
unset($participants[$uid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'default':
|
case 'default':
|
||||||
if ($recur_status == 'R') continue;
|
if ($status == 'R')
|
||||||
|
{
|
||||||
|
unset($participants[$uid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// All entries
|
// All entries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (($filter != 'tz_transitions' && $recur_status != $recurrences[0])
|
if (!isset($participants[$uid])
|
||||||
|| !empty($tz_id) && $first_start != $recur_start)
|
|| $participants[$uid] != $status)
|
||||||
|
return true;
|
||||||
|
unset($participants[$uid]);
|
||||||
|
}
|
||||||
|
return (!empty($participants));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the event is the whole day
|
||||||
|
*
|
||||||
|
* @param array $event event (all timestamps in servertime)
|
||||||
|
* @return boolean true if whole day event within its timezone, false othwerwise
|
||||||
|
*/
|
||||||
|
function isWholeDay($event)
|
||||||
{
|
{
|
||||||
// Every distinct status or starttime results in an exception
|
if (!isset($event['start']) || !isset($event['end'])) return false;
|
||||||
$days[] = $recur_date;
|
|
||||||
|
if (empty($event['tzid']))
|
||||||
|
{
|
||||||
|
$timezone = egw_time::$server_timezone;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!isset(self::$tz_cache[$event['tzid']]))
|
||||||
|
{
|
||||||
|
self::$tz_cache[$event['tzid']] = calendar_timezones::DateTimeZone($event['tzid']);
|
||||||
}
|
}
|
||||||
|
$timezone = self::$tz_cache[$event['tzid']];
|
||||||
}
|
}
|
||||||
break;
|
$start = new egw_time($event['start'],egw_time::$server_timezone);
|
||||||
default: // We don't handle the rest
|
$start->setTimezone($timezone);
|
||||||
break;
|
$end = new egw_time($event['end'],egw_time::$server_timezone);
|
||||||
|
$end->setTimezone($timezone);
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// '(): ' . $start . '-' . $end);
|
||||||
|
$start = egw_time::to($start,'array');
|
||||||
|
$end = egw_time::to($end,'array');
|
||||||
|
|
||||||
|
|
||||||
|
return !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a datetime to the beginning og the day within timezone
|
||||||
|
*
|
||||||
|
* @param egw_time &time the datetime entry
|
||||||
|
* @param string tz_id timezone
|
||||||
|
*
|
||||||
|
* @return DateTime
|
||||||
|
*/
|
||||||
|
function &startOfDay(egw_time $time, $tz_id)
|
||||||
|
{
|
||||||
|
if (empty($tz_id))
|
||||||
|
{
|
||||||
|
$timezone = egw_time::$server_timezone;
|
||||||
}
|
}
|
||||||
$days = array_unique($days);
|
else
|
||||||
sort($days);
|
{
|
||||||
return $days;
|
if (!isset(self::$tz_cache[$tz_id]))
|
||||||
|
{
|
||||||
|
self::$tz_cache[$tz_id] = calendar_timezones::DateTimeZone($tz_id);
|
||||||
|
}
|
||||||
|
$timezone = self::$tz_cache[$tz_id];
|
||||||
|
}
|
||||||
|
return new egw_time($time->format('Y-m-d 00:00:00'), $timezone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ class Horde_SyncML_State {
|
|||||||
$this->setPassword($password);
|
$this->setPassword($password);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_isAuthorized = false;
|
$this->_isAuthorized = 0;
|
||||||
$this->_isAuthConfirmed = false;
|
$this->_isAuthConfirmed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,12 +560,12 @@ class Horde_SyncML_State {
|
|||||||
|
|
||||||
if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text','u'))
|
if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text','u'))
|
||||||
{
|
{
|
||||||
$this->_isAuthorized = true;
|
$this->_isAuthorized = 1;
|
||||||
#Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
#Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->_isAuthorized = false;
|
$this->_isAuthorized = -1;
|
||||||
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,7 +577,7 @@ class Horde_SyncML_State {
|
|||||||
Horde::logMessage('SyncML_EGW: egw session('.$sessionID. ') not verified ' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
Horde::logMessage('SyncML_EGW: egw session('.$sessionID. ') not verified ' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_isAuthorized;
|
return ($this->_isAuthorized > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAuthConfirmed()
|
function isAuthConfirmed()
|
||||||
|
@ -60,6 +60,12 @@ class EGW_SyncML_State extends Horde_SyncML_State
|
|||||||
|
|
||||||
$ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action);
|
$ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action);
|
||||||
|
|
||||||
|
if (strstr($_id, ':')) {
|
||||||
|
// pseudo entries are related to parent entry
|
||||||
|
$parentId = array_shift(explode(':', $_id));
|
||||||
|
$pts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $parentId, $_action);
|
||||||
|
if ($pts > $ts) $ts = $pts; // We have changed the parent
|
||||||
|
}
|
||||||
return $ts;
|
return $ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,18 +342,18 @@ class EGW_SyncML_State extends Horde_SyncML_State
|
|||||||
|
|
||||||
if (($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text'))) {
|
if (($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text'))) {
|
||||||
if ($GLOBALS['egw_info']['user']['apps']['syncml']) {
|
if ($GLOBALS['egw_info']['user']['apps']['syncml']) {
|
||||||
$this->_isAuthorized = true;
|
$this->_isAuthorized = 1;
|
||||||
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded',
|
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded',
|
||||||
__FILE__, __LINE__, PEAR_LOG_DEBUG);
|
__FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||||
} else {
|
} else {
|
||||||
$this->_isAuthorized = false;
|
$this->_isAuthorized = -1; // Authentication failed!
|
||||||
Horde::logMessage('SyncML is not enabled for user ' . $this->_locName,
|
Horde::logMessage('SyncML is not enabled for user ' . $this->_locName,
|
||||||
__FILE__, __LINE__, PEAR_LOG_ERROR);
|
__FILE__, __LINE__, PEAR_LOG_ERROR);
|
||||||
}
|
}
|
||||||
return $this->_isAuthorized;
|
return ($this->_isAuthorized > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->_isAuthorized = false;
|
$this->_isAuthorized = -1;
|
||||||
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' ,
|
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' ,
|
||||||
__FILE__, __LINE__, PEAR_LOG_INFO);
|
__FILE__, __LINE__, PEAR_LOG_INFO);
|
||||||
} else {
|
} else {
|
||||||
@ -359,7 +365,7 @@ class EGW_SyncML_State extends Horde_SyncML_State
|
|||||||
__FILE__, __LINE__, PEAR_LOG_WARNING);
|
__FILE__, __LINE__, PEAR_LOG_WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->_isAuthorized;
|
return ($this->_isAuthorized > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,6 +89,9 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy
|
|||||||
|
|
||||||
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
|
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
|
||||||
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
|
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
|
||||||
|
|
||||||
|
if ($c === false) continue; // no content to export
|
||||||
|
|
||||||
if (is_a($c, 'PEAR_Error')) {
|
if (is_a($c, 'PEAR_Error')) {
|
||||||
Horde::logMessage("SyncML: refresh failed to export guid $guid:\n" . print_r($c, true),
|
Horde::logMessage("SyncML: refresh failed to export guid $guid:\n" . print_r($c, true),
|
||||||
__FILE__, __LINE__, PEAR_LOG_WARNING);
|
__FILE__, __LINE__, PEAR_LOG_WARNING);
|
||||||
|
@ -96,6 +96,9 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
|
|||||||
|
|
||||||
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
|
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
|
||||||
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
|
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
|
||||||
|
|
||||||
|
if ($c === false) continue; // no content to export
|
||||||
|
|
||||||
if (is_a($c, 'PEAR_Error')) {
|
if (is_a($c, 'PEAR_Error')) {
|
||||||
Horde::logMessage("SyncML: slowsync failed to export guid $guid:\n" . print_r($c, true),
|
Horde::logMessage("SyncML: slowsync failed to export guid $guid:\n" . print_r($c, true),
|
||||||
__FILE__, __LINE__, PEAR_LOG_WARNING);
|
__FILE__, __LINE__, PEAR_LOG_WARNING);
|
||||||
@ -235,7 +238,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
|
|||||||
$guid = false;
|
$guid = false;
|
||||||
|
|
||||||
$guid = $registry->call($hordeType . '/search',
|
$guid = $registry->call($hordeType . '/search',
|
||||||
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $state->getGlobalUID($type, $syncItem->getLocURI()) ));
|
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $state->getGlobalUID($type, $syncItem->getLocURI()), $type));
|
||||||
|
|
||||||
if ($guid) {
|
if ($guid) {
|
||||||
// Check if the found entry came from the client
|
// Check if the found entry came from the client
|
||||||
|
@ -135,6 +135,9 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
|
|||||||
'guid' => $guid,
|
'guid' => $guid,
|
||||||
'contentType' => $contentType
|
'contentType' => $contentType
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if ($c === false) continue; // no content to export
|
||||||
|
|
||||||
if (is_a($c, 'PEAR_Error')) {
|
if (is_a($c, 'PEAR_Error')) {
|
||||||
// Item in history but not in database. Strange, but can happen.
|
// Item in history but not in database. Strange, but can happen.
|
||||||
Horde :: logMessage("SyncML: change: export of guid $guid failed:\n" . print_r($c, true),
|
Horde :: logMessage("SyncML: change: export of guid $guid failed:\n" . print_r($c, true),
|
||||||
@ -343,13 +346,14 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
|
|||||||
|
|
||||||
// Create an Add request for client.
|
// Create an Add request for client.
|
||||||
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
|
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
|
||||||
|
|
||||||
$c = $registry->call($hordeType . '/export', array (
|
$c = $registry->call($hordeType . '/export', array (
|
||||||
'guid' => $guid,
|
'guid' => $guid,
|
||||||
'contentType' => $contentType,
|
'contentType' => $contentType,
|
||||||
|
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if ($c === false) continue; // no content to export
|
||||||
|
|
||||||
if (is_a($c, 'PEAR_Error')) {
|
if (is_a($c, 'PEAR_Error')) {
|
||||||
// Item in history but not in database. Strange, but can happen.
|
// Item in history but not in database. Strange, but can happen.
|
||||||
Horde :: logMessage("SyncML: add: export of guid $guid failed:\n" . print_r($c, true),
|
Horde :: logMessage("SyncML: add: export of guid $guid failed:\n" . print_r($c, true),
|
||||||
@ -406,7 +410,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
|
|||||||
function loadData() {
|
function loadData() {
|
||||||
global $registry;
|
global $registry;
|
||||||
|
|
||||||
$state = & $_SESSION['SyncML.state'];
|
$state =& $_SESSION['SyncML.state'];
|
||||||
$syncType = $this->_targetLocURI;
|
$syncType = $this->_targetLocURI;
|
||||||
$hordeType = $state->getHordeType($syncType);
|
$hordeType = $state->getHordeType($syncType);
|
||||||
$refts = $state->getServerAnchorLast($syncType);
|
$refts = $state->getServerAnchorLast($syncType);
|
||||||
@ -421,15 +425,29 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
|
|||||||
|
|
||||||
$state->setAddedItems($syncType, $addedItems);
|
$state->setAddedItems($syncType, $addedItems);
|
||||||
|
|
||||||
$state->setDeletedItems($syncType, $registry->call($hordeType . '/listBy', array (
|
$changedItems =& $state->getChangedItems($syncType);
|
||||||
|
|
||||||
|
$deletedItems =& $registry->call($hordeType . '/listBy', array (
|
||||||
'action' => 'delete',
|
'action' => 'delete',
|
||||||
'timestamp' => $refts,
|
'timestamp' => $refts,
|
||||||
'type' => $syncType,
|
'type' => $syncType,
|
||||||
'filter' => $this->_filterExpression
|
'filter' => $this->_filterExpression
|
||||||
)));
|
));
|
||||||
|
foreach ($deletedItems as $guid)
|
||||||
|
{
|
||||||
|
if (strstr($guid, ':'))
|
||||||
|
{
|
||||||
|
$parentGUID = array_shift(explode(':', $guid));
|
||||||
|
if (!in_array($parentGUID, $changedItems))
|
||||||
|
{
|
||||||
|
$changedItems[] = $parentGUID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$state->setDeletedItems($syncType, $deletedItems);
|
||||||
|
|
||||||
$this->_syncDataLoaded = TRUE;
|
$this->_syncDataLoaded = true;
|
||||||
|
|
||||||
return count($state->getChangedItems($syncType)) + count($state->getDeletedItems($syncType)) + count($state->getAddedItems($syncType)) + count($state->getConflictItems($syncType));
|
return count($changedItems) + count($deletedItems) + count($addedItems) + count($state->getConflictItems($syncType));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -735,9 +735,9 @@ class Horde_iCalendar {
|
|||||||
foreach ($values[1] as $value) {
|
foreach ($values[1] as $value) {
|
||||||
if ((isset($params['VALUE'])
|
if ((isset($params['VALUE'])
|
||||||
&& $params['VALUE'] == 'DATE') || (!isset($params['VALUE']) && $isDate)) {
|
&& $params['VALUE'] == 'DATE') || (!isset($params['VALUE']) && $isDate)) {
|
||||||
$dates[] = $this->_parseDate($value);
|
$dates[] = $this->_parseDate(trim($value));
|
||||||
} else {
|
} else {
|
||||||
$dates[] = $this->_parseDateTime($value, $tzid);
|
$dates[] = $this->_parseDateTime(trim($value), $tzid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->setAttribute($tag, isset($dates[0]) ? $dates[0] : null, $params, true, $dates);
|
$this->setAttribute($tag, isset($dates[0]) ? $dates[0] : null, $params, true, $dates);
|
||||||
@ -939,14 +939,54 @@ class Horde_iCalendar {
|
|||||||
case 'DCREATED':
|
case 'DCREATED':
|
||||||
case 'LAST-MODIFIED':
|
case 'LAST-MODIFIED':
|
||||||
$value = $this->_exportDateTime($value);
|
$value = $this->_exportDateTime($value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
// Support additional fields after date.
|
||||||
|
case 'AALARM':
|
||||||
|
case 'DALARM':
|
||||||
|
if (isset($params['VALUE'])) {
|
||||||
|
if ($params['VALUE'] == 'DATE') {
|
||||||
|
// VCALENDAR 1.0 uses T000000 - T235959 for all day events:
|
||||||
|
if ($this->isOldFormat() && $name == 'DTEND') {
|
||||||
|
$d = new Horde_Date($value);
|
||||||
|
$value = new Horde_Date(array(
|
||||||
|
'year' => $d->year,
|
||||||
|
'month' => $d->month,
|
||||||
|
'mday' => $d->mday - 1));
|
||||||
|
$value->correct();
|
||||||
|
$value = $this->_exportDate($value, '235959');
|
||||||
|
} else {
|
||||||
|
$value = $this->_exportDate($value, '000000');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = $this->_exportDateTime($value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = $this->_exportDateTime($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($attribute['values']) &&
|
||||||
|
count($attribute['values']) > 0) {
|
||||||
|
$values = $attribute['values'];
|
||||||
|
if ($this->isOldFormat()) {
|
||||||
|
$values = str_replace(';', '\\;', $values);
|
||||||
|
} else {
|
||||||
|
// As of rfc 2426 2.5 semicolon and comma must be
|
||||||
|
// escaped.
|
||||||
|
$values = str_replace(array('\\', ';', ','),
|
||||||
|
array('\\\\', '\\;', '\\,'),
|
||||||
|
$values);
|
||||||
|
}
|
||||||
|
$value .= ';' . implode(';', $values);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'DTEND':
|
case 'DTEND':
|
||||||
case 'DTSTART':
|
case 'DTSTART':
|
||||||
case 'DTSTAMP':
|
case 'DTSTAMP':
|
||||||
case 'DUE':
|
case 'DUE':
|
||||||
case 'AALARM':
|
|
||||||
case 'DALARM':
|
|
||||||
case 'RECURRENCE-ID':
|
case 'RECURRENCE-ID':
|
||||||
case 'X-RECURRENCE-ID':
|
case 'X-RECURRENCE-ID':
|
||||||
if (isset($params['VALUE'])) {
|
if (isset($params['VALUE'])) {
|
||||||
|
Loading…
Reference in New Issue
Block a user