- merged SyncML-1.2 branch with trunk:

svn merge -r 26935:HEAD ^/branches/SyncML-1.2/calendar .
- with the exception of class.calendar_uiforms.inc.php,
  as it was not updated with the latest changes from trunk
  and I'm not sure about the changes
--> needs further discussion, sorry :-(
svn revert inc/class.calendar_uiforms.inc.php
This commit is contained in:
Ralf Becker 2009-07-15 20:35:56 +00:00
parent 036b2cd14e
commit cb0fc5db82
5 changed files with 1908 additions and 1141 deletions

View File

@ -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) 2004-8 by RalfBecker-At-outdoor-training.de * @copyright (c) 2004-8 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$
@ -134,6 +135,7 @@ class calendar_bo
*/ */
protected static $cached_event = array(); protected static $cached_event = array();
protected static $cached_event_date_format = false; protected static $cached_event_date_format = false;
protected static $cached_event_date = 0;
/** /**
* @var array $cached_holidays holidays plus birthdays (gets cached in the session for performance reasons) * @var array $cached_holidays holidays plus birthdays (gets cached in the session for performance reasons)
*/ */
@ -570,9 +572,17 @@ class calendar_bo
$events = array(); $events = array();
$this->insert_all_repetitions($event,$start,$this->date2ts($this->config['horizont'],true),$events,null); $this->insert_all_repetitions($event,$start,$this->date2ts($this->config['horizont'],true),$events,null);
$days = $this->so->get_recurrence_exceptions($event);
$days = is_array($days) ? $days : array();
//error_log('set_recurrences: days' . print_r($days, true) );
foreach($events as $event) foreach($events as $event)
{ {
//error_log('set_recurrences: start = ' . $event['start'] );
if (in_array($event['start'], $days))
{
// we don't change the stati of recurrence exceptions
$event['participants'] = array();
}
$this->so->recurrence($event['id'],$this->date2ts($event['start'],true),$this->date2ts($event['end'],true),$event['participants']); $this->so->recurrence($event['id'],$this->date2ts($event['start'],true),$this->date2ts($event['end'],true),$event['participants']);
} }
} }
@ -657,8 +667,8 @@ class calendar_bo
if ($ignore_acl || is_array($ids) || ($return = $this->check_perms(EGW_ACL_READ,$ids,0,$date_format,$date))) if ($ignore_acl || is_array($ids) || ($return = $this->check_perms(EGW_ACL_READ,$ids,0,$date_format,$date)))
{ {
if (is_array($ids) || !isset(self::$cached_event['id']) || self::$cached_event['id'] != $ids || if (is_array($ids) || !isset(self::$cached_event['id']) || self::$cached_event['id'] != $ids ||
self::$cached_event_date_format != $date_format || self::$cached_event_date_format != $date_format ||
self::$cached_event['recur_type'] != MCAL_RECUR_NONE && !is_null($date) && (!$date || self::$cached_event['start'] < $date)) self::$cached_event['recur_type'] != MCAL_RECUR_NONE && !is_null($date) && self::$cached_event_date != $date || (!$date || self::$cached_event['start'] < $date))
{ {
$events = $this->so->read($ids,$date ? $this->date2ts($date,true) : 0); $events = $this->so->read($ids,$date ? $this->date2ts($date,true) : 0);
@ -674,6 +684,7 @@ class calendar_bo
{ {
self::$cached_event = array_shift($events); self::$cached_event = array_shift($events);
self::$cached_event_date_format = $date_format; self::$cached_event_date_format = $date_format;
self::$cached_event_date = $date;
$return =& self::$cached_event; $return =& self::$cached_event;
} }
} }
@ -753,6 +764,8 @@ class calendar_bo
{ {
$search_date_ymd = (int)$this->date2string($ts); $search_date_ymd = (int)$this->date2string($ts);
//error_log('insert_all_repetitions search_date = ' . $search_date_ymd . ' => ' . print_r($recur_exceptions, true));
$have_exception = !is_null($recur_exceptions) && isset($recur_exceptions[$search_date_ymd]); $have_exception = !is_null($recur_exceptions) && isset($recur_exceptions[$search_date_ymd]);
if (!$have_exception) // no execption by an edited event => check the deleted ones if (!$have_exception) // no execption by an edited event => check the deleted ones
@ -928,7 +941,7 @@ class calendar_bo
{ {
if (is_numeric($uid)) if (is_numeric($uid))
{ {
$info = array( $info = array(
'res_id' => $uid, 'res_id' => $uid,
'email' => $GLOBALS['egw']->accounts->id2name($uid,'account_email'), 'email' => $GLOBALS['egw']->accounts->id2name($uid,'account_email'),
'name' => trim($GLOBALS['egw']->accounts->id2name($uid,'account_firstname'). ' ' . 'name' => trim($GLOBALS['egw']->accounts->id2name($uid,'account_firstname'). ' ' .
@ -1840,4 +1853,20 @@ class calendar_bo
$GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php?user='.urlencode($user). $GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php?user='.urlencode($user).
($pw ? '&password='.urlencode($pw) : ''); ($pw ? '&password='.urlencode($pw) : '');
} }
/**
* Check if the event is the whole day
*
* @param event
* @return boolean true for whole day events
*/
function isWholeDay($event)
{
// check if the event is the whole day
$start = $this->date2array($event['start']);
$end = $this->date2array($event['end']);
$result = (!$start['hour'] && !$start['minute']
&& $end['hour'] == 23 && $end['minute'] == 59);
return $result;
}
} }

View File

@ -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) 2005-8 by RalfBecker-At-outdoor-training.de * @copyright (c) 2005-8 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$
@ -73,7 +74,7 @@ class calendar_boupdate extends calendar_bo
* @param array &$event event-array, on return some values might be changed due to set defaults * @param array &$event event-array, on return some values might be changed due to set defaults
* @param boolean $ignore_conflicts=false just ignore conflicts or do a conflict check and return the conflicting events * @param boolean $ignore_conflicts=false just ignore conflicts or do a conflict check and return the conflicting events
* @param boolean $touch_modified=true touch modificatin time and set modifing user, default true=yes * @param boolean $touch_modified=true touch modificatin time and set modifing user, default true=yes
* @param boolean $ignore_acl=flase should we ignore the acl * @param boolean $ignore_acl=false should we ignore the acl
* @return mixed on success: int $cal_id > 0, on error false or array with conflicting events (only if $check_conflicts) * @return mixed on success: int $cal_id > 0, on error false or array with conflicting events (only if $check_conflicts)
* Please note: the events are not garantied to be readable by the user (no read grant or private)! * Please note: the events are not garantied to be readable by the user (no read grant or private)!
*/ */
@ -256,7 +257,7 @@ class calendar_boupdate extends calendar_bo
//echo "saving $event[id]="; _debug_array($event); //echo "saving $event[id]="; _debug_array($event);
$event2save = $event; $event2save = $event;
if (!($cal_id = $this->save($event))) if (!($cal_id = $this->save($event, $ignore_acl)))
{ {
return $cal_id; return $cal_id;
} }
@ -572,7 +573,7 @@ class calendar_boupdate extends calendar_bo
break; break;
case 'ical': case 'ical':
$ics = ExecMethod2('calendar.calendar_ical.exportVCal',$event['id'],'2.0',$method,false); $ics = ExecMethod2('calendar.calendar_ical.exportVCal',$event['id'],'2.0',$method);
if ($method == 'REQUEST') if ($method == 'REQUEST')
{ {
$attachment = array( 'string' => $ics, $attachment = array( 'string' => $ics,
@ -666,15 +667,16 @@ class calendar_boupdate extends calendar_bo
* This methode converts from user to server time and handles the insertion of users and dates of repeating events * This methode converts from user to server time and handles the insertion of users and dates of repeating events
* *
* @param array $event * @param array $event
* @param boolean $ignore_acl=false should we ignore the acl
* @return int/boolean $cal_id > 0 or false on error (eg. permission denied) * @return int/boolean $cal_id > 0 or false on error (eg. permission denied)
*/ */
function save($event) function save($event, $ignore_acl=false)
{ {
//error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$etag)"); //error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$etag)");
// check if user has the permission to update / create the event // check if user has the permission to update / create the event
if ($event['id'] && !$this->check_perms(EGW_ACL_EDIT,$event['id']) || if (!$ignore_acl && ($event['id'] && !$this->check_perms(EGW_ACL_EDIT,$event['id']) ||
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner']) && !$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner']) &&
!$this->check_perms(EGW_ACL_ADD,0,$event['owner'])) !$this->check_perms(EGW_ACL_ADD,0,$event['owner'])))
{ {
return false; return false;
} }
@ -705,10 +707,11 @@ class calendar_boupdate extends calendar_bo
$event['alarm'][$id]['time'] = $this->date2ts($alarm['time'],true); $event['alarm'][$id]['time'] = $this->date2ts($alarm['time'],true);
} }
} }
if (($cal_id = $this->so->save($event,$set_recurrences,NULL,$event['etag'])) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE) $set_recurrences = false;
if (($cal_id = $this->so->save($event,$set_recurrences,0,$event['etag'])) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE)
{ {
$save_event['id'] = $cal_id; $save_event['id'] = $cal_id;
$this->set_recurrences($save_event); $this->set_recurrences($save_event, 0);
} }
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,$event['id'] ? 'modify' : 'add',time()); $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,$event['id'] ? 'modify' : 'add',time());
@ -749,13 +752,14 @@ class calendar_boupdate extends calendar_bo
* @param string/int $uid account_id or 1-char type-identifer plus id (eg. c15 for addressbook entry #15) * @param string/int $uid account_id or 1-char type-identifer plus id (eg. c15 for addressbook entry #15)
* @param int/char $status numeric status (defines) or 1-char code: 'R', 'U', 'T' or 'A' * @param int/char $status numeric status (defines) or 1-char code: 'R', 'U', 'T' or 'A'
* @param int $recur_date=0 date to change, or 0 = all since now * @param int $recur_date=0 date to change, or 0 = all since now
* @param boolean $ignore_acl=false do not check the permisions for the $uid, if true
* @return int number of changed recurrences * @return int number of changed recurrences
*/ */
function set_status($event,$uid,$status,$recur_date=0) function set_status($event,$uid,$status,$recur_date=0, $ignore_acl=false)
{ {
$cal_id = is_array($event) ? $event['id'] : $event; $cal_id = is_array($event) ? $event['id'] : $event;
//echo "<p>bocalupdate::set_status($cal_id,$uid,$status,$recur_date)</p>\n"; //echo "<p>bocalupdate::set_status($cal_id,$uid,$status,$recur_date)</p>\n";
if (!$cal_id || !$this->check_status_perms($uid,$event)) if (!$cal_id || (!$ignore_acl && !$this->check_status_perms($uid,$event)))
{ {
return false; return false;
} }
@ -1006,7 +1010,9 @@ class calendar_boupdate extends calendar_bo
} }
$alarm['time'] = $this->date2ts($alarm['time'],true); // user to server-time $alarm['time'] = $this->date2ts($alarm['time'],true); // user to server-time
return $this->so->save_alarm($cal_id,$alarm); $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id, 'modify', time());
return $this->so->save_alarm($cal_id,$alarm, $this->now_su);
} }
/** /**
@ -1023,7 +1029,10 @@ class calendar_boupdate extends calendar_bo
{ {
return false; // no rights to delete the alarm return false; // no rights to delete the alarm
} }
return $this->so->delete_alarm($id);
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id, 'modify', time());
return $this->so->delete_alarm($id, $this->now_su);
} }
var $categories; var $categories;
@ -1046,7 +1055,7 @@ class calendar_boupdate extends calendar_bo
{ {
$cat_name = substr($cat_name, 2); $cat_name = substr($cat_name, 2);
} }
$cat_id = $this->categories->add(array('name' => $cat_name,'descr' => $cat_name)); $cat_id = $this->categories->add(array('name' => $cat_name, 'descr' => $cat_name, 'access' => 'private'));
} }
if ($cat_id) if ($cat_id)
@ -1086,4 +1095,95 @@ class calendar_boupdate extends calendar_bo
return $cat_list; return $cat_list;
} }
/**
* Try to find a matching db entry
*
* @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
* @return the calendar_id of the matching entry or false (if none matches)
*/
function find_event($event, $relax=false)
{
if ($event['uid'] && ($uidmatch = $this->read($event['uid'])))
{
if ($event['reference']
&& ($egw_event = $this->read($uidmatch['id'], $event['reference'])))
{
// Do we work with a "status only" exception here?
$match = true;
foreach (array('start','end','title','description','priority',
'location','public','non_blocking') as $name)
{
if (isset($event[$name])
&& $event[$name] != $egw_event[$name])
{
$match = false;
break;
}
}
if ($match)
{
//return ($uidmatch['id'] . ':' . $event['reference']);
foreach ($event['participants'] as $attendee => $status)
{
if (!isset($egw_event['participants'][$attendee])
|| $egw_event['participants'][$attendee] != $status)
{
$match = false;
break;
}
else
{
unset($egw_event['participants'][$attendee]);
}
}
if ($match && !empty($egw_event['participants'])) $match = false;
}
if ($match) return ($uidmatch['id'] . ':' . $event['reference']);
return false; // We need to create a new "status only" exception
}
else
{
return $uidmatch['id'];
}
}
if ($event['id'] && ($found = $this->read($event['id'])))
{
// We only do a simple consistency check
if ($found['title'] == $event['title']
&& $found['start'] == $event['start']
&& $found['end'] == $event['end'])
{
return $found['id'];
}
}
unset($event['id']);
$query = array(
'cal_start='.$event['start'],
'cal_end='.$event['end'],
);
#foreach(array('title','location','priority','public','non_blocking','category') as $name) {
foreach (array('title','location','public','non_blocking','category') as $name)
{
if (!empty($event[$name])) $query['cal_'.$name] = $event[$name];
}
if($foundEvents = parent::search(array(
//'user' => $this->user,
'query' => $query,
)))
{
if(is_array($foundEvents))
{
$event = array_shift($foundEvents);
return $event['id'];
}
}
return false;
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
* *
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @author Lars Kneschke <lkneschke@egroupware.org> * @author Lars Kneschke <lkneschke@egroupware.org>
* @author Joerg Lehrke <jlehrke@noc.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
* @package calendar * @package calendar
* @subpackage export * @subpackage export
@ -21,6 +22,7 @@ class calendar_sif extends calendar_boupdate
'Start' => 'start', 'Start' => 'start',
'End' => 'end', 'End' => 'end',
'AllDayEvent' => 'alldayevent', 'AllDayEvent' => 'alldayevent',
'Attendees' => '',
'BillingInformation' => '', 'BillingInformation' => '',
'Body' => 'description', 'Body' => 'description',
'BusyStatus' => '', 'BusyStatus' => '',
@ -33,6 +35,11 @@ class calendar_sif extends calendar_boupdate
'Mileage' => '', 'Mileage' => '',
'ReminderMinutesBeforeStart' => 'reminderstart', 'ReminderMinutesBeforeStart' => 'reminderstart',
'ReminderSet' => 'reminderset', 'ReminderSet' => 'reminderset',
'ReminderSoundFile' => '',
'ReminderOptions' => '',
'ReminderInterval' => '',
'ReminderRepeatCount' => '',
'Exceptions' => '',
'ReplyTime' => '', 'ReplyTime' => '',
'Sensitivity' => 'public', 'Sensitivity' => 'public',
'Subject' => 'title', 'Subject' => 'title',
@ -51,6 +58,11 @@ class calendar_sif extends calendar_boupdate
// the calendar event array // the calendar event array
var $event; var $event;
// device specific settings
var $productName = 'mozilla plugin';
var $productSoftwareVersion = '0.3';
var $uidExtension = false;
// constants for recurence type // constants for recurence type
const olRecursDaily = 0; const olRecursDaily = 0;
const olRecursWeekly = 1; const olRecursWeekly = 1;
@ -68,6 +80,10 @@ class calendar_sif extends calendar_boupdate
const olFriday = 32; const olFriday = 32;
const olSaturday = 64; const olSaturday = 64;
// standard headers
const xml_decl = '<?xml version="1.0" encoding="UTF-8"?>';
const SIF_decl = '<SIFVersion>1.1</SIFVersion>';
function startElement($_parser, $_tag, $_attributes) { function startElement($_parser, $_tag, $_attributes) {
} }
@ -87,14 +103,13 @@ class calendar_sif extends calendar_boupdate
$vcal = new Horde_iCalendar; $vcal = new Horde_iCalendar;
$finalEvent = array(); $finalEvent = array();
$sysCharSet = $GLOBALS['egw']->translation->charset(); $sysCharSet = $GLOBALS['egw']->translation->charset();
$sifData = base64_decode($_sifdata);
#error_log($sifData); #error_log($sifData);
$tmpfname = tempnam('/tmp/sync/contents','sife_'); #$tmpfname = tempnam('/tmp/sync/contents','sife_');
$handle = fopen($tmpfname, "w"); #$handle = fopen($tmpfname, "w");
fwrite($handle, $sifData); #fwrite($handle, $sifData);
fclose($handle); #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);
@ -111,11 +126,13 @@ class calendar_sif extends calendar_boupdate
#error_log(print_r($this->event, true)); #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 = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet); $value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
#error_log("$key => $value"); #error_log("$key => $value");
switch($key) { switch($key) {
case 'alldayevent': case 'alldayevent':
if($value == 1) { if($value == 1) {
$finalEvent['whole_day'] = true;
$startParts = explode('-',$this->event['start']); $startParts = explode('-',$this->event['start']);
$finalEvent['start'] = mktime(0, 0, 0, $startParts[1], $startParts[2], $startParts[0]); $finalEvent['start'] = mktime(0, 0, 0, $startParts[1], $startParts[2], $startParts[0]);
$endParts = explode('-',$this->event['end']); $endParts = explode('-',$this->event['end']);
@ -191,6 +208,11 @@ class calendar_sif extends calendar_boupdate
// do nothing, get's handled in isrecuring clause // do nothing, get's handled in isrecuring clause
break; break;
case 'description':
if (preg_match('/\s*\[UID:(.+)?\]/Usm', $value, $matches)) {
$finalEvent['uid'] = $matches[1];
}
default: default:
$finalEvent[$key] = $value; $finalEvent[$key] = $value;
break; break;
@ -205,69 +227,32 @@ class calendar_sif extends calendar_boupdate
return $finalEvent; return $finalEvent;
} }
function search($_sifdata, $contentID=null) { function search($_sifdata, $contentID=null, $relax=false)
if(!$event = $this->siftoegw($_sifdata)) { {
return false; $result = false;
}
$query = array( if($event = $this->siftoegw($_sifdata))
'cal_start='.$this->date2ts($event['start'],true), // true = Server-time {
'cal_end='.$this->date2ts($event['end'],true), if ($contentID) {
); $event['id'] = $contentID;
if ($contentID) {
$query[] = 'egw_cal.cal_id='.(int)$contentID;
}
#foreach(array('title','location','priority','public','non_blocking') as $name) {
foreach(array('title','location','public','non_blocking') as $name) {
if (isset($event[$name])) $query['cal_'.$name] = $event[$name];
}
if($foundEvents = parent::search(array(
'user' => $this->user,
'query' => $query,
))) {
if(is_array($foundEvents)) {
$event = array_shift($foundEvents);
return $event['id'];
} }
$result = $this->find_event($event, $relax);
} }
return false; return $result;
$search['start'] = $event['start'];
$search['end'] = $event['end'];
unset($event['description']);
unset($event['start']);
unset($event['end']);
foreach($event as $key => $value) {
if (substr($key,0,6) != 'recur_' && substr($key,0,5) != 'alarm') {
$search['query']['cal_'.$key] = $value;
} else {
#$search['query'][$key] = $value;
}
}
if($foundEvents = parent::search($search)) {
if(is_array($foundEvents)) {
$event = array_shift($foundEvents);
return $event['id'];
}
}
return false;
} }
/** /**
* @return int contact id * @return int contact id
* @param string $_vcard the vcard * @param string $_vcard the vcard
* @param int $_abID the internal addressbook id * @param int $_abID the internal addressbook id
* @param boolean $merge=false merge data with existing entry
* @desc import a vard into addressbook * @desc import a vard into addressbook
*/ */
function addSIF($_sifdata, $_calID) function addSIF($_sifdata, $_calID, $merge=false)
{ {
$state = &$_SESSION['SyncML.state'];
$deviceInfo = $state->getClientDeviceInfo();
$calID = false; $calID = false;
#error_log('ABID: '.$_abID); #error_log('ABID: '.$_abID);
@ -282,10 +267,16 @@ class calendar_sif extends calendar_boupdate
unset($event['alarm']); unset($event['alarm']);
} }
if($_calID > 0) if($_calID > 0) {
{
// update entry // update entry
$event['id'] = $_calID; $event['id'] = $_calID;
} else {
if (isset($event['whole_day']) && $event['whole_day']
&& isset ($deviceInfo) && is_array($deviceInfo)
&& isset($deviceInfo['nonBlockingAllday'])
&& $deviceInfo['nonBlockingAllday']) {
$event['non_blocking'] = '1';
}
} }
if($eventID = $this->update($event, TRUE)) { if($eventID = $this->update($event, TRUE)) {
@ -308,14 +299,15 @@ class calendar_sif extends calendar_boupdate
} }
/** /**
* return a vcard * return a sife
* *
* @param int $_id the id of the contact * @param int $_id the id of the event
* @param int $_vcardProfile profile id for mapping from vcard values to egw addressbook
* @return string containing the vcard * @return string containing the vcard
*/ */
function getSIF($_id) function getSIF($_id)
{ {
$sysCharSet = $GLOBALS['egw']->translation->charset();
$fields = array_unique(array_values($this->sifMapping)); $fields = array_unique(array_values($this->sifMapping));
sort($fields); sort($fields);
@ -323,11 +315,17 @@ class calendar_sif extends calendar_boupdate
#error_log("FOUND EVENT: ". print_r($event, true)); #error_log("FOUND EVENT: ". print_r($event, true));
if($event = $this->read($_id,null,false,'server')) { if($event = $this->read($_id,null,false,'server')) {
$sysCharSet = $GLOBALS['egw']->translation->charset();
$vcal = new Horde_iCalendar; if ($this->uidExtension) {
if (!preg_match('/\[UID:.+\]/m', $event['description'])) {
$event['description'] .= "\n[UID:" . $event['uid'] . "]";
}
}
$vcal = &new Horde_iCalendar('1.0');
$sifEvent = '<appointment>'; $sifEvent = self::xml_decl . "\n<appointment>" . self::SIF_decl;
foreach($this->sifMapping as $sifField => $egwField) foreach($this->sifMapping as $sifField => $egwField)
{ {
@ -340,14 +338,6 @@ class calendar_sif extends calendar_boupdate
switch($sifField) switch($sifField)
{ {
case 'Categories':
if(!empty($value)) {
$value = implode('; ', $this->get_categories(explode(',',$value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifEvent .= "<$sifField>$value</$sifField>";
break;
case 'Importance': case 'Importance':
$value = $value-1; $value = $value-1;
$sifEvent .= "<$sifField>$value</$sifField>"; $sifEvent .= "<$sifField>$value</$sifField>";
@ -437,6 +427,48 @@ class calendar_sif extends calendar_boupdate
$sifEvent .= '<Occurrences>'. $occurrences .'</Occurrences>'; $sifEvent .= '<Occurrences>'. $occurrences .'</Occurrences>';
} }
break; break;
case MCAL_RECUR_MONTHLY_MDAY:
$eventInterval = ($event['recur_interval'] > 1 ? $event['recur_interval'] : 1);
$recurStartDate = mktime(0,0,0,date('m',$event['start']), date('d', $event['start']), date('Y', $event['start']));
$sifEvent .= "<$sifField>1</$sifField>";
$sifEvent .= '<RecurrenceType>'. self::olRecursMonthly .'</RecurrenceType>';
$sifEvent .= '<Interval>'. $eventInterval .'</Interval>';
$sifEvent .= '<PatternStartDate>'. $vcal->_exportDateTime($recurStartDate) .'</PatternStartDate>';
if($event['recur_enddate'] == 0) {
$sifEvent .= '<NoEndDate>1</NoEndDate>';
} else {
$recurEndDate = mktime(24, 0, 0, date('m',$event['recur_enddate']), date('d', $event['recur_enddate']), date('Y', $event['recur_enddate']));
$sifEvent .= '<NoEndDate>0</NoEndDate>';
$sifEvent .= '<PatternEndDate>'. $vcal->_exportDateTime($recurEndDate) .'</PatternEndDate>';
}
break;
case MCAL_RECUR_MONTHLY_WDAY:
$weekMaskMap = array('Sun' => self::olSunday, 'Mon' => self::olMonday, 'Tue' => self::olTuesday,
'Wed' => self::olWednesday, 'Thu' => self::olThursday, 'Fri' => self::olFriday,
'Sat' => self::olSaturday);
$eventInterval = ($event['recur_interval'] > 1 ? $event['recur_interval'] : 1);
$recurStartDate = mktime(0,0,0,date('m',$event['start']), date('d', $event['start']), date('Y', $event['start']));
$sifEvent .= "<$sifField>1</$sifField>";
$sifEvent .= '<RecurrenceType>'. self::olRecursMonthNth .'</RecurrenceType>';
$sifEvent .= '<Interval>'. $eventInterval .'</Interval>';
$sifEvent .= '<PatternStartDate>'. $vcal->_exportDateTime($recurStartDate) .'</PatternStartDate>';
$sifEvent .= '<Instance>' . (1 + (int) ((date('d',$event['start'])-1) / 7)) . '</Instance>';
if($event['recur_enddate'] == 0) {
$sifEvent .= '<NoEndDate>1</NoEndDate>';
$sifEvent .= '<DayOfWeekMask>' . $weekMaskMap[date('D',$event['start'])] . '</DayOfWeekMask>';
} else {
$recurEndDate = mktime(24, 0, 0, date('m',$event['recur_enddate']), date('d', $event['recur_enddate']), date('Y', $event['recur_enddate']));
$sifEvent .= '<NoEndDate>0</NoEndDate>';
$sifEvent .= '<PatternEndDate>'. $vcal->_exportDateTime($recurEndDate) .'</PatternEndDate>';
$sifEvent .= '<DayOfWeekMask>' . $weekMaskMap[date('D',$event['start'])] . '</DayOfWeekMask>';
}
break;
case MCAL_RECUR_YEARLY:
break;
} }
break; break;
@ -456,9 +488,10 @@ class calendar_sif extends calendar_boupdate
break; break;
case 'Start': case 'Start':
if($event['end'] - $event['start'] == 86399 && date('Y-m-d', $event['end']) == date('Y-m-d', $event['start'])) { if ($this->isWholeDay($event)) {
$value = date('Y-m-d', $event['start']); $value = date('Y-m-d', $event['start']);
$sifEvent .= "<Start>$value</Start>"; $sifEvent .= "<Start>$value</Start>";
$vaule = date('Y-m-d', $event['end']);
$sifEvent .= "<End>$value</End>"; $sifEvent .= "<End>$value</End>";
$sifEvent .= "<AllDayEvent>1</AllDayEvent>"; $sifEvent .= "<AllDayEvent>1</AllDayEvent>";
} else { } else {
@ -487,14 +520,23 @@ class calendar_sif extends calendar_boupdate
} }
break; break;
case 'Categories':
if(!empty($value)) {
$value = implode('; ', $this->get_categories(explode(',',$value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
} else {
break;
}
default: default:
$value = @htmlspecialchars($value, ENT_QUOTES, 'utf-8');
$sifEvent .= "<$sifField>$value</$sifField>"; $sifEvent .= "<$sifField>$value</$sifField>";
break; break;
} }
} }
$sifEvent .= '</appointment>'; $sifEvent .= '</appointment>';
return base64_encode($sifEvent); return $sifEvent;
} }
if($this->xmlrpc) if($this->xmlrpc)
@ -504,4 +546,31 @@ class calendar_sif extends calendar_boupdate
return False; return False;
} }
/**
* Set the supported fields
*
* Currently we only store name and version, manucfacturer is always Funambol
*
* @param string $_productName
* @param string $_productSoftwareVersion
*/
function setSupportedFields($_productName='', $_productSoftwareVersion='')
{
$state = &$_SESSION['SyncML.state'];
$deviceInfo = $state->getClientDeviceInfo();
if(isset($deviceInfo) && is_array($deviceInfo)) {
if(isset($deviceInfo['uidExtension']) &&
$deviceInfo['uidExtension']){
$this->uidExtension = true;
}
}
// store product name and version, to be able to use it elsewhere
if ($_productName) {
$this->productName = strtolower($_productName);
if (preg_match('/^[^\d]*(\d+\.?\d*)[\.|\d]*$/', $_productSoftwareVersion, $matches)) {
$this->productSoftwareVersion = $matches[1];
}
}
}
} }

View File

@ -5,7 +5,8 @@
* @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>
* @copyright (c) 2005-9 by RalfBecker-At-outdoor-training.de * @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) 2005-8 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$
*/ */
@ -105,12 +106,18 @@ class calendar_so
* *
* All times (start, end and modified) are returned as timesstamps in servertime! * All times (start, end and modified) are returned as timesstamps in servertime!
* *
* @param int|array/string $ids id or array of id's of the entries to read, or string with a single uid * @param int|array|string $ids id or array of id's of the entries to read, or string with a single uid
* @param int $recur_date=0 if set read the next recurrance at or after the timestamp, default 0 = read the initital one * @param int $recur_date=0 if set read the next recurrance at or after the timestamp, default 0 = read the initital one
* @return array|boolean array with id => data pairs or false if entry not found * @return array|boolean array with id => data pairs or false if entry not found
*/ */
function read($ids,$recur_date=0) function read($ids,$recur_date=0)
{ {
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {
$minimum_uid_length = 8;
}
//echo "<p>socal::read(".print_r($ids,true).",$recur_date)<br />\n".function_backtrace()."<p>\n"; //echo "<p>socal::read(".print_r($ids,true).",$recur_date)<br />\n".function_backtrace()."<p>\n";
$table_def = $this->db->get_table_definitions('calendar',$this->cal_table); $table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
@ -131,7 +138,9 @@ class calendar_so
} }
else else
{ {
// We only what the parents to match
$where['cal_uid'] = $ids; $where['cal_uid'] = $ids;
$where['cal_reference'] = 0;
} }
if ((int) $recur_date) if ((int) $recur_date)
{ {
@ -153,6 +162,15 @@ class calendar_so
} }
if (!$events) return false; if (!$events) return false;
foreach ($events as $event) {
if (!isset($event['uid']) || strlen($event['uid']) < $minimum_uid_length) {
// event (without uid), not strong enough uid => create new uid
$event['uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$event['id']);
$this->db->update($this->cal_table, array('cal_uid' => $event['uid']),
array('cal_id' => $event['id']),__LINE__,__FILE__,'calendar');
}
}
// check if we have a real recurance, if not set $recur_date=0 // check if we have a real recurance, if not set $recur_date=0
if (is_array($ids) || $events[(int)$ids]['recur_type'] == MCAL_RECUR_NONE) if (is_array($ids) || $events[(int)$ids]['recur_type'] == MCAL_RECUR_NONE)
{ {
@ -222,7 +240,7 @@ class calendar_so
$sql = ''; $sql = '';
if ($cat_id) if ($cat_id)
{ {
if (!is_array($cat_ids) && !@$GLOBALS['egw_info']['user']['preferences']['common']['cats_no_subs']) if (!is_array($cat_id) && !@$GLOBALS['egw_info']['user']['preferences']['common']['cats_no_subs'])
{ {
$cats = $GLOBALS['egw']->categories->return_all_children($cat_id); $cats = $GLOBALS['egw']->categories->return_all_children($cat_id);
} }
@ -260,12 +278,11 @@ class calendar_so
* @param string|array $_cols=null what to select, default "$this->repeats_table.*,$this->cal_table.*,cal_start,cal_end,cal_recur_date" * @param string|array $_cols=null what to select, default "$this->repeats_table.*,$this->cal_table.*,cal_start,cal_end,cal_recur_date"
* if specified an iterator for the rows is returned * if specified an iterator for the rows is returned
* @param string $append='' SQL to append to the query before $order, eg. for a GROUP BY clause * @param string $append='' SQL to append to the query before $order, eg. for a GROUP BY clause
* @return array|boolean|iterator of cal_id => event pairs, or false if error in the parameters, or iterator if !is_null($_cols) * @return array of cal_ids, or false if error in the parameters
* *
* ToDo: search custom-fields too * ToDo: search custom-fields too
*/ */
function &search($start,$end,$users,$cat_id=0,$filter='',$query='',$offset=False,$num_rows=0,$order = 'cal_start',$show_rejected=true, function &search($start,$end,$users,$cat_id=0,$filter='',$query='',$offset=False,$num_rows=0,$order='cal_start',$show_rejected=true,$_cols=null,$append='')
$_cols=null,$append='')
{ {
//echo '<p>'.__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($query).",$offset,$num_rows,$order,$show_rejected,".array2string($_cols).",$append)</p>\n"; //echo '<p>'.__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($query).",$offset,$num_rows,$order,$show_rejected,".array2string($_cols).",$append)</p>\n";
@ -344,6 +361,7 @@ class calendar_so
$selects[0]['cols'] = $selects[1]['cols'] = $select['cols']; // restore the original cols $selects[0]['cols'] = $selects[1]['cols'] = $select['cols']; // restore the original cols
} }
// error_log("calendar_so_search:\n" . print_r($selects, true));
$rs = $this->db->union($selects,__LINE__,__FILE__,$order,$offset,$num_rows); $rs = $this->db->union($selects,__LINE__,__FILE__,$order,$offset,$num_rows);
} }
else // MsSQL oder MySQL 3.23 else // MsSQL oder MySQL 3.23
@ -487,6 +505,12 @@ ORDER BY cal_user_type, cal_usre_id
*/ */
function save($event,&$set_recurrences,$change_since=0,&$etag=null) function save($event,&$set_recurrences,$change_since=0,&$etag=null)
{ {
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {
$minimum_uid_length = 8;
}
//echo "<p>socal::save(,$change_since) event="; _debug_array($event); //echo "<p>socal::save(,$change_since) event="; _debug_array($event);
//error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$set_recurrences,$change_since,$etag)"); //error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$set_recurrences,$change_since,$etag)");
@ -520,9 +544,17 @@ ORDER BY cal_user_type, cal_usre_id
return 0; // wrong etag, someone else updated the entry return 0; // wrong etag, someone else updated the entry
} }
if (!is_null($etag)) ++$etag; if (!is_null($etag)) ++$etag;
// events need to have at least one participant, default to the owner
if (!isset($event['cal_participants'])) {
$event['cal_participants'] = array($event['cal_owner'] => 'A');
}
if (!isset($event['cal_participants'][$event['cal_owner']])) {
$event['cal_participants'][$event['cal_owner']] = 'A';
}
} }
else else
{ {
// new event
if (!$event['cal_owner']) $event['cal_owner'] = $GLOBALS['egw_info']['user']['account_id']; if (!$event['cal_owner']) $event['cal_owner'] = $GLOBALS['egw_info']['user']['account_id'];
if (!$event['cal_id'] && !isset($event['cal_uid'])) $event['cal_uid'] = ''; // uid is NOT NULL! if (!$event['cal_id'] && !isset($event['cal_uid'])) $event['cal_uid'] = ''; // uid is NOT NULL!
@ -532,45 +564,54 @@ ORDER BY cal_user_type, cal_usre_id
{ {
return false; return false;
} }
// new event (without uid), not strong enough uid or new created referencing event => create new uid
if (strlen($event['cal_uid']) < 20 || is_numeric($event['cal_uid']) ||
$event['cal_reference'] && strpos($event['cal_uid'],'cal-'.$event['calreference'].'-') !== false)
{
$event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id);
$this->db->update($this->cal_table,array('cal_uid' => $event['cal_uid']),array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}
$etag = 0; $etag = 0;
// new events need to have at least one participant, default to the owner // new events need to have at least one participant, default to the owner
if (!isset($event['cal_participants'])) if (!isset($event['cal_participants'])) {
{
$event['cal_participants'] = array($event['cal_owner'] => 'A'); $event['cal_participants'] = array($event['cal_owner'] => 'A');
} }
if (!isset($event['cal_participants'][$event['cal_owner']])) {
$event['cal_participants'][$event['cal_owner']] = 'A';
}
}
if (!isset($event['cal_uid']) || strlen($event['cal_uid']) < $minimum_uid_length
|| ($event['cal_reference'] && strpos($event['cal_uid'],
'cal-'.$event['calreference'].'-') !== false)) {
// event (without uid), not strong enough uid
// or new created referencing event => create new uid
$event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id);
$this->db->update($this->cal_table, array('cal_uid' => $event['cal_uid']),
array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar');
} }
// write information about recuring event, if recur_type is present in the array // write information about recuring event, if recur_type is present in the array
if (isset($event['recur_type'])) if (isset($event['recur_type']))
{ {
// save the original start
$min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchSingle();
if (isset($event['recur_exception']) && is_array($event['recur_exception']) && count($event['recur_exception'])) if (isset($event['recur_exception']) && is_array($event['recur_exception']) && count($event['recur_exception']))
{ {
// delete execeptions from the user and dates table, it could be the first time // delete the execeptions from the user and dates table, it could be the first time
$this->db->delete($this->user_table,array('cal_id' => $cal_id,'cal_recur_date' => $event['recur_exception']),__LINE__,__FILE__,'calendar'); $this->db->delete($this->user_table,array('cal_id' => $cal_id,'cal_recur_date' => $event['recur_exception']),__LINE__,__FILE__,'calendar');
$this->db->delete($this->dates_table,array('cal_id' => $cal_id,'cal_start' => $event['recur_exception']),__LINE__,__FILE__,'calendar'); $this->db->delete($this->dates_table,array('cal_id' => $cal_id,'cal_start' => $event['recur_exception']),__LINE__,__FILE__,'calendar');
$event['recur_exception'] = implode(',',$event['recur_exception']);
} }
else else
{ {
$event['recur_exception'] = null; $event['recur_exception'] = array();
} }
if (!$set_recurrences) if (!$set_recurrences)
{ {
// check if the recure-information changed // check if the recurrence-information changed
$old_recur = $this->db->select($this->repeats_table,'*',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetch(); $old_recur = $this->db->select($this->repeats_table,'*',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetch();
$old_exceptions = $old_recur['recur_exception'] ? explode(',',$old_recur['recur_exception']) : array(); $old_exceptions = $old_recur['recur_exception'] ? explode(',',$old_recur['recur_exception']) : array();
$exceptions = $event['recur_exception'] ? explode(',',$event['recur_exception']) : array(); $set_recurrences = (isset($event['cal_start']) && (int)$min != (int) $event['cal_start']) ||
$set_recurrences = $event['recur_type'] != $old_recur['recur_type'] || $event['recur_data'] != $old_recur['recur_data'] || $event['recur_type'] != $old_recur['recur_type'] || $event['recur_data'] != $old_recur['recur_data'] ||
$event['recur_interval'] != $old_recur['recur_interval'] || $event['recur_enddate'] != $old_recur['recur_enddate'] || (int)$event['recur_interval'] != (int)$old_recur['recur_interval'] || $event['recur_enddate'] != $old_recur['recur_enddate'] ||
count(array_diff($old_exceptions,$exceptions)); // exception deleted or added count(array_diff($old_exceptions,$event['recur_exception'])); // exception deleted or added
$max = (int) $this->db->select($this->dates_table,'MAX(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchSingle();
} else {
$max = 0;
} }
$event['recur_exception'] = empty($event['recur_exception']) ? null : implode(',',$event['recur_exception']);
unset($event[0]); // unset the 'etag=etag+1', as it's not in the repeats table unset($event[0]); // unset the 'etag=etag+1', as it's not in the repeats table
if($event['recur_type'] != MCAL_RECUR_NONE) if($event['recur_type'] != MCAL_RECUR_NONE)
{ {
@ -582,17 +623,21 @@ ORDER BY cal_user_type, cal_usre_id
} }
if ($set_recurrences) if ($set_recurrences)
{ {
if ((int)$min != (int)$event['cal_start'])
{
// We need to reset all recurrences
$max = -1;
}
// delete all, but the lowest dates record // delete all, but the lowest dates record
$min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn();
$this->db->delete($this->dates_table,array( $this->db->delete($this->dates_table,array(
'cal_id' => $cal_id, 'cal_id' => $cal_id,
'cal_start > '.(int)$min, 'cal_start > '.(int)$min,
),__LINE__,__FILE__,'calendar'); ),__LINE__,__FILE__,'calendar');
// delete all user-records, with recur-date != 0
// delete all user-records, with recur-date > old enddate
$this->db->delete($this->user_table,array( $this->db->delete($this->user_table,array(
'cal_id' => $cal_id, 'cal_id' => $cal_id,
'cal_recur_date != 0', 'cal_recur_date > '.(int)$max,
),__LINE__,__FILE__,'calendar'); ),__LINE__,__FILE__,'calendar');
} }
} }
@ -647,10 +692,10 @@ ORDER BY cal_user_type, cal_usre_id
} }
//pgoerzen: don't add an alarm if it is before the current date. //pgoerzen: don't add an alarm if it is before the current date.
if ($event['recur_type'] && ($tmp_event = $this->read($eventID, time() + $alarm['offset']))) /*if ($event['recur_type'] && ($tmp_event = $this->read($eventID, time() + $alarm['offset'])))
{ {
$alarm['time'] = $tmp_event['cal_start'] - $alarm['offset']; $alarm['time'] = $tmp_event['cal_start'] - $alarm['offset'];
} } */
$this->save_alarm($cal_id,$alarm); $this->save_alarm($cal_id,$alarm);
} }
@ -708,7 +753,7 @@ ORDER BY cal_user_type, cal_usre_id
{ {
// move the recur-date of the participants // move the recur-date of the participants
$this->db->query("UPDATE $this->user_table SET cal_recur_date=cal_recur_date+$move_start WHERE $where AND cal_recur_date ". $this->db->query("UPDATE $this->user_table SET cal_recur_date=cal_recur_date+$move_start WHERE $where AND cal_recur_date ".
((int)$change_since ? '>= '.(int)$change_since : '!= 0'),__LINE__,__FILE__); ((int)$change_since ? '>= '.(int)$change_since : '!= 0') + " ORDER BY cal_recur_date DESC",__LINE__,__FILE__);
} }
if ($move_start || $move_end) if ($move_start || $move_end)
{ {
@ -762,7 +807,6 @@ ORDER BY cal_user_type, cal_usre_id
* @param int $cal_id * @param int $cal_id
* @param array $participants id => status pairs * @param array $participants id => status pairs
* @param int|boolean $change_since=0 false=new entry, > 0 time from which on the repetitions should be changed, default 0=all * @param int|boolean $change_since=0 false=new entry, > 0 time from which on the repetitions should be changed, default 0=all
* @param int $recur_date=0 time of which repetitions should be updated, default 0=all
* @return int|boolean number of updated recurrences or false on error * @return int|boolean number of updated recurrences or false on error
*/ */
function participants($cal_id,$participants,$change_since=0) function participants($cal_id,$participants,$change_since=0)
@ -786,6 +830,7 @@ ORDER BY cal_user_type, cal_usre_id
if ($change_since !== false) // existing entries only if ($change_since !== false) // existing entries only
{ {
// delete not longer set participants // delete not longer set participants
$where[0] = 'cal_recur_date=0';
$deleted = array(); $deleted = array();
foreach($this->db->select($this->user_table,'DISTINCT cal_user_type,cal_user_id,cal_quantity',$where, foreach($this->db->select($this->user_table,'DISTINCT cal_user_type,cal_user_id,cal_quantity',$where,
__LINE__,__FILE__,false,'','calendar') as $row) __LINE__,__FILE__,false,'','calendar') as $row)
@ -795,11 +840,15 @@ ORDER BY cal_user_type, cal_usre_id
{ {
$deleted[$row['cal_user_type']][] = $row['cal_user_id']; $deleted[$row['cal_user_type']][] = $row['cal_user_id'];
} }
elseif($row['cal_quantity'] == (substr($participants[$uid],1) ? substr($participants[$uid],1) : 1)) elseif((($row['cal_user_type'] == 'r') &&
// quantity of resource unchanged
($row['cal_quantity'] == (substr($participants[$uid],1) ? substr($participants[$uid],1) : 1))) ||
($row['cal_status'] == $participants[$uid]))
{ {
unset($participants[$uid]); // we dont touch them unset($participants[$uid]); // we don't touch them
} }
} }
unset($where[0]);
if (count($deleted)) if (count($deleted))
{ {
@ -815,21 +864,32 @@ ORDER BY cal_user_type, cal_usre_id
$this->db->delete($this->user_table,$where + array('('.implode(' OR ',$to_or).')'),__LINE__,__FILE__,'calendar'); $this->db->delete($this->user_table,$where + array('('.implode(' OR ',$to_or).')'),__LINE__,__FILE__,'calendar');
} }
} }
if (count($participants)) // these are NEW participants now if (count($participants)) // these are ALL participants now - we exclude the existing ones later
{ {
// find all recurrences, as they all need the new parts to be added // find all recurrences, as they all need the new parts to be added
$recurrences = array(); $recurrences = array();
$existing_participants = array();
if ($change_since !== false) // existing entries only if ($change_since !== false) // existing entries only
{ {
foreach($this->db->select($this->user_table,'DISTINCT cal_recur_date',$where,__LINE__,__FILE__,false,'','calendar') as $row) foreach($this->db->select($this->user_table,'DISTINCT cal_recur_date',$where,__LINE__,__FILE__,false,'','calendar') as $row)
{ {
$recurrences[] = $row['cal_recur_date']; $recurrences[] = $row['cal_recur_date'];
} }
// existing participants must not be updated
foreach($this->db->select($this->user_table,'DISTINCT cal_user_type,cal_user_id',$where,__LINE__,__FILE__,false,'','calendar') as $row)
{
$existing_participants[] = $this->combine_user($row['cal_user_type'],$row['cal_user_id']);
}
} }
if (!count($recurrences)) $recurrences[] = 0; // insert the default one if (!count($recurrences)) $recurrences[] = 0; // insert the default one
foreach($participants as $uid => $status) foreach($participants as $uid => $status)
{ {
if (in_array($uid,$existing_participants)) continue; // don't update existing participants
$id = null;
$this->split_user($uid,$type,$id); $this->split_user($uid,$type,$id);
foreach($recurrences as $recur_date) foreach($recurrences as $recur_date)
{ {
@ -854,7 +914,7 @@ ORDER BY cal_user_type, cal_usre_id
* @param int $cal_id * @param int $cal_id
* @param char $user_type 'u' regular user, 'r' resource, 'c' contact * @param char $user_type 'u' regular user, 'r' resource, 'c' contact
* @param int $user_id * @param int $user_id
* @param int|string $status numeric status (defines) or 1-char code: 'R', 'U', 'T' or 'A' * @param int|char $status numeric status (defines) or 1-char code: 'R', 'U', 'T' or 'A'
* @param int $recur_date=0 date to change, or 0 = all since now * @param int $recur_date=0 date to change, or 0 = all since now
* @return int number of changed recurrences * @return int number of changed recurrences
*/ */
@ -897,7 +957,7 @@ ORDER BY cal_user_type, cal_usre_id
),$where,__LINE__,__FILE__,'calendar'); ),$where,__LINE__,__FILE__,'calendar');
} }
$ret = $this->db->affected_rows(); $ret = $this->db->affected_rows();
error_log(__METHOD__."($cal_id,$user_type,$user_id,$status,$recur_date) = $ret"); //error_log(__METHOD__."($cal_id,$user_type,$user_id,$status,$recur_date) = $ret");
return $this->db->affected_rows(); return $this->db->affected_rows();
} }
@ -923,6 +983,8 @@ ORDER BY cal_user_type, cal_usre_id
{ {
if ($status == 'G') continue; // dont save group-invitations if ($status == 'G') continue; // dont save group-invitations
$type = '';
$id = null;
$this->split_user($uid,$type,$id); $this->split_user($uid,$type,$id);
$this->db->insert($this->user_table,array( $this->db->insert($this->user_table,array(
'cal_status' => $status !== true ? $status[0] : 'U', 'cal_status' => $status !== true ? $status[0] : 'U',
@ -1024,9 +1086,10 @@ ORDER BY cal_user_type, cal_usre_id
* *
* @param int $cal_id Id of the calendar-entry * @param int $cal_id Id of the calendar-entry
* @param array $alarm array with fields: text, owner, enabled, .. * @param array $alarm array with fields: text, owner, enabled, ..
* @param timestamp $now_su=0 timestamp for modification of related event
* @return string id of the alarm * @return string id of the alarm
*/ */
function save_alarm($cal_id,$alarm) function save_alarm($cal_id, $alarm, $now_su = 0)
{ {
//echo "<p>save_alarm(cal_id=$cal_id, alarm="; print_r($alarm); echo ")</p>\n"; //echo "<p>save_alarm(cal_id=$cal_id, alarm="; print_r($alarm); echo ")</p>\n";
if (!($id = $alarm['id'])) if (!($id = $alarm['id']))
@ -1050,6 +1113,13 @@ ORDER BY cal_user_type, cal_usre_id
{ {
return False; return False;
} }
// update the modification information of the related event
$datetime = $GLOBALS['egw']->datetime;
$now = ($now_su ? $now_su : time() + $datetime->this->tz_offset);
$modifier = $GLOBALS['egw_info']['user']['account_id'];
$this->db->update($this->cal_table, array('cal_modified' => $now, 'cal_modifier' => $modifier),
array('cal_id' => $cal_id), __LINE__, __FILE__, 'calendar');
return $id; return $id;
} }
@ -1074,10 +1144,20 @@ ORDER BY cal_user_type, cal_usre_id
* delete one alarms identified by its id * delete one alarms identified by its id
* *
* @param string $id alarm-id is a string of 'cal:'.$cal_id.':'.$alarm_nr, it is used as the job-id too * @param string $id alarm-id is a string of 'cal:'.$cal_id.':'.$alarm_nr, it is used as the job-id too
* @param timestamp $now_su=0 timestamp for modification of related event
* @return int number of alarms deleted * @return int number of alarms deleted
*/ */
function delete_alarm($id) function delete_alarm($id, $now_su = 0)
{ {
// update the modification information of the related event
list(,$cal_id) = explode(':',$id);
if ($cal_id) {
$datetime = $GLOBALS['egw']->datetime;
$now = ($now_su ? $now_su : time() + $datetime->this->tz_offset);
$modifier = $GLOBALS['egw_info']['user']['account_id'];
$this->db->update($this->cal_table, array('cal_modified' => $now, 'cal_modifier' => $modifier),
array('cal_id' => $cal_id), __LINE__, __FILE__, 'calendar');
}
return $this->async->cancel_timer($id); return $this->async->cancel_timer($id);
} }
@ -1087,7 +1167,7 @@ ORDER BY cal_user_type, cal_usre_id
* @param array|int $old_user integer old user or array with keys 'account_id' and 'new_owner' as the deleteaccount hook uses it * @param array|int $old_user integer old user or array with keys 'account_id' and 'new_owner' as the deleteaccount hook uses it
* @param int $new_user=null * @param int $new_user=null
*/ */
function deleteaccount($data) function deleteaccount($old_user, $newuser=null)
{ {
if (is_array($old_user)) if (is_array($old_user))
{ {
@ -1096,6 +1176,8 @@ ORDER BY cal_user_type, cal_usre_id
} }
if (!(int)$new_user) if (!(int)$new_user)
{ {
$user_type = '';
$user_id = null;
$this->split_user($old_user,$user_type,$user_id); $this->split_user($old_user,$user_type,$user_id);
if ($user_type == 'u') // only accounts can be owners of events if ($user_type == 'u') // only accounts can be owners of events
@ -1144,4 +1226,134 @@ ORDER BY cal_user_type, cal_usre_id
),__LINE__,__FILE__,'calendar'); ),__LINE__,__FILE__,'calendar');
} }
} }
/**
* get stati of all recurrences of an event for a specific participant
*
* @param int $cal_id
* @param int $uid participant uid
* @return array recur_date => status pairs (index 0 => main status)
*/
function get_recurrences($cal_id, $uid)
{
$user_type = $user_id = null;
$this->split_user($uid, $user_type, $user_id);
$participant_status = array();
$where = array('cal_id' => $cal_id);
foreach($this->db->select($this->user_table,'DISTINCT cal_recur_date',$where,__LINE__,__FILE__,false,'','calendar') as $row)
{
// inititalize the array
$participant_status[$row['cal_recur_date']] = null;
}
$where = array(
'cal_id' => $cal_id,
'cal_user_type' => $user_type ? $user_type : 'u',
'cal_user_id' => $user_id,
);
foreach ($this->db->select($this->user_table,'cal_recur_date,cal_status',$where,
__LINE__,__FILE__,false,'','calendar') as $row)
{
$participant_status[$row['cal_recur_date']] = $row['cal_status'];
}
return $participant_status;
}
/**
* get all participants of an event
*
* @param int $cal_id
* @param int $recur_date=0 gives participants of this recurrence, default 0=all
*
* @return array participants
*/
function get_participants($cal_id, $recur_date=0)
{
$participants = array();
$where = array('cal_id' => $cal_id);
if ($recur_date)
{
$where['cal_recur_date'] = $recur_date;
}
foreach ($this->db->select($this->user_table,'DISTINCT cal_user_type,cal_user_id', $where,
__LINE__,__FILE__,false,'','calendar') as $row)
{
$uid = $this->combine_user($row['cal_user_type'], $row['cal_user_id']);
$id = $row['cal_user_type'] . $row['cal_user_id'];
$participants[$id]['type'] = $row['cal_user_type'];
$participants[$id]['id'] = $row['cal_user_id'];
$participants[$id]['uid'] = $uid;
}
return $participants;
}
/**
* get all releated events
*
* @param int $uid UID of the series
*
* @return array of event exception ids for all events which share $uid
*/
function get_related($uid)
{
$where = array(
'cal_uid' => $uid,
);
$related = array();
foreach ($this->db->select($this->cal_table,'cal_id,cal_reference',$where,
__LINE__,__FILE__,false,'','calendar') as $row)
{
if ($row['cal_reference'])
{
// not the series entry itself
$related[$row['cal_id']] = $row['cal_reference'];
}
}
return $related;
}
/**
* Gets the exception days of a given recurring event caused by
* irregular participant stati
*
* @param array $event Recurring Event.
*
* @return array Array of exception days (false for non-recurring events).
*/
function get_recurrence_exceptions(&$event)
{
$cal_id = (int) $event['id'];
if (!$cal_id || $event['recur_type'] == MCAL_RECUR_NONE) return false;
$days = array();
$participants = $this->get_participants($event['id'], 0);
// Check if the stati for all participants are identical for all recurrences
foreach ($participants as $uid => $attendee)
{
switch ($attendee['type'])
{
case 'u': // account
case 'c': // contact
case 'e': // email address
$recurrences = $this->get_recurrences($event['id'], $uid);
foreach ($recurrences as $recur_date => $recur_status)
{
if ($recur_date && $recur_status != $recurrences[0])
{
// Every distinct status results in an exception
$days[] = $recur_date;
}
}
break;
default: // We don't handle the rest
break;
}
}
$days = array_unique($days);
sort($days);
return $days;
}
} }