mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-08 06:59:46 +01:00
Calendar synchronization backport
This commit is contained in:
parent
79a2c95678
commit
6433df94ec
@ -101,13 +101,14 @@ class calendar_bo
|
|||||||
'R' => 'Rejected',
|
'R' => 'Rejected',
|
||||||
'T' => 'Tentative',
|
'T' => 'Tentative',
|
||||||
'U' => 'No Response',
|
'U' => 'No Response',
|
||||||
|
'D' => 'Delegated',
|
||||||
'G' => 'Group invitation',
|
'G' => 'Group invitation',
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
* @var array recur_types translates MCAL recur-types to verbose labels
|
* @var array recur_types translates MCAL recur-types to verbose labels
|
||||||
*/
|
*/
|
||||||
var $recur_types = Array(
|
var $recur_types = Array(
|
||||||
MCAL_RECUR_NONE => 'None',
|
MCAL_RECUR_NONE => 'No recurrence',
|
||||||
MCAL_RECUR_DAILY => 'Daily',
|
MCAL_RECUR_DAILY => 'Daily',
|
||||||
MCAL_RECUR_WEEKLY => 'Weekly',
|
MCAL_RECUR_WEEKLY => 'Weekly',
|
||||||
MCAL_RECUR_MONTHLY_WDAY => 'Monthly (by day)',
|
MCAL_RECUR_MONTHLY_WDAY => 'Monthly (by day)',
|
||||||
@ -166,7 +167,7 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Instance of the categories class
|
* Instance of the categories class
|
||||||
*
|
*
|
||||||
* @var $categories
|
* @var categories
|
||||||
*/
|
*/
|
||||||
var $categories;
|
var $categories;
|
||||||
|
|
||||||
@ -290,9 +291,9 @@ class calendar_bo
|
|||||||
* filter string filter-name, atm. 'all' or 'hideprivate'
|
* filter string filter-name, atm. 'all' or 'hideprivate'
|
||||||
* query string pattern so search for, if unset or empty all matching entries are returned (no search)
|
* query string pattern so search for, if unset or empty all matching entries are returned (no search)
|
||||||
* Please Note: a search never returns repeating events more then once AND does not honor start+end date !!!
|
* Please Note: a search never returns repeating events more then once AND does not honor start+end date !!!
|
||||||
* dayswise boolean on True it returns an array with YYYYMMDD strings as keys and an array with events
|
* daywise boolean on True it returns an array with YYYYMMDD strings as keys and an array with events
|
||||||
* (events spanning multiple days are returned each day again (!)) otherwise it returns one array with
|
* (events spanning multiple days are returned each day again (!)) otherwise it returns one array with
|
||||||
* the events (default), not honored in a search ==> always returns an array of events !
|
* the events (default), not honored in a search ==> always returns an array of events!
|
||||||
* date_format string date-formats: 'ts'=timestamp (default), 'array'=array, or string with format for date
|
* date_format string date-formats: 'ts'=timestamp (default), 'array'=array, or string with format for date
|
||||||
* offset boolean/int false (default) to return all entries or integer offset to return only a limited result
|
* offset boolean/int false (default) to return all entries or integer offset to return only a limited result
|
||||||
* enum_recuring boolean if true or not set (default) or daywise is set, each recurence of a recuring events is returned,
|
* enum_recuring boolean if true or not set (default) or daywise is set, each recurence of a recuring events is returned,
|
||||||
@ -399,7 +400,7 @@ class calendar_bo
|
|||||||
}
|
}
|
||||||
// date2ts(,true) converts to server time, db2data converts again to user-time
|
// date2ts(,true) converts to server time, db2data converts again to user-time
|
||||||
$events =& $this->so->search(isset($start) ? $this->date2ts($start,true) : null,isset($end) ? $this->date2ts($end,true) : null,
|
$events =& $this->so->search(isset($start) ? $this->date2ts($start,true) : null,isset($end) ? $this->date2ts($end,true) : null,
|
||||||
$users,$cat_id,$filter,$params['query'],$offset,(int)$params['num_rows'],$params['order'],$show_rejected,$params['cols'],$params['append']);
|
$users,$cat_id,$filter,$params['query'],$offset,(int)$params['num_rows'],$params['order'],$show_rejected,$params['cols'],$params['append'],$params['cfs']);
|
||||||
|
|
||||||
if (isset($params['cols']))
|
if (isset($params['cols']))
|
||||||
{
|
{
|
||||||
@ -555,7 +556,7 @@ class calendar_bo
|
|||||||
$old_horizont = $this->config['horizont'];
|
$old_horizont = $this->config['horizont'];
|
||||||
$this->config['horizont'] = $new_horizont;
|
$this->config['horizont'] = $new_horizont;
|
||||||
|
|
||||||
// create further recurances for all recuring and not yet (at the old horizont) ended events
|
// create further recurrences for all recurring and not yet (at the old horizont) ended events
|
||||||
if (($recuring = $this->so->unfinished_recuring($old_horizont)))
|
if (($recuring = $this->so->unfinished_recuring($old_horizont)))
|
||||||
{
|
{
|
||||||
foreach($this->read(array_keys($recuring)) as $cal_id => $event)
|
foreach($this->read(array_keys($recuring)) as $cal_id => $event)
|
||||||
@ -576,10 +577,10 @@ class calendar_bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set all recurances for an event til the defined horizont $this->config['horizont']
|
* set all recurrences for an event until the defined horizont $this->config['horizont']
|
||||||
*
|
*
|
||||||
* @param array $event
|
* @param array $event
|
||||||
* @param mixed $start=0 minimum start-time for new recurances or !$start = since the start of the event
|
* @param mixed $start=0 minimum start-time for new recurrences or !$start = since the start of the event
|
||||||
*/
|
*/
|
||||||
function set_recurrences($event,$start=0)
|
function set_recurrences($event,$start=0)
|
||||||
{
|
{
|
||||||
@ -587,11 +588,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 participants 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']))
|
if (!isset($event['participants']) || !isset($event['start']) || !isset($event['end']))
|
||||||
{
|
{
|
||||||
list(,$event_read) = each($this->so->read($event['id']));
|
list(,$event_read) = each($this->so->read($event['id']));
|
||||||
$event['participants'] = $event_read['participants'];
|
if (!isset($event['participants']))
|
||||||
|
{
|
||||||
|
$event['participants'] = $event_read['participants'];
|
||||||
|
}
|
||||||
|
if (!isset($event['start']) || !isset($event['end']))
|
||||||
|
{
|
||||||
|
$event['start'] = $event_read['start'];
|
||||||
|
$event['end'] = $event_read['end'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!$start) $start = $event['start'];
|
if (!$start) $start = $event['start'];
|
||||||
|
|
||||||
@ -679,11 +688,11 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Reads a calendar-entry
|
* Reads a calendar-entry
|
||||||
*
|
*
|
||||||
* @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 mixed $date=null date to specify a single event of a series
|
* @param mixed $date=null date to specify a single event of a series
|
||||||
* @param boolean $ignore_acl should we ignore the acl, default False for a single id, true for multiple id's
|
* @param boolean $ignore_acl should we ignore the acl, default False for a single id, true for multiple id's
|
||||||
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in servertime, 'array'=array, or string with date-format
|
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in servertime, 'array'=array, or string with date-format
|
||||||
* @return boolean/array event or array of id => event pairs, false if the acl-check went wrong, null if $ids not found
|
* @return boolean|array event or array of id => event pairs, false if the acl-check went wrong, null if $ids not found
|
||||||
*/
|
*/
|
||||||
function read($ids,$date=null,$ignore_acl=False,$date_format='ts')
|
function read($ids,$date=null,$ignore_acl=False,$date_format='ts')
|
||||||
{
|
{
|
||||||
@ -747,9 +756,9 @@ class calendar_bo
|
|||||||
*/
|
*/
|
||||||
function insert_all_repetitions($event,$start,$end,&$events,$recur_exceptions)
|
function insert_all_repetitions($event,$start,$end,&$events,$recur_exceptions)
|
||||||
{
|
{
|
||||||
if ((int) $this->debug >= 3 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repitions')
|
if ((int) $this->debug >= 3 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repetions')
|
||||||
{
|
{
|
||||||
$this->debug_message('bocal::insert_all_repitions(%1,%2,%3,&$event,%4)',true,$event,$start,$end,$recur_exceptions);
|
$this->debug_message(__METHOD__.'(%1,%2,%3,&$event,%4)',true,$event,$start,$end,$recur_exceptions);
|
||||||
}
|
}
|
||||||
$start_in = $start; $end_in = $end;
|
$start_in = $start; $end_in = $end;
|
||||||
|
|
||||||
@ -758,9 +767,9 @@ class calendar_bo
|
|||||||
$event_start_ts = $this->date2ts($event['start']);
|
$event_start_ts = $this->date2ts($event['start']);
|
||||||
$event_end_ts = $this->date2ts($event['end']);
|
$event_end_ts = $this->date2ts($event['end']);
|
||||||
|
|
||||||
if ($this->debug && ((int) $this->debug > 3 || $this->debug == 'insert_all_repetions' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repitions'))
|
if ($this->debug && ((int) $this->debug > 3 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repetions'))
|
||||||
{
|
{
|
||||||
$this->debug_message('bocal::insert_all_repetions(%1,start=%2,end=%3,,%4) starting...',True,$event,$start_in,$end_in,$recur_exceptions);
|
$this->debug_message(__METHOD__.'(%1,start=%2,end=%3,,%4) starting...',True,$event,$start_in,$end_in,$recur_exceptions);
|
||||||
}
|
}
|
||||||
$id = $event['id'];
|
$id = $event['id'];
|
||||||
$event_start_arr = $this->date2array($event['start']);
|
$event_start_arr = $this->date2array($event['start']);
|
||||||
@ -800,9 +809,9 @@ class calendar_bo
|
|||||||
if (($have_exception = $search_date_ymd == (int)$this->date2string($exception_ts))) break;
|
if (($have_exception = $search_date_ymd == (int)$this->date2string($exception_ts))) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->debug && ((int) $this->debug > 3 || $this->debug == 'insert_all_repetions' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repitions'))
|
if ($this->debug && ((int) $this->debug > 3 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repetions'))
|
||||||
{
|
{
|
||||||
$this->debug_message('bocal::insert_all_repetions(...,%1) checking recur_exceptions[%2] and event[recur_exceptions]=%3 ==> %4',False,
|
$this->debug_message(__METHOD__.'(...,%1) checking recur_exceptions[%2] and event[recur_exceptions]=%3 ==> %4',False,
|
||||||
$recur_exceptions,$search_date_ymd,$event['recur_exception'],$have_exception);
|
$recur_exceptions,$search_date_ymd,$event['recur_exception'],$have_exception);
|
||||||
}
|
}
|
||||||
if ($have_exception)
|
if ($have_exception)
|
||||||
@ -914,18 +923,18 @@ class calendar_bo
|
|||||||
break;
|
break;
|
||||||
} // switch(recur-type)
|
} // switch(recur-type)
|
||||||
} // for($date = ...)
|
} // for($date = ...)
|
||||||
if ($this->debug && ((int) $this->debug > 2 || $this->debug == 'insert_all_repetions' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repitions'))
|
if ($this->debug && ((int) $this->debug > 2 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont' || $this->debug == 'insert_all_repetions'))
|
||||||
{
|
{
|
||||||
$this->debug_message('bocal::insert_all_repetions(%1,start=%2,end=%3,events,exections=%4) events=%5',True,$event,$start_in,$end_in,$recur_exceptions,$events);
|
$this->debug_message(__METHOD__.'(%1,start=%2,end=%3,events,exections=%4) events=%5',True,$event,$start_in,$end_in,$recur_exceptions,$events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds one repetion of $event for $date_ymd to the $events array, after adjusting its start- and end-time
|
* Adds one repetion of $event for $date_ymd to the $events array, after adjusting its start- and end-time
|
||||||
*
|
*
|
||||||
* @param $events array in which the event gets inserted
|
* @param array $events in which the event gets inserted
|
||||||
* @param $event array event to insert, it has start- and end-date of the first recurrence, not of $date_ymd
|
* @param array $event event to be inserted; it has start- and end-date of the first recurrence, not of $date_ymd
|
||||||
* @param $date_ymd int/string of the date of the event
|
* @param int|string $date_ymd of the date of the event
|
||||||
*/
|
*/
|
||||||
function add_adjusted_event(&$events,$event,$date_ymd)
|
function add_adjusted_event(&$events,$event,$date_ymd)
|
||||||
{
|
{
|
||||||
@ -1092,13 +1101,13 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Converts several date-types to a timestamp and optionaly converts user- to server-time
|
* Converts several date-types to a timestamp and optionaly converts user- to server-time
|
||||||
*
|
*
|
||||||
* @param $date mixed date to convert, should be one of the following types
|
* @param mixed $date date to convert, should be one of the following types
|
||||||
* string (!) in form YYYYMMDD or iso8601 YYYY-MM-DDThh:mm:ss or YYYYMMDDThhmmss
|
* string (!) in form YYYYMMDD or iso8601 YYYY-MM-DDThh:mm:ss or YYYYMMDDThhmmss
|
||||||
* int already a timestamp
|
* int already a timestamp
|
||||||
* array with keys 'second', 'minute', 'hour', 'day' or 'mday' (depricated !), 'month' and 'year'
|
* array with keys 'second', 'minute', 'hour', 'day' or 'mday' (depricated !), 'month' and 'year'
|
||||||
* @param $user2server_time boolean conversation between user- and server-time default False == Off
|
* @param boolean $user2server_time conversation between user- and server-time; default false == Off
|
||||||
*/
|
*/
|
||||||
function date2ts($date,$user2server=False)
|
function date2ts($date,$user2server=false)
|
||||||
{
|
{
|
||||||
$date_in = $date;
|
$date_in = $date;
|
||||||
|
|
||||||
@ -1173,11 +1182,11 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Converts a date to an array and optionaly converts server- to user-time
|
* Converts a date to an array and optionaly converts server- to user-time
|
||||||
*
|
*
|
||||||
* @param $date mixed date to convert
|
* @param mixed $date date to convert
|
||||||
* @param $server2user_time boolean conversation between user- and server-time default False == Off
|
* @param boolean $server2user_time=false conversation between user- and server-time; default false == Off
|
||||||
* @return array with keys 'second', 'minute', 'hour', 'day', 'month', 'year', 'raw' (timestamp) and 'full' (Ymd-string)
|
* @return array with keys 'second', 'minute', 'hour', 'day', 'month', 'year', 'raw' (timestamp) and 'full' (Ymd-string)
|
||||||
*/
|
*/
|
||||||
function date2array($date,$server2user=False)
|
function date2array($date,$server2user=false)
|
||||||
{
|
{
|
||||||
$date_called = $date;
|
$date_called = $date;
|
||||||
|
|
||||||
@ -1254,7 +1263,7 @@ class calendar_bo
|
|||||||
* Formats a date given as timestamp or array
|
* Formats a date given as timestamp or array
|
||||||
*
|
*
|
||||||
* @param mixed $date integer timestamp or array with ('year','month',..,'second') to convert
|
* @param mixed $date integer timestamp or array with ('year','month',..,'second') to convert
|
||||||
* @param string/boolean $format='' default common_prefs[dateformat], common_prefs[timeformat], false=time only, true=date only
|
* @param string|boolean $format='' default common_prefs[dateformat], common_prefs[timeformat], false=time only, true=date only
|
||||||
* @return string the formated date (incl. time)
|
* @return string the formated date (incl. time)
|
||||||
*/
|
*/
|
||||||
function format_date($date,$format='')
|
function format_date($date,$format='')
|
||||||
@ -1288,13 +1297,13 @@ class calendar_bo
|
|||||||
*
|
*
|
||||||
* The parameters get formated depending on their type. ACL-values need a ACL_TYPE_IDENTIFER prefix.
|
* The parameters get formated depending on their type. ACL-values need a ACL_TYPE_IDENTIFER prefix.
|
||||||
*
|
*
|
||||||
* @param $msg string message with parameters/variables like lang(), eg. '%1'
|
* @param string $msg message with parameters/variables like lang(), eg. '%1'
|
||||||
* @param $backtrace include a function-backtrace, default True=On
|
* @param boolean $backtrace=true include a function-backtrace, default true=On
|
||||||
* should only be set to False=Off, if your code ensures a call with backtrace=On was made before !!!
|
* should only be set to False=Off, if your code ensures a call with backtrace=On was made before !!!
|
||||||
* @param $param mixed a variable number of parameters, to be inserted in $msg
|
* @param mixed $param a variable number of parameters, to be inserted in $msg
|
||||||
* arrays get serialized with print_r() !
|
* arrays get serialized with print_r() !
|
||||||
*/
|
*/
|
||||||
function debug_message($msg,$backtrace=True)
|
function debug_message($msg,$backtrace=true)
|
||||||
{
|
{
|
||||||
static $acl2string = array(
|
static $acl2string = array(
|
||||||
0 => 'ACL-UNKNOWN',
|
0 => 'ACL-UNKNOWN',
|
||||||
@ -1473,7 +1482,7 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Converts a participant into a (readable) user- or resource-name
|
* Converts a participant into a (readable) user- or resource-name
|
||||||
*
|
*
|
||||||
* @param $id string|int id of user or resource
|
* @param string|int $id id of user or resource
|
||||||
* @return string with name
|
* @return string with name
|
||||||
*/
|
*/
|
||||||
function participant_name($id,$use_type=false)
|
function participant_name($id,$use_type=false)
|
||||||
@ -1534,6 +1543,9 @@ class calendar_bo
|
|||||||
case 'U': // no response = unknown
|
case 'U': // no response = unknown
|
||||||
$status = html::image('calendar','cnr-pending',$this->verbose_status[$status]);
|
$status = html::image('calendar','cnr-pending',$this->verbose_status[$status]);
|
||||||
break;
|
break;
|
||||||
|
case 'D': // delegated
|
||||||
|
$status = html::image('calendar','forward',$this->verbose_status[$status]);
|
||||||
|
break;
|
||||||
case 'G': // group invitation
|
case 'G': // group invitation
|
||||||
// Todo: Image, seems not to be used
|
// Todo: Image, seems not to be used
|
||||||
$status = '('.$this->verbose_status[$status].')';
|
$status = '('.$this->verbose_status[$status].')';
|
||||||
@ -1571,8 +1583,8 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Converts category string of an event into array of (readable) category-names
|
* Converts category string of an event into array of (readable) category-names
|
||||||
*
|
*
|
||||||
* @param $category string cat-id (multiple id's commaseparated)
|
* @param string $category cat-id (multiple id's commaseparated)
|
||||||
* @param $color int color of the category, if multiple cats, the color of the last one with color is returned
|
* @param int $color color of the category, if multiple cats, the color of the last one with color is returned
|
||||||
* @return array with id / names
|
* @return array with id / names
|
||||||
*/
|
*/
|
||||||
function categories($category,&$color)
|
function categories($category,&$color)
|
||||||
@ -1656,7 +1668,7 @@ class calendar_bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the recure-information of an event, into a human readable string
|
* Convert the recurrence-information of an event, into a human readable string
|
||||||
*
|
*
|
||||||
* @param array $event
|
* @param array $event
|
||||||
* @return string
|
* @return string
|
||||||
@ -1708,7 +1720,7 @@ class calendar_bo
|
|||||||
*
|
*
|
||||||
* The holidays get cached in the session (performance), so changes in holidays or birthdays do NOT affect a current session!!!
|
* The holidays get cached in the session (performance), so changes in holidays or birthdays do NOT affect a current session!!!
|
||||||
*
|
*
|
||||||
* @param integer $year=0 year, defaults to 0 = current year
|
* @param int $year=0 year, defaults to 0 = current year
|
||||||
* @return array indexed with Ymd of array of holidays. A holiday is an array with the following fields:
|
* @return array indexed with Ymd of array of holidays. A holiday is an array with the following fields:
|
||||||
* index: numerical unique id
|
* index: numerical unique id
|
||||||
* locale: string, 2-char short for the nation
|
* locale: string, 2-char short for the nation
|
||||||
@ -1775,8 +1787,8 @@ class calendar_bo
|
|||||||
*
|
*
|
||||||
* Is called as hook to participate in the linking
|
* Is called as hook to participate in the linking
|
||||||
*
|
*
|
||||||
* @param int/array $entry int cal_id or array with event
|
* @param int|array $entry int cal_id or array with event
|
||||||
* @param string/boolean string with title, null if not found or false if not read perms
|
* @param string|boolean string with title, null if not found or false if not read perms
|
||||||
*/
|
*/
|
||||||
function link_title($event)
|
function link_title($event)
|
||||||
{
|
{
|
||||||
@ -1797,7 +1809,7 @@ class calendar_bo
|
|||||||
* Is called as hook to participate in the linking
|
* Is called as hook to participate in the linking
|
||||||
*
|
*
|
||||||
* @param string $pattern pattern to search
|
* @param string $pattern pattern to search
|
||||||
* @return array with pm_id - title pairs of the matching entries
|
* @return array with cal_id - title pairs of the matching entries
|
||||||
*/
|
*/
|
||||||
function link_query($pattern)
|
function link_query($pattern)
|
||||||
{
|
{
|
||||||
@ -1883,7 +1895,7 @@ class calendar_bo
|
|||||||
/**
|
/**
|
||||||
* Get the freebusy URL of a user
|
* Get the freebusy URL of a user
|
||||||
*
|
*
|
||||||
* @param int/string $user account_id or account_lid
|
* @param int|string $user account_id or account_lid
|
||||||
* @param string $pw=null password
|
* @param string $pw=null password
|
||||||
*/
|
*/
|
||||||
static function freebusy_url($user,$pw=null)
|
static function freebusy_url($user,$pw=null)
|
||||||
@ -1900,13 +1912,14 @@ class calendar_bo
|
|||||||
* Check if the event is the whole day
|
* Check if the event is the whole day
|
||||||
*
|
*
|
||||||
* @param event
|
* @param event
|
||||||
|
* @param boolean $server2user_time=false conversation between user- and server-time; default false == Off
|
||||||
* @return boolean true for whole day events
|
* @return boolean true for whole day events
|
||||||
*/
|
*/
|
||||||
function isWholeDay($event)
|
function isWholeDay($event, $server2user=false)
|
||||||
{
|
{
|
||||||
// check if the event is the whole day
|
// check if the event is the whole day
|
||||||
$start = $this->date2array($event['start']);
|
$start = $this->date2array($event['start'], $server2user);
|
||||||
$end = $this->date2array($event['end']);
|
$end = $this->date2array($event['end'], $server2user);
|
||||||
$result = (!$start['hour'] && !$start['minute']
|
$result = (!$start['hour'] && !$start['minute']
|
||||||
&& $end['hour'] == 23 && $end['minute'] == 59);
|
&& $end['hour'] == 23 && $end['minute'] == 59);
|
||||||
return $result;
|
return $result;
|
||||||
|
@ -20,6 +20,7 @@ define('MSG_TENTATIVE',4);
|
|||||||
define('MSG_ACCEPTED',5);
|
define('MSG_ACCEPTED',5);
|
||||||
define('MSG_ALARM',6);
|
define('MSG_ALARM',6);
|
||||||
define('MSG_DISINVITE',7);
|
define('MSG_DISINVITE',7);
|
||||||
|
define('MSG_DELEGATED',8);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to access AND manipulate all calendar data (business object)
|
* Class to access AND manipulate all calendar data (business object)
|
||||||
@ -51,6 +52,13 @@ class calendar_boupdate extends calendar_bo
|
|||||||
*/
|
*/
|
||||||
var $debug;
|
var $debug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Logging
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
var $log = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|boolean $log_file filename to enable the login or false for no update-logging
|
* @var string|boolean $log_file filename to enable the login or false for no update-logging
|
||||||
*/
|
*/
|
||||||
@ -97,29 +105,35 @@ class calendar_boupdate extends calendar_bo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$event['id']) // some defaults for new entries
|
if (($new_event = !$event['id'])) // some defaults for new entries
|
||||||
{
|
{
|
||||||
// if no owner given, set user to owner
|
// if no owner given, set user to owner
|
||||||
if (!$event['owner']) $event['owner'] = $this->user;
|
if (!$event['owner']) $event['owner'] = $this->user;
|
||||||
// set owner as participant if none is given
|
// set owner as participant if none is given
|
||||||
if (!is_array($event['participants']) || !count($event['participants']))
|
if (!is_array($event['participants']) || !count($event['participants']))
|
||||||
{
|
{
|
||||||
$event['participants'] = array($event['owner'] => 'U');
|
$status = $event['owner'] == $this->user ? 'A' : 'U';
|
||||||
}
|
$status = calendar_so::combine_status($status, 1, 'CHAIR');
|
||||||
// set the status of the current user to 'A' = accepted
|
$event['participants'] = array($event['owner'] => $status);
|
||||||
if (isset($event['participants'][$this->user]) && $event['participants'][$this->user][0] != 'A')
|
|
||||||
{
|
|
||||||
$event['participants'][$this->user][0] = 'A';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user has the permission to update / create the event
|
// check if user has the permission to update / create the event
|
||||||
if (!$ignore_acl && ($event['id'] && !$this->check_perms(EGW_ACL_EDIT,$event['id']) ||
|
if (!$ignore_acl && (!$new_event && !$this->check_perms(EGW_ACL_EDIT,$event['id']) ||
|
||||||
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) &&
|
$new_event && !$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$new_event)
|
||||||
|
{
|
||||||
|
$old_event = $this->read((int)$event['id'],null,$ignore_acl);
|
||||||
|
// if no participants are set, set them from the old event, as we might need them to update recuring events
|
||||||
|
if (!isset($event['participants'])) $event['participants'] = $old_event['participants'];
|
||||||
|
//echo "old $event[id]="; _debug_array($old_event);
|
||||||
|
}
|
||||||
|
|
||||||
// check for conflicts only happens !$ignore_conflicts AND if start + end date are given
|
// check for conflicts only happens !$ignore_conflicts AND if start + end date are given
|
||||||
if (!$ignore_conflicts && !$event['non_blocking'] && isset($event['start']) && isset($event['end']))
|
if (!$ignore_conflicts && !$event['non_blocking'] && isset($event['start']) && isset($event['end']))
|
||||||
{
|
{
|
||||||
@ -250,14 +264,6 @@ class calendar_boupdate extends calendar_bo
|
|||||||
$event['modified'] = $this->now_su; // we are still in user-time
|
$event['modified'] = $this->now_su; // we are still in user-time
|
||||||
$event['modifier'] = $GLOBALS['egw_info']['user']['account_id'];
|
$event['modifier'] = $GLOBALS['egw_info']['user']['account_id'];
|
||||||
}
|
}
|
||||||
if (!($new_event = !(int)$event['id']))
|
|
||||||
{
|
|
||||||
$old_event = $this->read((int)$event['id'],null,$ignore_acl);
|
|
||||||
// if no participants are set, set them from the old event, as we might need them to update recuring events
|
|
||||||
if (!isset($event['participants'])) $event['participants'] = $old_event['participants'];
|
|
||||||
//echo "old $event[id]="; _debug_array($old_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
//echo "saving $event[id]="; _debug_array($event);
|
//echo "saving $event[id]="; _debug_array($event);
|
||||||
$event2save = $event;
|
$event2save = $event;
|
||||||
|
|
||||||
@ -359,7 +365,7 @@ class calendar_boupdate extends calendar_bo
|
|||||||
|
|
||||||
// the following switch falls through all cases, as each included the following too
|
// the following switch falls through all cases, as each included the following too
|
||||||
//
|
//
|
||||||
$msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE;
|
$msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE || $msg_type == MSG_DELEGATED;
|
||||||
|
|
||||||
switch($ru = $part_prefs['calendar']['receive_updates'])
|
switch($ru = $part_prefs['calendar']['receive_updates'])
|
||||||
{
|
{
|
||||||
@ -485,6 +491,12 @@ class calendar_boupdate extends calendar_bo
|
|||||||
$msgtype = '"calendar";';
|
$msgtype = '"calendar";';
|
||||||
$method = 'REPLY';
|
$method = 'REPLY';
|
||||||
break;
|
break;
|
||||||
|
case MSG_DELEGATED:
|
||||||
|
$action = lang('Delegated');
|
||||||
|
$msg = 'Response';
|
||||||
|
$msgtype = '"calendar";';
|
||||||
|
$method = 'REPLY';
|
||||||
|
break;
|
||||||
case MSG_ALARM:
|
case MSG_ALARM:
|
||||||
$action = lang('Alarm');
|
$action = lang('Alarm');
|
||||||
$msg = 'Alarm';
|
$msg = 'Alarm';
|
||||||
@ -738,7 +750,7 @@ class calendar_boupdate extends calendar_bo
|
|||||||
*/
|
*/
|
||||||
function check_status_perms($uid,$event)
|
function check_status_perms($uid,$event)
|
||||||
{
|
{
|
||||||
if ($uid[0] == 'c' || $uid['0'] == 'e') // for contact we use the owner of the event
|
if ($uid[0] == 'c' || $uid[0] == 'e') // for contact we use the owner of the event
|
||||||
{
|
{
|
||||||
if (!is_array($event) && !($event = $this->read($event))) return false;
|
if (!is_array($event) && !($event = $this->read($event))) return false;
|
||||||
|
|
||||||
@ -781,6 +793,7 @@ class calendar_boupdate extends calendar_bo
|
|||||||
'R' => MSG_REJECTED,
|
'R' => MSG_REJECTED,
|
||||||
'T' => MSG_TENTATIVE,
|
'T' => MSG_TENTATIVE,
|
||||||
'A' => MSG_ACCEPTED,
|
'A' => MSG_ACCEPTED,
|
||||||
|
'D' => MSG_DELEGATED,
|
||||||
);
|
);
|
||||||
if (isset($status2msg[$status]))
|
if (isset($status2msg[$status]))
|
||||||
{
|
{
|
||||||
@ -802,8 +815,6 @@ class calendar_boupdate extends calendar_bo
|
|||||||
*/
|
*/
|
||||||
function delete($cal_id,$recur_date=0,$ignore_acl=false)
|
function delete($cal_id,$recur_date=0,$ignore_acl=false)
|
||||||
{
|
{
|
||||||
$event = $this->read($cal_id,$recur_date);
|
|
||||||
|
|
||||||
if (!($event = $this->read($cal_id,$recur_date)) ||
|
if (!($event = $this->read($cal_id,$recur_date)) ||
|
||||||
!$ignore_acl && !$this->check_perms(EGW_ACL_DELETE,$event))
|
!$ignore_acl && !$this->check_perms(EGW_ACL_DELETE,$event))
|
||||||
{
|
{
|
||||||
@ -828,7 +839,7 @@ class calendar_boupdate extends calendar_bo
|
|||||||
}
|
}
|
||||||
if ($event['reference'])
|
if ($event['reference'])
|
||||||
{
|
{
|
||||||
// evtl. delete recur_exception $event['recurrence'] from event with cal_id=$event['reference']
|
// evtl. delete recur_exception $event['reference'] from event with cal_id=$event['reference']
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1138,110 +1149,597 @@ 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 -> find the matching entry
|
||||||
* @return the calendar_id of the matching entry or false (if none matches)
|
* check -> check (consitency) for identical matches
|
||||||
|
* relax -> be more tolerant
|
||||||
|
* master -> try to find a releated series master
|
||||||
|
* @return array calendar_ids 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']))
|
$recur_date = 0;
|
||||||
|
|
||||||
|
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];
|
if (isset($event['reference']))
|
||||||
}
|
|
||||||
|
|
||||||
if ($event['uid'] && ($uidmatch = $this->read($event['uid'])))
|
|
||||||
{
|
|
||||||
if ($event['reference'])
|
|
||||||
{
|
{
|
||||||
// Let's try to find a real exception first
|
$recur_date = $event['reference'];
|
||||||
$query['cal_uid'] = $event['uid'];
|
|
||||||
$query['cal_reference'] = $event['reference'];
|
|
||||||
|
|
||||||
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['reference'])))
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
{
|
|
||||||
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 pseudo exception
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
elseif (isset($event['start']))
|
||||||
{
|
{
|
||||||
return $uidmatch['id'];
|
$recur_date = $event['start'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event['id'] && ($found = $this->read($event['id'])))
|
if ($event['id'])
|
||||||
{
|
{
|
||||||
// We only do a simple consistency check
|
if ($this->log)
|
||||||
if ($found['title'] == $event['title']
|
{
|
||||||
&& $found['start'] == $event['start']
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
&& $found['end'] == $event['end'])
|
'(' . $event['id'] . ")[EventID]");
|
||||||
|
}
|
||||||
|
if (($egwEvent = $this->read($event['id'], $recur_date, false, 'server')))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
{
|
{
|
||||||
return $found['id'];
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'()[FOUND]:' . array2string($egwEvent));
|
||||||
}
|
}
|
||||||
|
// Just a simple consistency check
|
||||||
|
if ($filter == 'exact' ||
|
||||||
|
$filter == 'master' && $egwEvent['recur_type'] != MCAL_RECUR_NONE ||
|
||||||
|
$filter != 'master' && strpos($egwEvent['title'], $event['title']) === 0)
|
||||||
|
{
|
||||||
|
$retval = $egwEvent['id'];
|
||||||
|
if ($egwEvent['recur_type'] != MCAL_RECUR_NONE &&
|
||||||
|
$event['recur_type'] == MCAL_RECUR_NONE && $event['reference'] != 0)
|
||||||
|
{
|
||||||
|
$retval .= ':' . (int)$event['reference'];
|
||||||
|
}
|
||||||
|
$matchingEvents[] = $retval;
|
||||||
|
return $matchingEvents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($filter == 'exact') return array();
|
||||||
}
|
}
|
||||||
unset($event['id']);
|
unset($event['id']);
|
||||||
|
|
||||||
if($foundEvents = parent::search(array(
|
if ($filter == 'master')
|
||||||
'query' => $query,
|
|
||||||
)))
|
|
||||||
{
|
{
|
||||||
if(is_array($foundEvents))
|
$query[] = 'recur_type!='. MCAL_RECUR_NONE;
|
||||||
|
$query['cal_reference'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
{
|
{
|
||||||
$event = array_shift($foundEvents);
|
if ($user && !in_array($user,$users)) // already added?
|
||||||
return $event['id'];
|
{
|
||||||
|
$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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
if ($filter != 'master' && ($filter != 'exact' || empty($event['uid'])))
|
||||||
|
{
|
||||||
|
if (isset($event['whole_day']) && $event['whole_day'])
|
||||||
|
{
|
||||||
|
if ($filter == 'relax')
|
||||||
|
{
|
||||||
|
$delta = 1800;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
$query[] = ('cal_start>' . ($event['start'] - 86400));
|
||||||
|
$query[] = ('cal_start<' . ($event['start'] + 86400));
|
||||||
|
}
|
||||||
|
elseif (isset($event['start']))
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$matchFields = array('priority', 'public', 'non_blocking', 'reference');
|
||||||
|
foreach ($matchFields as $key)
|
||||||
|
{
|
||||||
|
if (isset($event[$key])) $query['cal_'.$key] = $event[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($event['uid']))
|
||||||
|
{
|
||||||
|
$query['cal_uid'] = $event['uid'];
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'(' . $event['uid'] . ')[EventUID]');
|
||||||
|
}
|
||||||
|
if ($filter != 'master' && isset($event['reference']))
|
||||||
|
{
|
||||||
|
$query['cal_reference'] = $event['reference'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[QUERY]: ' . array2string($query));
|
||||||
|
}
|
||||||
|
if (!count($users) || !($foundEvents =
|
||||||
|
$this->so->search(null, null, $users, 0, 'all', $query)))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[NO MATCH]');
|
||||||
|
}
|
||||||
|
return $matchingEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pseudos = array();
|
||||||
|
|
||||||
|
foreach($foundEvents as $egwEvent)
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[FOUND]: ' . array2string($egwEvent));
|
||||||
|
}
|
||||||
|
if (in_array($egwEvent['id'], $matchingEvents)) continue;
|
||||||
|
|
||||||
|
if (in_array($filter, array('exact', 'master')) && !empty($event['uid']))
|
||||||
|
{
|
||||||
|
$matchingEvents[] = $egwEvent['id']; // UID found
|
||||||
|
if ($filter = 'master') break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check times
|
||||||
|
if ($filter != 'relax')
|
||||||
|
{
|
||||||
|
if (isset($event['whole_day'])&& $event['whole_day'])
|
||||||
|
{
|
||||||
|
if (!$this->isWholeDay($egwEvent, true))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for real match
|
||||||
|
$matchFields = array('title');
|
||||||
|
switch ($filter)
|
||||||
|
{
|
||||||
|
case 'master':
|
||||||
|
break;
|
||||||
|
case 'relax':
|
||||||
|
$matchFields[] = 'location';
|
||||||
|
default:
|
||||||
|
$matchFields[] = 'description';
|
||||||
|
}
|
||||||
|
foreach ($matchFields as $key)
|
||||||
|
{
|
||||||
|
if (!empty($event[$key]) && (empty($egwEvent[$key])
|
||||||
|
|| strpos($egwEvent[$key], $event[$key]) !== 0))
|
||||||
|
{
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"() event[$key] differ: '" . $event[$key] .
|
||||||
|
"' <> '" . $egwEvent[$key]) . "'";
|
||||||
|
}
|
||||||
|
continue 2; // next foundEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filter != 'master' && is_array($event['category']))
|
||||||
|
{
|
||||||
|
// check categories
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
$start = $this->date2ts($event['start'], true);
|
||||||
|
$pseudos[] = $egwEvent['id'] . ':' . $start;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($filter != 'relax')
|
||||||
|
{
|
||||||
|
// check exceptions
|
||||||
|
// $exceptions[$remote_ts] = $egw_ts
|
||||||
|
$exceptions = $this->so->get_recurrence_exceptions($egwEvent);
|
||||||
|
$exceptions = array_merge($egwEvent['recur_exception'], $exceptions);
|
||||||
|
if (is_array($event['recur_exception']))
|
||||||
|
{
|
||||||
|
foreach ($event['recur_exception'] 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_exception']));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check recurrence information
|
||||||
|
foreach (array('recur_type', 'recur_interval', 'recur_enddate') 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
|
||||||
|
if ($filter = 'master') break;
|
||||||
|
}
|
||||||
|
// append pseudos as last entries
|
||||||
|
$matchingEvents = array_merge($matchingEvents, $pseudos);
|
||||||
|
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
'[MATCHES]:' . array2string($matchingEvents));
|
||||||
|
}
|
||||||
|
return $matchingEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* classifies an incoming event from the eGW point-of-view
|
||||||
|
*
|
||||||
|
* exceptions: unlike other calendar apps eGW does not create an event exception
|
||||||
|
* if just the participant state changes - therefore we have to distinguish between
|
||||||
|
* real exceptions and status only exceptions
|
||||||
|
*
|
||||||
|
* @param array $event the event to check
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* type =>
|
||||||
|
* SINGLE a single event
|
||||||
|
* SERIES-MASTER the series master
|
||||||
|
* SERIES-EXCEPTION event is a real 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
|
||||||
|
* stored_event => if event already exists in the database array with event data or false
|
||||||
|
* master_event => for event type SERIES-EXCEPTION, SERIES-PSEUDO-EXCEPTION or SERIES-EXCEPTION-PROPAGATE
|
||||||
|
* the corresponding series master event array
|
||||||
|
* NOTE: this param is false if event is of type SERIES-MASTER
|
||||||
|
*/
|
||||||
|
function get_event_info($event)
|
||||||
|
{
|
||||||
|
$type = 'SINGLE'; // default
|
||||||
|
$master_event = false; //default
|
||||||
|
$stored_event = false;
|
||||||
|
$recurrence_event = false;
|
||||||
|
$wasPseudo = false;
|
||||||
|
|
||||||
|
if (($foundEvents = $this->find_event($event, 'exact')))
|
||||||
|
{
|
||||||
|
// We found the exact match
|
||||||
|
$eventID = array_shift($foundEvents);
|
||||||
|
if (strstr($eventID, ':'))
|
||||||
|
{
|
||||||
|
$type = 'SERIES-PSEUDO-EXCEPTION';
|
||||||
|
$wasPseudo = true;
|
||||||
|
list($eventID, $recur_date) = explode(':', $eventID);
|
||||||
|
$recur_date = $this->date2usertime($recur_date);
|
||||||
|
$stored_event = $this->read($eventID, $recur_date, false, 'server');
|
||||||
|
$master_event = $this->read($eventID, 0, false, 'server');
|
||||||
|
$recurrence_event = $stored_event;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$stored_event = $this->read($eventID, 0);
|
||||||
|
}
|
||||||
|
if (!empty($stored_event['uid']) && empty($event['uid']))
|
||||||
|
{
|
||||||
|
$event['uid'] = $stored_event['uid']; // restore the UID if it was not delivered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event['recur_type'] != MCAL_RECUR_NONE)
|
||||||
|
{
|
||||||
|
$type = 'SERIES-MASTER';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type == 'SINGLE' &&
|
||||||
|
($foundEvents = $this->find_event($event, 'master')))
|
||||||
|
{
|
||||||
|
// SINGLE, SERIES-EXCEPTION OR SERIES-EXCEPTON-STATUS
|
||||||
|
foreach ($foundEvents as $eventID)
|
||||||
|
{
|
||||||
|
// Let's try to find a related series
|
||||||
|
if ($this->log)
|
||||||
|
{
|
||||||
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
"()[MASTER]: $eventID");
|
||||||
|
}
|
||||||
|
if (($master_event = $this->read($eventID, 0)))
|
||||||
|
{
|
||||||
|
if (isset($stored_event['id']) && $master_event['id'] != $stored_event['id'])
|
||||||
|
{
|
||||||
|
$type = 'SERIES-EXCEPTION'; // this is an existing exception
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elseif (isset($event['reference']) &&
|
||||||
|
in_array($event['reference'], $master_event['recur_exception']))
|
||||||
|
{
|
||||||
|
$type = 'SERIES-PSEUDO-EXCEPTION'; // could also be a real one
|
||||||
|
$recurrence_event = $master_event;
|
||||||
|
$recurrence_event['start'] = $event['reference'];
|
||||||
|
$recurrence_event['end'] -= $master_event['start'] - $event['reference'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elseif (in_array($event['start'], $master_event['recur_exception']))
|
||||||
|
{
|
||||||
|
$type='SERIES-PSEUDO-EXCEPTION'; // new pseudo exception?
|
||||||
|
$recurrence_event = $master_event;
|
||||||
|
$recurrence_event['start'] = $event['start'];
|
||||||
|
$recurrence_event['end'] -= $master_event['start'] - $event['start'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// try to find a suitable pseudo exception date
|
||||||
|
$recur_date = $this->date2usertime($event['start']);
|
||||||
|
$egwEvent = $this->read($eventID, $recur_date, false, 'server');
|
||||||
|
if ($event['start'] == $egwEvent['start'])
|
||||||
|
{
|
||||||
|
$type = 'SERIES-PSEUDO-EXCEPTION'; // let's try a pseudo exception
|
||||||
|
$recurrence_event = $master_event;
|
||||||
|
$recurrence_event['start'] = $event['start'];
|
||||||
|
$recurrence_event['end'] -= $master_event['start'] - $event['start'];
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
$recur_date = $this->date2usertime($event['reference']);
|
||||||
|
$egwEvent = $this->read($eventID,$recur_date , false, 'server');
|
||||||
|
if (isset($event['reference']) && $event['reference'] == $egwEvent['start'])
|
||||||
|
{
|
||||||
|
$type = 'SERIES-EXCEPTION-PROPAGATE';
|
||||||
|
if ($stored_event)
|
||||||
|
{
|
||||||
|
unset($stored_event['id']); // signal the true exception
|
||||||
|
$stored_event['recur_type'] = MCAL_RECUR_NONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// check for changed data
|
||||||
|
foreach (array('start','end','uid','title','location','description',
|
||||||
|
'priority','public','special','non_blocking') as $key)
|
||||||
|
{
|
||||||
|
if (!empty($event[$key]) && $recurrence_event[$key] != $event[$key])
|
||||||
|
{
|
||||||
|
if ($wasPseudo)
|
||||||
|
{
|
||||||
|
// We started with a pseudo exception
|
||||||
|
$type = 'SERIES-EXCEPTION-PROPAGATE';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$type = 'SERIES-EXCEPTION';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($stored_event)
|
||||||
|
{
|
||||||
|
unset($stored_event['id']); // signal the true exception
|
||||||
|
$stored_event['recur_type'] = MCAL_RECUR_NONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the event id here is always the id of the master event
|
||||||
|
// unset it to prevent confusion of stored event and master event
|
||||||
|
unset($event['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check ACL
|
||||||
|
if (is_array($master_event))
|
||||||
|
{
|
||||||
|
$acl_edit = $this->check_perms(EGW_ACL_EDIT, $master_event['id']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (is_array($stored_event))
|
||||||
|
{
|
||||||
|
$acl_edit = $this->check_perms(EGW_ACL_EDIT, $stored_event['id']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$acl_edit = true; // new event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'type' => $type,
|
||||||
|
'acl_edit' => $acl_edit,
|
||||||
|
'stored_event' => $stored_event,
|
||||||
|
'master_event' => $master_event,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@ -33,9 +33,25 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
//'RDATE' => 'cal_start',
|
//'RDATE' => 'cal_start',
|
||||||
//'EXRULE'
|
//'EXRULE'
|
||||||
//'EXDATE'
|
//'EXDATE'
|
||||||
'RECURRENCE-ID' => 'cal_reference',
|
//'RECURRENCE-ID' => 'cal_reference',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does client understand exceptions to be included in VCALENDAR component of series master sharing its UID
|
||||||
|
*
|
||||||
|
* That also means no EXDATE for these exceptions!
|
||||||
|
*
|
||||||
|
* Setting it to false, should give the old behavior used in 1.6 (hopefully) no client needs that.
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
var $client_shared_uid_exceptions = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we using id or uid for the path/url
|
||||||
|
*/
|
||||||
|
const PATH_ATTRIBUTE = 'id';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -50,8 +66,6 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
$this->bo = new calendar_boupdate();
|
$this->bo = new calendar_boupdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
const PATH_ATTRIBUTE = 'id';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the path for an event
|
* Create the path for an event
|
||||||
*
|
*
|
||||||
@ -84,11 +98,13 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
*/
|
*/
|
||||||
function propfind($path,$options,&$files,$user,$id='')
|
function propfind($path,$options,&$files,$user,$id='')
|
||||||
{
|
{
|
||||||
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id)");
|
if ($this->debug)
|
||||||
//error_log(__METHOD__."($path,".array2string($options).",,$user,$id)");//njv:
|
{
|
||||||
|
error_log(__METHOD__."($path,".array2string($options).",,$user,$id)");
|
||||||
|
$starttime = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
// ToDo: add parameter to only return id & etag
|
// ToDo: add parameter to only return id & etag
|
||||||
//error_log( __FILE__ . __METHOD__ ." :$user ". print_r($options,true));
|
|
||||||
$st = microtime(true);
|
|
||||||
$cal_filters = array(
|
$cal_filters = array(
|
||||||
'users' => $user,
|
'users' => $user,
|
||||||
'start' => time()-100*24*3600, // default one month back -30 breaks all sync recurrences
|
'start' => time()-100*24*3600, // default one month back -30 breaks all sync recurrences
|
||||||
@ -97,13 +113,23 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
'daywise' => false,
|
'daywise' => false,
|
||||||
'date_format' => 'server',
|
'date_format' => 'server',
|
||||||
);
|
);
|
||||||
if ($this->debug > 1) error_log(__METHOD__."($path,,,$user,$id) cal_filters=".array2string($cal_filters));
|
/*
|
||||||
//error_log(__METHOD__."($path,,,$user,$id) cal_filters=".array2string($cal_filters));//njv
|
if ($this->client_shared_uid_exceptions)
|
||||||
|
{
|
||||||
|
$cal_filters['query']['cal_reference'] = 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
// process REPORT filters or multiget href's
|
// process REPORT filters or multiget href's
|
||||||
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$cal_filters,$id))
|
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$cal_filters,$id))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ($this->debug > 1)
|
||||||
|
{
|
||||||
|
error_log(__METHOD__."($path,,,$user,$id) cal_filters=".
|
||||||
|
array2string($cal_filters));
|
||||||
|
}
|
||||||
|
|
||||||
// check if we have to return the full calendar data or just the etag's
|
// check if we have to return the full calendar data or just the etag's
|
||||||
if (!($calendar_data = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CALDAV) && is_array($options['props']))
|
if (!($calendar_data = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CALDAV) && is_array($options['props']))
|
||||||
{
|
{
|
||||||
@ -116,11 +142,28 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//error_log(__FILE__ . __METHOD__ ."Filters:" .print_r($cal_filters,true));
|
$events =& $this->bo->search($cal_filters);
|
||||||
if (($events = $this->bo->search($cal_filters)))
|
if ($events)
|
||||||
{
|
{
|
||||||
foreach($events as $event)
|
// get all max user modified times at once
|
||||||
|
foreach($events as $k => &$event)
|
||||||
{
|
{
|
||||||
|
if ($this->client_shared_uid_exceptions &&
|
||||||
|
$event['reference'] &&
|
||||||
|
($master = $this->bo->read($event['reference'], 0, false, 'server')) &&
|
||||||
|
array_search($event['reference'], $master['recur_exception']) !== false)
|
||||||
|
{
|
||||||
|
// this exception will be handled with the series master
|
||||||
|
unset($events[$k]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ids[] = $event['id'];
|
||||||
|
}
|
||||||
|
$max_user_modified = $this->bo->so->max_user_modified($ids);
|
||||||
|
|
||||||
|
foreach($events as &$event)
|
||||||
|
{
|
||||||
|
$event['max_user_modified'] = $max_user_modified[$event['id']];
|
||||||
//header('X-EGROUPWARE-EVENT-'.$event['id'].': '.$event['title'].': '.date('Y-m-d H:i:s',$event['start']).' - '.date('Y-m-d H:i:s',$event['end']));
|
//header('X-EGROUPWARE-EVENT-'.$event['id'].': '.$event['title'].': '.date('Y-m-d H:i:s',$event['start']).' - '.date('Y-m-d H:i:s',$event['end']));
|
||||||
$props = array(
|
$props = array(
|
||||||
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($event)),
|
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($event)),
|
||||||
@ -133,8 +176,7 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
//error_log(__FILE__ . __METHOD__ . "Calendar Data : $calendar_data");
|
//error_log(__FILE__ . __METHOD__ . "Calendar Data : $calendar_data");
|
||||||
if ($calendar_data)
|
if ($calendar_data)
|
||||||
{
|
{
|
||||||
if (is_null($handler)) $handler = $this->_get_handler();
|
$content = $this->iCal($event);
|
||||||
$content = $handler->exportVCal(array($event),'2.0','PUBLISH');
|
|
||||||
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
|
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
|
||||||
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content);
|
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content);
|
||||||
}
|
}
|
||||||
@ -148,8 +190,11 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$end = microtime(true) - $st;
|
if ($this->debug)
|
||||||
if ($this->debug) error_log(__FILE__ . __METHOD__ . "Function took : $end");
|
{
|
||||||
|
error_log(__METHOD__."($path) took ".(microtime(true) - $starttime).
|
||||||
|
' to return '.count($files['files']).' items');
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,29 +220,29 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
switch($filter['name'])
|
switch($filter['name'])
|
||||||
{
|
{
|
||||||
case 'comp-filter':
|
case 'comp-filter':
|
||||||
if ($this->debug > 1) error_log(__METHOD__."($path,...) comp-filter='{$filter['attrs']['name']}'");
|
if ($this->debug > 1) error_log(__METHOD__."($options[path],...) comp-filter='{$filter['attrs']['name']}'");
|
||||||
|
|
||||||
switch($filter['attrs']['name'])
|
switch($filter['attrs']['name'])
|
||||||
{
|
{
|
||||||
case 'VTODO':
|
case 'VTODO':
|
||||||
return false; // return nothing for now, todo: check if we can pass it on to the infolog handler
|
return false; // return nothing for now, todo: check if we can pass it on to the infolog handler
|
||||||
// todos are handled by the infolog handler
|
// todos are handled by the infolog handler
|
||||||
$infolog_handler = new groupdav_infolog();
|
//$infolog_handler = new groupdav_infolog();
|
||||||
return $infolog_handler->propfind($path,$options,$files,$user,$method);
|
//return $infolog_handler->propfind($path,$options,$files,$user,$method);
|
||||||
case 'VCALENDAR':
|
case 'VCALENDAR':
|
||||||
case 'VEVENT':
|
case 'VEVENT':
|
||||||
break; // that's our default anyway
|
break; // that's our default anyway
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'prop-filter':
|
case 'prop-filter':
|
||||||
if ($this->debug > 1) error_log(__METHOD__."($path,...) prop-filter='{$filter['attrs']['name']}'");
|
if ($this->debug > 1) error_log(__METHOD__."($options[path],...) prop-filter='{$filter['attrs']['name']}'");
|
||||||
$prop_filter = $filter['attrs']['name'];
|
$prop_filter = $filter['attrs']['name'];
|
||||||
break;
|
break;
|
||||||
case 'text-match':
|
case 'text-match':
|
||||||
if ($this->debug > 1) error_log(__METHOD__."($path,...) text-match: $prop_filter='{$filter['data']}'");
|
if ($this->debug > 1) error_log(__METHOD__."($options[path],...) text-match: $prop_filter='{$filter['data']}'");
|
||||||
if (!isset($this->filter_prop2cal[strtoupper($prop_filter)]))
|
if (!isset($this->filter_prop2cal[strtoupper($prop_filter)]))
|
||||||
{
|
{
|
||||||
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user) unknown property '$prop_filter' --> ignored");
|
if ($this->debug) error_log(__METHOD__."($options[path],".array2string($options).",...) unknown property '$prop_filter' --> ignored");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -206,15 +251,15 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
unset($prop_filter);
|
unset($prop_filter);
|
||||||
break;
|
break;
|
||||||
case 'param-filter':
|
case 'param-filter':
|
||||||
if ($this->debug) error_log(__METHOD__."($path,...) param-filter='{$filter['attrs']['name']}' not (yet) implemented!");
|
if ($this->debug) error_log(__METHOD__."($options[path],...) param-filter='{$filter['attrs']['name']}' not (yet) implemented!");
|
||||||
break;
|
break;
|
||||||
case 'time-range':
|
case 'time-range':
|
||||||
if ($this->debug > 1) error_log(__FILE__ . __METHOD__."($path,...) time-range={$filter['attrs']['start']}-{$filter['attrs']['end']}");
|
if ($this->debug > 1) error_log(__FILE__ . __METHOD__."($options[path],...) time-range={$filter['attrs']['start']}-{$filter['attrs']['end']}");
|
||||||
$cal_filters['start'] = $filter['attrs']['start'];
|
$cal_filters['start'] = $filter['attrs']['start'];
|
||||||
$cal_filters['end'] = $filter['attrs']['end'];
|
$cal_filters['end'] = $filter['attrs']['end'];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user) unknown filter --> ignored");
|
if ($this->debug) error_log(__METHOD__."($options[path],".array2string($options).",...) unknown filter --> ignored");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +307,7 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
$cal_filters['query'][] = 'egw_cal.cal_id IN ('.implode(',',array_map(create_function('$n','return (int)$n;'),$ids)).')';
|
$cal_filters['query'][] = 'egw_cal.cal_id IN ('.implode(',',array_map(create_function('$n','return (int)$n;'),$ids)).')';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->debug > 1) error_log(__FILE__ . __METHOD__ ."($path,,,$user,$id) calendar-multiget: ids=".implode(',',$ids));
|
if ($this->debug > 1) error_log(__FILE__ . __METHOD__ ."($options[path],...,$id) calendar-multiget: ids=".implode(',',$ids));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -280,14 +325,103 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
{
|
{
|
||||||
return $event;
|
return $event;
|
||||||
}
|
}
|
||||||
$handler = $this->_get_handler();
|
$options['data'] = $this->iCal($event);
|
||||||
$options['data'] = $handler->exportVCal(array($event),'2.0','PUBLISH');
|
|
||||||
$options['mimetype'] = 'text/calendar; charset=utf-8';
|
$options['mimetype'] = 'text/calendar; charset=utf-8';
|
||||||
header('Content-Encoding: identity');
|
header('Content-Encoding: identity');
|
||||||
header('ETag: '.$this->get_etag($event));
|
header('ETag: '.$this->get_etag($event));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an iCal for the given event
|
||||||
|
*
|
||||||
|
* Taking into account virtual an real exceptions for recuring events
|
||||||
|
*
|
||||||
|
* @param array $event
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function iCal(array $event)
|
||||||
|
{
|
||||||
|
static $handler = null;
|
||||||
|
if (is_null($handler)) $handler = $this->_get_handler();
|
||||||
|
|
||||||
|
$events = array($event);
|
||||||
|
|
||||||
|
// for recuring events we have to add the exceptions
|
||||||
|
if ($this->client_shared_uid_exceptions && $event['recur_type'] && !empty($event['uid']))
|
||||||
|
{
|
||||||
|
$events =& self::get_series($event['uid'],$this->bo);
|
||||||
|
}
|
||||||
|
elseif(!$this->client_shared_uid_exceptions && $event['reference'])
|
||||||
|
{
|
||||||
|
$events[0]['uid'] .= '-'.$event['id']; // force a different uid
|
||||||
|
}
|
||||||
|
return $handler->exportVCal($events,'2.0','PUBLISH');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get array with events of a series identified by its UID (master and all exceptions)
|
||||||
|
*
|
||||||
|
* Maybe that should be part of calendar_bo
|
||||||
|
*
|
||||||
|
* @param string $uid UID
|
||||||
|
* @param calendar_bo $bo=null calendar_bo object to reuse for search call
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private static function &get_series($uid,calendar_bo $bo=null)
|
||||||
|
{
|
||||||
|
if (is_null($bo)) $bo = new calendar_bopdate();
|
||||||
|
|
||||||
|
if (!($masterId = array_shift($bo->find_event(array('uid' => $uid), 'master')))
|
||||||
|
|| !($master = $bo->read($masterId, 0, false, 'server')))
|
||||||
|
{
|
||||||
|
return array(); // should never happen
|
||||||
|
}
|
||||||
|
|
||||||
|
$exceptions = $master['recur_exception'];
|
||||||
|
|
||||||
|
$events =& $bo->search(array(
|
||||||
|
'query' => array('cal_uid' => $uid),
|
||||||
|
'daywise' => false,
|
||||||
|
'date_format' => 'server',
|
||||||
|
));
|
||||||
|
$events = array_merge(array($master), $events);
|
||||||
|
foreach($events as $k => &$recurrence)
|
||||||
|
{
|
||||||
|
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
||||||
|
// "($uid)[$k]:" . array2string($recurrence));
|
||||||
|
if (!$k) continue; // nothing to change
|
||||||
|
|
||||||
|
if ($recurrence['id'] != $master['id']) // real exception
|
||||||
|
{
|
||||||
|
//error_log('real exception: '.array2string($recurrence));
|
||||||
|
// remove from masters recur_exception, as exception is include
|
||||||
|
// at least Lightning "understands" EXDATE as exception from what's included
|
||||||
|
// in the whole resource / VCALENDAR component
|
||||||
|
// not removing it causes Lightning to remove the exception itself
|
||||||
|
if (($e = array_search($recurrence['reference'],$exceptions)) !== false)
|
||||||
|
{
|
||||||
|
unset($exceptions[$e]);
|
||||||
|
}
|
||||||
|
continue; // nothing to change
|
||||||
|
}
|
||||||
|
// now we need to check if this recurrence is an exception
|
||||||
|
if ($master['participants'] == $recurrence['participants'])
|
||||||
|
{
|
||||||
|
//error_log('NO exception: '.array2string($recurrence));
|
||||||
|
unset($events[$k]); // no exception --> remove it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// this is a virtual exception now (no extra event/cal_id in DB)
|
||||||
|
//error_log('virtual exception: '.array2string($recurrence));
|
||||||
|
$recurrence['reference'] = $recurrence['start'];
|
||||||
|
$recurrence['recur_type'] = MCAL_RECUR_NONE; // is set, as this is a copy of the master
|
||||||
|
// not for included exceptions (Lightning): $master['recur_exception'][] = $recurrence['start'];
|
||||||
|
}
|
||||||
|
$events[0]['recur_exception'] = $exceptions;
|
||||||
|
return $events;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle put request for an event
|
* Handle put request for an event
|
||||||
*
|
*
|
||||||
@ -298,7 +432,7 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
*/
|
*/
|
||||||
function put(&$options,$id,$user=null)
|
function put(&$options,$id,$user=null)
|
||||||
{
|
{
|
||||||
if($this->debug) error_log(__METHOD__."($id, $user)".print_r($options,true));
|
if ($this->debug) error_log(__METHOD__."($id, $user)".print_r($options,true));
|
||||||
$return_no_access=true; // as handled by importVCal anyway and allows it to set the status for participants
|
$return_no_access=true; // as handled by importVCal anyway and allows it to set the status for participants
|
||||||
$event = $this->_common_get_put_delete('PUT',$options,$id,$return_no_access);
|
$event = $this->_common_get_put_delete('PUT',$options,$id,$return_no_access);
|
||||||
|
|
||||||
@ -308,6 +442,12 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
return $event;
|
return $event;
|
||||||
}
|
}
|
||||||
$handler = $this->_get_handler();
|
$handler = $this->_get_handler();
|
||||||
|
|
||||||
|
if (!is_numeric($id) && ($foundEntries = $handler->find_event($options['content'], 'exact')))
|
||||||
|
{
|
||||||
|
$id = array_shift($foundEntries);
|
||||||
|
}
|
||||||
|
|
||||||
if (!($cal_id = $handler->importVCal($options['content'],is_numeric($id) ? $id : -1,
|
if (!($cal_id = $handler->importVCal($options['content'],is_numeric($id) ? $id : -1,
|
||||||
self::etag2value($this->http_if_match))))
|
self::etag2value($this->http_if_match))))
|
||||||
{
|
{
|
||||||
@ -325,11 +465,85 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix event series with exceptions, called by calendar_ical::importVCal():
|
||||||
|
* a) only series master = first event got cal_id from URL
|
||||||
|
* b) exceptions need to be checked if they are already in DB or new
|
||||||
|
* c) recurrence-id of (real not virtual) exceptions need to be re-added to master
|
||||||
|
*
|
||||||
|
* @param array &$events
|
||||||
|
*/
|
||||||
|
static function fix_series(array &$events)
|
||||||
|
{
|
||||||
|
foreach($events as $n => $event) error_log(__METHOD__." $n before: ".array2string($event));
|
||||||
|
//$master =& $events[0];
|
||||||
|
|
||||||
|
$bo = new calendar_boupdate();
|
||||||
|
|
||||||
|
// get array with orginal recurrences indexed by recurrence-id
|
||||||
|
$org_recurrences = $exceptions = array();
|
||||||
|
foreach(self::get_series($events[0]['uid'],$bo) as $k => $event)
|
||||||
|
{
|
||||||
|
if (!$k) $master = $event;
|
||||||
|
if ($event['reference'])
|
||||||
|
{
|
||||||
|
$org_recurrences[$event['reference']] = $event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign cal_id's to already existing recurrences and evtl. re-add recur_exception to master
|
||||||
|
foreach($events as $k => &$recurrence)
|
||||||
|
{
|
||||||
|
if (!$recurrence['reference'])
|
||||||
|
{
|
||||||
|
// master
|
||||||
|
$recurrence['id'] = $master['id'];
|
||||||
|
$master =& $events[$k];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from now on we deal with exceptions
|
||||||
|
$org_recurrence = $org_recurrences[$recurrence['reference']];
|
||||||
|
if (isset($org_recurrence)) // already existing recurrence
|
||||||
|
{
|
||||||
|
error_log(__METHOD__.'() setting id #'.$org_recurrence['id']).' for '.$recurrence['reference'].' = '.date('Y-m-d H:i:s',$recurrence['reference']);
|
||||||
|
$recurrence['id'] = $org_recurrence['id'];
|
||||||
|
|
||||||
|
// re-add (non-virtual) exceptions to master's recur_exception
|
||||||
|
if ($recurrence['id'] != $master['id'])
|
||||||
|
{
|
||||||
|
error_log(__METHOD__.'() re-adding recur_exception '.$recurrence['reference'].' = '.date('Y-m-d H:i:s',$recurrence['reference']));
|
||||||
|
$exceptions[] = $recurrence['reference'];
|
||||||
|
}
|
||||||
|
// remove recurrence to be able to detect deleted exceptions
|
||||||
|
unset($org_recurrences[$recurrence['reference']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$master['recur_exception'] = array_merge($exceptions, $master['recur_exception']);
|
||||||
|
|
||||||
|
// delete not longer existing recurrences
|
||||||
|
foreach($org_recurrences as $org_recurrence)
|
||||||
|
{
|
||||||
|
if ($org_recurrence['id'] != $master['id']) // non-virtual recurrence
|
||||||
|
{
|
||||||
|
error_log(__METHOD__.'() deleting #'.$org_recurrence['id']);
|
||||||
|
$bo->delete($org_recurrence['id']); // might fail because of permissions
|
||||||
|
}
|
||||||
|
else // virtual recurrence
|
||||||
|
{
|
||||||
|
error_log(__METHOD__.'() ToDO: delete virtual exception '.$org_recurrence['reference'].' = '.date('Y-m-d H:i:s',$org_recurrence['reference']));
|
||||||
|
// todo: reset status and participants to master default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach($events as $n => $event) error_log(__METHOD__." $n after: ".array2string($event));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle delete request for an event
|
* Handle delete request for an event
|
||||||
*
|
*
|
||||||
* If current user has no right to delete the event, but is an attendee, we reject the event for him.
|
* If current user has no right to delete the event, but is an attendee, we reject the event for him.
|
||||||
*
|
*
|
||||||
|
* @todo remove (non-virtual) exceptions, if series master gets deleted
|
||||||
* @param array &$options
|
* @param array &$options
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
|
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
|
||||||
@ -372,21 +586,41 @@ class calendar_groupdav extends groupdav_handler
|
|||||||
*/
|
*/
|
||||||
function get_etag($entry)
|
function get_etag($entry)
|
||||||
{
|
{
|
||||||
$e_in = $entry;
|
|
||||||
if (!is_array($entry))
|
if (!is_array($entry))
|
||||||
{
|
{
|
||||||
$entry = $this->read($entry);
|
$entry = $this->read($entry);
|
||||||
}
|
}
|
||||||
if (!$entry['id'] || !isset($entry['etag']) || !isset($entry['participants']))
|
|
||||||
{
|
|
||||||
if ($this->debug > 1) error_log(__FILE__ . __METHOD__."($e_in): id=$entry[id], etag=$entry[etag], isset(participants)=".(int)isset($entry['participants']).", title=$entry[title]: id, etag or participants not set!!!");
|
|
||||||
}
|
|
||||||
$etag = $entry['id'].':'.$entry['etag'];
|
$etag = $entry['id'].':'.$entry['etag'];
|
||||||
// add a hash over the participants and their stati
|
|
||||||
ksort($entry['participants']); // create a defined order
|
// use new MAX(modification date) of egw_cal_user table (deals with virtual exceptions too)
|
||||||
$etag .= ':'.md5(serialize($entry['participants']));
|
if (isset($entry['max_user_modified']))
|
||||||
//error_log(__FILE__ .__METHOD__ . "($entry[id] ($entry[etag]): $entry[title] --> etag=$etag");
|
{
|
||||||
return $etag;
|
$etag .= ':'.$entry['max_user_modified'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$etag .= ':'.$this->bo->so->max_user_modified($entry['id']);
|
||||||
|
}
|
||||||
|
// include exception etags into our own etag, if exceptions are included
|
||||||
|
if ($this->client_shared_uid_exceptions && !empty($entry['uid']) &&
|
||||||
|
$entry['recur_type'] != MCAL_RECUR_NONE && $entry['recur_exception'])
|
||||||
|
{
|
||||||
|
$events =& $this->bo->search(array(
|
||||||
|
'query' => array('cal_uid' => $entry['uid']),
|
||||||
|
'daywise' => false,
|
||||||
|
'enum_recuring' => false,
|
||||||
|
'date_format' => 'server',
|
||||||
|
));
|
||||||
|
foreach($events as $k => &$recurrence)
|
||||||
|
{
|
||||||
|
if ($recurrence['reference']) // ignore series master
|
||||||
|
{
|
||||||
|
$etag .= ':'.substr($this->get_etag($recurrence),1,-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//error_log(__METHOD__ . "($entry[id] ($entry[etag]): $entry[title] --> etag=$etag");
|
||||||
|
return '"'.$etag.'"';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -44,6 +44,7 @@ define('REJECTED',0);
|
|||||||
define('NO_RESPONSE',1);
|
define('NO_RESPONSE',1);
|
||||||
define('TENTATIVE',2);
|
define('TENTATIVE',2);
|
||||||
define('ACCEPTED',3);
|
define('ACCEPTED',3);
|
||||||
|
define('DELEGATED',4);
|
||||||
|
|
||||||
define('HOUR_s',60*60);
|
define('HOUR_s',60*60);
|
||||||
define('DAY_s',24*HOUR_s);
|
define('DAY_s',24*HOUR_s);
|
||||||
@ -112,7 +113,7 @@ 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 recurrence 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)
|
||||||
@ -179,6 +180,18 @@ class calendar_so
|
|||||||
$this->db->update($this->cal_table, array('cal_uid' => $event['uid']),
|
$this->db->update($this->cal_table, array('cal_uid' => $event['uid']),
|
||||||
array('cal_id' => $event['id']),__LINE__,__FILE__,'calendar');
|
array('cal_id' => $event['id']),__LINE__,__FILE__,'calendar');
|
||||||
}
|
}
|
||||||
|
if ((int) $recur_date == 0 &&
|
||||||
|
$event['recur_type'] != MCAL_RECUR_NONE &&
|
||||||
|
!empty($event['recur_exception']))
|
||||||
|
{
|
||||||
|
sort($event['recur_exception']);
|
||||||
|
if ($event['recur_exception'][0] < $event['start'])
|
||||||
|
{
|
||||||
|
// leading exceptions => move start and end
|
||||||
|
$event['end'] -= $event['start'] - $event['recur_exception'][0];
|
||||||
|
$event['start'] = $event['recur_exception'][0];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -231,6 +244,37 @@ class calendar_so
|
|||||||
return $events;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get maximum modification time of participant data of given event(s)
|
||||||
|
*
|
||||||
|
* This includes ALL recurences of an event series
|
||||||
|
*
|
||||||
|
* @param int|array $ids one or multiple cal_id's
|
||||||
|
* @return int|array (array of) modification timestamp(s)
|
||||||
|
*/
|
||||||
|
function max_user_modified($ids)
|
||||||
|
{
|
||||||
|
$etags = array();
|
||||||
|
if (is_array($ids))
|
||||||
|
{
|
||||||
|
$events = $ids;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$events = array($ids);
|
||||||
|
}
|
||||||
|
foreach ($events as $id)
|
||||||
|
{
|
||||||
|
if (!($ts = $GLOBALS['egw']->contenthistory->getTSforAction('calendar', $id, 'modify')))
|
||||||
|
{
|
||||||
|
$ts = $GLOBALS['egw']->contenthistory->getTSforAction('calendar', $id, 'add');
|
||||||
|
}
|
||||||
|
if ($ts) $etags[$id] = $ts;
|
||||||
|
}
|
||||||
|
//echo "<p>".__METHOD__.'('.array2string($ids).') = '.array2string($etags)."</p>\n";
|
||||||
|
return is_array($ids) ? $etags : $etags[$ids];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generate SQL to filter after a given category (evtl. incl. subcategories)
|
* generate SQL to filter after a given category (evtl. incl. subcategories)
|
||||||
*
|
*
|
||||||
@ -285,7 +329,7 @@ class calendar_so
|
|||||||
*
|
*
|
||||||
* 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,$_cols=null,$append='')
|
function &search($start,$end,$users,$cat_id=0,$filter='',$query='',$offset=False,$num_rows=0,$order='cal_start',$show_rejected=true,$_cols=null,$append='',$cfs=null)
|
||||||
{
|
{
|
||||||
//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,".array2string($cfs).")</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,".array2string($cfs).")</p>\n";
|
||||||
|
|
||||||
@ -326,6 +370,11 @@ class calendar_so
|
|||||||
'cal_user_type' => $type,
|
'cal_user_type' => $type,
|
||||||
'cal_user_id' => $ids,
|
'cal_user_id' => $ids,
|
||||||
));
|
));
|
||||||
|
if ($type == 'u' && $show_rejected)
|
||||||
|
{
|
||||||
|
$cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
|
||||||
|
$to_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$where[] = '('.implode(' OR ',$to_or).')';
|
$where[] = '('.implode(' OR ',$to_or).')';
|
||||||
|
|
||||||
@ -338,7 +387,7 @@ class calendar_so
|
|||||||
if ($start) $where[] = (int)$start.' < cal_end';
|
if ($start) $where[] = (int)$start.' < cal_end';
|
||||||
if ($end) $where[] = 'cal_start < '.(int)$end;
|
if ($end) $where[] = 'cal_start < '.(int)$end;
|
||||||
|
|
||||||
if (!preg_match('/^[a-z_ ,]+$/i',$order)) $order = 'cal_start'; // gard against SQL injunktion
|
if (!preg_match('/^[a-z_ ,]+$/i',$order)) $order = 'cal_start'; // gard against SQL injection
|
||||||
|
|
||||||
if ($this->db->capabilities['distinct_on_text'] && $this->db->capabilities['union'])
|
if ($this->db->capabilities['distinct_on_text'] && $this->db->capabilities['union'])
|
||||||
{
|
{
|
||||||
@ -390,12 +439,24 @@ class calendar_so
|
|||||||
$events = $ids = $recur_dates = $recur_ids = array();
|
$events = $ids = $recur_dates = $recur_ids = array();
|
||||||
foreach($rs as $row)
|
foreach($rs as $row)
|
||||||
{
|
{
|
||||||
$ids[] = $id = $row['cal_id'];
|
$id = $row['cal_id'];
|
||||||
|
if (is_numeric($id)) $ids[] = $id;
|
||||||
|
|
||||||
if ($row['cal_recur_date'])
|
if ($row['cal_recur_date'])
|
||||||
{
|
{
|
||||||
$id .= '-'.$row['cal_recur_date'];
|
$id .= '-'.$row['cal_recur_date'];
|
||||||
$recur_dates[] = $row['cal_recur_date'];
|
$recur_dates[] = $row['cal_recur_date'];
|
||||||
}
|
}
|
||||||
|
if ($row['participants'])
|
||||||
|
{
|
||||||
|
$row['participants'] = explode(',',$row['participants']);
|
||||||
|
$row['participants'] = array_combine($row['participants'],
|
||||||
|
array_fill(0,count($row['participants']),''));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$row['participants'] = array();
|
||||||
|
}
|
||||||
$row['alarm'] = array();
|
$row['alarm'] = array();
|
||||||
$row['recur_exception'] = $row['recur_exception'] ? explode(',',$row['recur_exception']) : array();
|
$row['recur_exception'] = $row['recur_exception'] ? explode(',',$row['recur_exception']) : array();
|
||||||
|
|
||||||
@ -523,6 +584,8 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
$minimum_uid_length = 8;
|
$minimum_uid_length = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$old_min = $old_duration = 0;
|
||||||
|
|
||||||
//echo '<p>'.__METHOD__.'('.array2string($event).",$change_since) event="; _debug_array($event);
|
//echo '<p>'.__METHOD__.'('.array2string($event).",$change_since) event="; _debug_array($event);
|
||||||
//error_log(__METHOD__.'('.array2string($event).",$set_recurrences,$change_since,$etag)");
|
//error_log(__METHOD__.'('.array2string($event).",$set_recurrences,$change_since,$etag)");
|
||||||
|
|
||||||
@ -545,7 +608,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)
|
||||||
{
|
{
|
||||||
@ -684,7 +759,7 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
// update start- and endtime if present in the event-array, evtl. we need to move all recurrences
|
// update start- and endtime if present in the event-array, evtl. we need to move all recurrences
|
||||||
if (isset($event['cal_start']) && isset($event['cal_end']))
|
if (isset($event['cal_start']) && isset($event['cal_end']))
|
||||||
{
|
{
|
||||||
$this->move($cal_id,$event['cal_start'],$event['cal_end'],!$cal_id ? false : $change_since);
|
$this->move($cal_id,$event['cal_start'],$event['cal_end'],!$cal_id ? false : $change_since, $old_min, $old_min + $old_duration);
|
||||||
}
|
}
|
||||||
// update participants if present in the event-array
|
// update participants if present in the event-array
|
||||||
if (isset($event['cal_participants']))
|
if (isset($event['cal_participants']))
|
||||||
@ -860,7 +935,7 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
/**
|
/**
|
||||||
* splits the combined status, quantity and role
|
* splits the combined status, quantity and role
|
||||||
*
|
*
|
||||||
* @param string &$status I: combined value, O: status letter: U, T, A, R
|
* @param string &$status I: combined value, O: status letter: U, T, A, R, D
|
||||||
* @param int &$quantity only O: quantity
|
* @param int &$quantity only O: quantity
|
||||||
* @param string &$role only O: role
|
* @param string &$role only O: role
|
||||||
*/
|
*/
|
||||||
@ -875,6 +950,10 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
if ($matches[2]) $role = $matches[2];
|
if ($matches[2]) $role = $matches[2];
|
||||||
$status = $status[0];
|
$status = $status[0];
|
||||||
}
|
}
|
||||||
|
elseif ($status === true)
|
||||||
|
{
|
||||||
|
$status = 'U';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1009,7 +1088,8 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
REJECTED => 'R',
|
REJECTED => 'R',
|
||||||
NO_RESPONSE => 'U',
|
NO_RESPONSE => 'U',
|
||||||
TENTATIVE => 'T',
|
TENTATIVE => 'T',
|
||||||
ACCEPTED => 'A'
|
ACCEPTED => 'A',
|
||||||
|
DELEGATED => 'D'
|
||||||
);
|
);
|
||||||
if (!(int)$cal_id || !(int)$user_id && $user_type != 'e')
|
if (!(int)$cal_id || !(int)$user_id && $user_type != 'e')
|
||||||
{
|
{
|
||||||
@ -1323,16 +1403,14 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
* get stati of all recurrences of an event for a specific participant
|
* get stati of all recurrences of an event for a specific participant
|
||||||
*
|
*
|
||||||
* @param int $cal_id
|
* @param int $cal_id
|
||||||
* @param int $uid participant uid
|
* @param int $uid=null participant uid; if == null return onyl the recur dates
|
||||||
* @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)
|
||||||
*
|
*
|
||||||
* @return array recur_date => status pairs (index 0 => main status)
|
* @return array recur_date => status pairs (index 0 => main status)
|
||||||
*/
|
*/
|
||||||
function get_recurrences($cal_id, $uid, $start=0, $end=0)
|
function get_recurrences($cal_id, $uid=null, $start=0, $end=0)
|
||||||
{
|
{
|
||||||
$user_type = $user_id = null;
|
|
||||||
self::split_user($uid, $user_type, $user_id);
|
|
||||||
$participant_status = array();
|
$participant_status = array();
|
||||||
$where = array('cal_id' => $cal_id);
|
$where = array('cal_id' => $cal_id);
|
||||||
if ($start != 0 && $end == 0) $where[] = '(cal_recur_date = 0 OR cal_recur_date >= ' . (int)$start . ')';
|
if ($start != 0 && $end == 0) $where[] = '(cal_recur_date = 0 OR cal_recur_date >= ' . (int)$start . ')';
|
||||||
@ -1347,6 +1425,9 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
// inititalize the array
|
// inititalize the array
|
||||||
$participant_status[$row['cal_recur_date']] = null;
|
$participant_status[$row['cal_recur_date']] = null;
|
||||||
}
|
}
|
||||||
|
if (is_null($uid)) return $participant_status;
|
||||||
|
$user_type = $user_id = null;
|
||||||
|
self::split_user($uid, $user_type, $user_id);
|
||||||
$where = array(
|
$where = array(
|
||||||
'cal_id' => $cal_id,
|
'cal_id' => $cal_id,
|
||||||
'cal_user_type' => $user_type ? $user_type : 'u',
|
'cal_user_type' => $user_type ? $user_type : 'u',
|
||||||
@ -1426,21 +1507,21 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
* irregular participant stati
|
* irregular participant stati
|
||||||
*
|
*
|
||||||
* @param array $event Recurring Event.
|
* @param array $event Recurring Event.
|
||||||
* @param int servertime=0 == 0 -> export event with UTC timestamps
|
|
||||||
* != 0 -> export with servertime timestamps
|
|
||||||
* @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 boolean $show_rejected=true should the search return rejected invitations
|
||||||
*
|
*
|
||||||
* @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, $servertime=0, $start=0, $end=0)
|
function get_recurrence_exceptions(&$event, $start=0, $end=0, $show_rejected=true)
|
||||||
{
|
{
|
||||||
$cal_id = (int) $event['id'];
|
$cal_id = (int) $event['id'];
|
||||||
|
$user = $GLOBALS['egw_info']['user']['account_id'];
|
||||||
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 = $removed_days = array();
|
||||||
|
|
||||||
$participants = $this->get_participants($event['id'], 0);
|
$participants = $this->get_participants($cal_id, 0);
|
||||||
|
|
||||||
// Check if the stati for all participants are identical for all recurrences
|
// Check if the stati for all participants are identical for all recurrences
|
||||||
foreach ($participants as $uid => $attendee)
|
foreach ($participants as $uid => $attendee)
|
||||||
@ -1450,13 +1531,18 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
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);
|
$recurrences = $this->get_recurrences($cal_id, $uid, $start, $end);
|
||||||
foreach ($recurrences as $recur_date => $recur_status)
|
foreach ($recurrences as $recur_date => $recur_status)
|
||||||
{
|
{
|
||||||
|
if ($uid == $user && !$show_rejected && $recur_status[0] == 'R')
|
||||||
|
{
|
||||||
|
$removed_days[$recur_date] = $recur_date;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ($recur_date && $recur_status != $recurrences[0])
|
if ($recur_date && $recur_status != $recurrences[0])
|
||||||
{
|
{
|
||||||
// Every distinct status results in an exception
|
// Every distinct status results in an exception
|
||||||
$days[] = $recur_date;
|
$days[$recur_date] = $recur_date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1464,6 +1550,7 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$days = array_diff($days, $removed_days);
|
||||||
$days = array_unique($days);
|
$days = array_unique($days);
|
||||||
sort($days);
|
sort($days);
|
||||||
return $days;
|
return $days;
|
||||||
|
BIN
calendar/templates/default/images/forward.gif
Normal file
BIN
calendar/templates/default/images/forward.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 319 B |
BIN
calendar/templates/default/images/forward.png
Normal file
BIN
calendar/templates/default/images/forward.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 494 B |
@ -1222,7 +1222,7 @@ class Horde_iCalendar {
|
|||||||
$attr_string = $name . $params_str;
|
$attr_string = $name . $params_str;
|
||||||
if (strlen($value) > 0) {
|
if (strlen($value) > 0) {
|
||||||
$attr_string .= ':' . $value;
|
$attr_string .= ':' . $value;
|
||||||
} elseif ($name != 'RRULE') {
|
} elseif ($name != 'RRULE' && $name != 'ATTENDEE' && $name != 'ORGANIZER') {
|
||||||
$attr_string .= ':';
|
$attr_string .= ':';
|
||||||
}
|
}
|
||||||
if (!$this->isOldFormat()) {
|
if (!$this->isOldFormat()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user