2005-06-14 23:54:17 +02:00
< ? php
2007-03-09 12:39:47 +01:00
/**
2011-04-06 21:26:10 +02:00
* EGroupware - Calendar ' s buisness - object - access + update
2007-03-09 12:39:47 +01:00
*
* @ link http :// www . egroupware . org
* @ package calendar
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2009-07-15 22:35:56 +02:00
* @ author Joerg Lehrke < jlehrke @ noc . de >
2011-04-06 21:26:10 +02:00
* @ copyright ( c ) 2005 - 11 by RalfBecker - At - outdoor - training . de
2007-03-09 12:39:47 +01:00
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ version $Id $
*/
2005-06-14 23:54:17 +02:00
2009-08-04 19:14:16 +02:00
// types of messsages send by calendar_boupdate::send_update
2005-11-09 00:15:14 +01:00
define ( 'MSG_DELETED' , 0 );
define ( 'MSG_MODIFIED' , 1 );
define ( 'MSG_ADDED' , 2 );
define ( 'MSG_REJECTED' , 3 );
define ( 'MSG_TENTATIVE' , 4 );
define ( 'MSG_ACCEPTED' , 5 );
define ( 'MSG_ALARM' , 6 );
define ( 'MSG_DISINVITE' , 7 );
2010-02-17 14:29:28 +01:00
define ( 'MSG_DELEGATED' , 8 );
2005-11-09 00:15:14 +01:00
2005-06-14 23:54:17 +02:00
/**
2005-11-09 00:15:14 +01:00
* Class to access AND manipulate all calendar data ( business object )
2005-06-14 23:54:17 +02:00
*
* The new UI , BO and SO classes have a strikt definition , in which time - zone they operate :
* UI only operates in user - time , so there have to be no conversation at all !!!
* BO ' s functions take and return user - time only ( ! ), they convert internaly everything to servertime , because
2005-11-09 00:15:14 +01:00
* SO operates only on server - time
2005-06-14 23:54:17 +02:00
*
* As this BO class deals with dates / times of several types and timezone , each variable should have a postfix
* appended , telling with type it is : _s = seconds , _su = secs in user - time , _ss = secs in server - time , _h = hours
*
* All new BO code ( should be true for eGW in general ) NEVER use any $_REQUEST ( $_POST or $_GET ) vars itself .
2005-11-09 00:15:14 +01:00
* Nor does it store the state of any UI - elements ( eg . cat - id selectbox ) . All this is the task of the UI class ( es ) !!!
2005-06-14 23:54:17 +02:00
*
2005-11-09 00:15:14 +01:00
* All permanent debug messages of the calendar - code should done via the debug - message method of the bocal class ! ! !
2005-06-14 23:54:17 +02:00
*/
2008-06-07 19:45:33 +02:00
class calendar_boupdate extends calendar_bo
2005-06-14 23:54:17 +02:00
{
2009-12-03 19:24:19 +01:00
/**
* Category ACL allowing to add a given category
*/
const CAT_ACL_ADD = 512 ;
/**
* Category ACL allowing to change status of a participant
*/
const CAT_ACL_STATUS = 1024 ;
2006-10-02 07:36:55 +02:00
/**
* name of method to debug or level of debug - messages :
* False = Off as higher as more messages you get ; - )
* 1 = function - calls incl . parameters to general functions like search , read , write , delete
* 2 = function - calls to exported helper - functions like check_perms
* 4 = function - calls to exported conversation - functions like date2ts , date2array , ...
* 5 = function - calls to private functions
* @ var mixed
*/
var $debug ;
2008-05-08 00:12:25 +02:00
2005-11-09 00:15:14 +01:00
/**
2010-01-29 22:42:54 +01:00
* Set Logging
*
* @ var boolean
2005-11-09 00:15:14 +01:00
*/
2010-01-29 22:42:54 +01:00
var $log = false ;
2010-03-09 18:03:41 +01:00
var $logfile = '/tmp/log-calendar-boupdate' ;
2005-11-09 00:15:14 +01:00
2010-02-09 22:56:39 +01:00
/**
* Cached timezone data
*
* @ var array id => data
*/
protected static $tz_cache = array ();
2005-06-14 23:54:17 +02:00
/**
* Constructor
*/
2008-06-07 19:45:33 +02:00
function __construct ()
2005-06-14 23:54:17 +02:00
{
2009-08-04 19:14:16 +02:00
if ( $this -> debug > 0 ) $this -> debug_message ( 'calendar_boupdate::__construct() started' , True );
2005-06-14 23:54:17 +02:00
2008-06-07 19:45:33 +02:00
parent :: __construct (); // calling the parent constructor
2008-05-08 00:12:25 +02:00
2009-08-04 19:14:16 +02:00
if ( $this -> debug > 0 ) $this -> debug_message ( 'calendar_boupdate::__construct() finished' , True );
2005-06-14 23:54:17 +02:00
}
/**
2008-05-08 00:12:25 +02:00
* updates or creates an event , it ( optionaly ) checks for conflicts and sends the necessary notifications
2005-06-14 23:54:17 +02:00
*
* @ param array & $event event - array , on return some values might be changed due to set defaults
* @ param boolean $ignore_conflicts = false just ignore conflicts or do a conflict check and return the conflicting events
2011-04-07 10:01:48 +02:00
* @ param boolean $touch_modified = true NOT USED ANYMORE ( was only used in old csv - import ), modified & modifier is always updated !
2009-07-15 22:35:56 +02:00
* @ param boolean $ignore_acl = false should we ignore the acl
2009-10-25 19:20:58 +01:00
* @ param boolean $updateTS = true update the content history of the event
2009-12-03 19:24:19 +01:00
* @ param array & $messages = null messages about because of missing ACL removed participants or categories
2005-06-14 23:54:17 +02:00
* @ return mixed on success : int $cal_id > 0 , on error false or array with conflicting events ( only if $check_conflicts )
2005-11-09 00:15:14 +01:00
* Please note : the events are not garantied to be readable by the user ( no read grant or private ) !
2010-11-11 09:51:13 +01:00
*
* @ ToDo current conflict checking code does NOT cope quantity - wise correct with multiple non - overlapping
* events overlapping the event to store : the quantity sum is used , even as the events dont overlap !
*
* ++++++++ ++++++++
* + + + B + If A get checked for conflicts , the check used for resource quantity is
* + + ++++++++
* + A + quantity ( A , resource ) + quantity ( B , resource ) + quantity ( C , resource ) > maxBookable ( resource )
* + + ++++++++
* + + + C + which is clearly wrong for everything with a maximum quantity > 1
* ++++++++ ++++++++
2005-06-14 23:54:17 +02:00
*/
2011-03-09 18:26:20 +01:00
function update ( & $event , $ignore_conflicts = false , $touch_modified = true , $ignore_acl = false , $updateTS = true , & $messages = null , $skip_notification = false )
2005-06-14 23:54:17 +02:00
{
2008-05-17 15:00:34 +02:00
//error_log(__METHOD__."(".array2string($event).",$ignore_conflicts,$touch_modified,$ignore_acl)");
2010-11-11 09:51:13 +01:00
2005-11-09 00:15:14 +01:00
if ( $this -> debug > 1 || $this -> debug == 'update' )
{
2009-08-04 19:14:16 +02:00
$this -> debug_message ( 'calendar_boupdate::update(%1,ignore_conflict=%2,touch_modified=%3,ignore_acl=%4)' ,
2005-11-09 00:15:14 +01:00
false , $event , $ignore_conflicts , $touch_modified , $ignore_acl );
}
2005-06-14 23:54:17 +02:00
// check some minimum requirements:
// - new events need start, end and title
// - updated events cant set start, end or title to empty
if ( ! $event [ 'id' ] && ( ! $event [ 'start' ] || ! $event [ 'end' ] || ! $event [ 'title' ]) ||
2008-05-08 00:12:25 +02:00
$event [ 'id' ] && ( isset ( $event [ 'start' ]) && ! $event [ 'start' ] || isset ( $event [ 'end' ]) && ! $event [ 'end' ] ||
2005-11-23 22:39:37 +01:00
isset ( $event [ 'title' ]) && ! $event [ 'title' ]))
2005-06-14 23:54:17 +02:00
{
return false ;
}
2008-01-17 16:39:22 +01:00
2010-06-23 16:52:55 +02:00
$status_reset_to_unknown = false ;
2010-11-11 09:51:13 +01:00
2009-12-03 19:24:19 +01:00
if (( $new_event = ! $event [ 'id' ])) // some defaults for new entries
2005-06-14 23:54:17 +02:00
{
// if no owner given, set user to owner
if ( ! $event [ 'owner' ]) $event [ 'owner' ] = $this -> user ;
// set owner as participant if none is given
2009-03-20 09:17:54 +01:00
if ( ! is_array ( $event [ 'participants' ]) || ! count ( $event [ 'participants' ]))
2005-06-14 23:54:17 +02:00
{
2009-11-26 19:36:19 +01:00
$status = $event [ 'owner' ] == $this -> user ? 'A' : 'U' ;
$status = calendar_so :: combine_status ( $status , 1 , 'CHAIR' );
$event [ 'participants' ] = array ( $event [ 'owner' ] => $status );
2008-05-08 00:12:25 +02:00
}
2009-12-03 19:24:19 +01:00
}
2009-11-26 19:36:19 +01:00
2005-11-09 00:15:14 +01:00
// check if user has the permission to update / create the event
2009-11-26 19:36:19 +01:00
if ( ! $ignore_acl && ( ! $new_event && ! $this -> check_perms ( EGW_ACL_EDIT , $event [ 'id' ]) ||
$new_event && ! $this -> check_perms ( EGW_ACL_EDIT , 0 , $event [ 'owner' ])) &&
2007-01-20 08:11:06 +01:00
! $this -> check_perms ( EGW_ACL_ADD , 0 , $event [ 'owner' ]))
2005-11-09 00:15:14 +01:00
{
return false ;
}
2009-11-26 19:36:19 +01:00
if ( $new_event )
{
2010-03-16 10:26:01 +01:00
$old_event = array ();
2009-11-26 19:36:19 +01:00
}
else
2009-11-19 19:56:04 +01:00
{
$old_event = $this -> read (( int ) $event [ 'id' ], null , $ignore_acl );
}
2009-11-26 19:36:19 +01:00
2009-11-19 19:56:04 +01:00
// do we need to check, if user is allowed to invite the invited participants
if ( $this -> require_acl_invite && ( $removed = $this -> remove_no_acl_invite ( $event , $old_event )))
{
2009-12-03 19:24:19 +01:00
// report removed participants back to user
foreach ( $removed as $key => $account_id )
{
$removed [ $key ] = $this -> participant_name ( $account_id );
}
$messages [] = lang ( '%1 participants removed because of missing invite grants' , count ( $removed )) .
': ' . implode ( ', ' , $removed );
}
// check category based ACL
if ( $event [ 'category' ])
{
if ( ! is_array ( $event [ 'category' ])) $event [ 'category' ] = explode ( ',' , $event [ 'category' ]);
2010-03-16 10:26:01 +01:00
if ( ! $old_event || ! isset ( $old_event [ 'category' ]))
2009-12-03 19:24:19 +01:00
{
2010-03-16 10:26:01 +01:00
$old_event [ 'category' ] = array ();
}
elseif ( ! is_array ( $old_event [ 'category' ]))
{
$old_event [ 'category' ] = explode ( ',' , $old_event [ 'category' ]);
2009-12-03 19:24:19 +01:00
}
foreach ( $event [ 'category' ] as $key => $cat_id )
{
// check if user is allowed to update event categories
2009-12-07 14:31:51 +01:00
if (( ! $old_event || ! in_array ( $cat_id , $old_event [ 'category' ])) &&
self :: has_cat_right ( self :: CAT_ACL_ADD , $cat_id , $this -> user ) === false )
2009-12-03 19:24:19 +01:00
{
unset ( $event [ 'category' ][ $key ]);
// report removed category to user
$removed_cats [ $cat_id ] = $this -> categories -> id2name ( $cat_id );
2009-12-07 14:31:51 +01:00
continue ; // no further check, as cat was removed
2009-12-03 19:24:19 +01:00
}
// for new or moved events check status of participants, if no category status right --> set all status to 'U' = unknown
2009-12-07 14:31:51 +01:00
if ( ! $status_reset_to_unknown &&
self :: has_cat_right ( self :: CAT_ACL_STATUS , $cat_id , $this -> user ) === false &&
2009-12-03 19:24:19 +01:00
( ! $old_event || $old_event [ 'start' ] != $event [ 'start' ] || $old_event [ 'end' ] != $event [ 'end' ]))
{
2010-03-07 00:06:43 +01:00
foreach (( array ) $event [ 'participants' ] as $uid => $status )
2009-12-03 19:24:19 +01:00
{
calendar_so :: split_status ( $status , $q , $r );
if ( $status != 'U' )
{
$event [ 'participants' ][ $uid ] = calendar_so :: combine_status ( 'U' , $q , $r );
// todo: report reset status to user
}
}
$status_reset_to_unknown = true ; // once is enough
}
}
if ( $removed_cats )
{
$messages [] = lang ( 'Category %1 removed because of missing rights' , implode ( ', ' , $removed_cats ));
}
if ( $status_reset_to_unknown )
{
$messages [] = lang ( 'Status of participants set to unknown because of missing category rights' );
}
2009-11-19 19:56:04 +01:00
}
2005-06-14 23:54:17 +02:00
// check for conflicts only happens !$ignore_conflicts AND if start + end date are given
2005-11-09 00:15:14 +01:00
if ( ! $ignore_conflicts && ! $event [ 'non_blocking' ] && isset ( $event [ 'start' ]) && isset ( $event [ 'end' ]))
2005-06-14 23:54:17 +02:00
{
$types_with_quantity = array ();
foreach ( $this -> resources as $type => $data )
{
2006-03-05 11:26:03 +01:00
if ( $data [ 'max_quantity' ]) $types_with_quantity [] = $type ;
2005-06-14 23:54:17 +02:00
}
// get all NOT rejected participants and evtl. their quantity
$quantity = $users = array ();
foreach ( $event [ 'participants' ] as $uid => $status )
{
2009-08-07 12:20:58 +02:00
calendar_so :: split_status ( $status , $q , $r );
2009-10-25 19:20:58 +01:00
if ( $status [ 0 ] == 'R' ) continue ; // ignore rejected participants
2008-05-08 00:12:25 +02:00
2007-12-03 18:06:42 +01:00
if ( $uid < 0 ) // group, check it's members too
{
$users += $GLOBALS [ 'egw' ] -> accounts -> members ( $uid , true );
$users = array_unique ( $users );
}
2005-06-14 23:54:17 +02:00
$users [] = $uid ;
2008-05-08 17:02:35 +02:00
if ( in_array ( $uid [ 0 ], $types_with_quantity ))
2005-06-14 23:54:17 +02:00
{
2009-08-07 12:20:58 +02:00
$quantity [ $uid ] = $q ;
2005-06-14 23:54:17 +02:00
}
}
$overlapping_events =& $this -> search ( array (
'start' => $event [ 'start' ],
'end' => $event [ 'end' ],
'users' => $users ,
2005-11-09 00:15:14 +01:00
'ignore_acl' => true , // otherwise we get only events readable by the user
2006-03-16 19:03:43 +01:00
'enum_groups' => true , // otherwise group-events would not block time
2005-06-14 23:54:17 +02:00
));
2005-11-09 00:15:14 +01:00
if ( $this -> debug > 2 || $this -> debug == 'update' )
{
2009-08-04 19:14:16 +02:00
$this -> debug_message ( 'calendar_boupdate::update() checking for potential overlapping events for users %1 from %2 to %3' , false , $users , $event [ 'start' ], $event [ 'end' ]);
2005-11-09 00:15:14 +01:00
}
2005-06-14 23:54:17 +02:00
$max_quantity = $possible_quantity_conflicts = $conflicts = array ();
foreach (( array ) $overlapping_events as $k => $overlap )
{
2005-11-09 00:15:14 +01:00
if ( $overlap [ 'id' ] == $event [ 'id' ] || // that's the event itself
2005-11-23 22:39:37 +01:00
$overlap [ 'id' ] == $event [ 'reference' ] || // event is an exception of overlap
2005-11-09 00:15:14 +01:00
$overlap [ 'non_blocking' ]) // that's a non_blocking event
{
continue ;
}
if ( $this -> debug > 3 || $this -> debug == 'update' )
{
2009-08-04 19:14:16 +02:00
$this -> debug_message ( 'calendar_boupdate::update() checking overlapping event %1' , false , $overlap );
2005-11-09 00:15:14 +01:00
}
2005-06-14 23:54:17 +02:00
// check if the overlap is with a rejected participant or within the allowed quantity
$common_parts = array_intersect ( $users , array_keys ( $overlap [ 'participants' ]));
foreach ( $common_parts as $n => $uid )
{
2010-11-11 09:51:13 +01:00
$status = $overlap [ 'participants' ][ $uid ];
calendar_so :: split_status ( $status , $q , $r );
if ( $status == 'R' )
2005-06-14 23:54:17 +02:00
{
2010-03-25 21:23:22 +01:00
unset ( $common_parts [ $n ]);
2005-06-14 23:54:17 +02:00
continue ;
}
2008-05-08 17:02:35 +02:00
if ( is_numeric ( $uid ) || ! in_array ( $uid [ 0 ], $types_with_quantity ))
2005-06-14 23:54:17 +02:00
{
continue ; // no quantity check: quantity allways 1 ==> conflict
}
if ( ! isset ( $max_quantity [ $uid ]))
{
2006-03-05 11:26:03 +01:00
$res_info = $this -> resource_info ( $uid );
2008-05-08 17:02:35 +02:00
$max_quantity [ $uid ] = $res_info [ $this -> resources [ $uid [ 0 ]][ 'max_quantity' ]];
2005-06-14 23:54:17 +02:00
}
2010-11-11 09:51:13 +01:00
$quantity [ $uid ] += $q ;
2006-03-05 11:26:03 +01:00
if ( $quantity [ $uid ] <= $max_quantity [ $uid ])
2005-06-14 23:54:17 +02:00
{
$possible_quantity_conflicts [ $uid ][] =& $overlapping_events [ $k ]; // an other event can give the conflict
2006-03-05 11:26:03 +01:00
unset ( $common_parts [ $n ]);
2005-06-14 23:54:17 +02:00
continue ;
}
// now we have a quantity conflict for $uid
}
2005-11-09 00:15:14 +01:00
if ( count ( $common_parts ))
2005-06-14 23:54:17 +02:00
{
2005-11-09 00:15:14 +01:00
if ( $this -> debug > 3 || $this -> debug == 'update' )
{
2009-08-04 19:14:16 +02:00
$this -> debug_message ( 'calendar_boupdate::update() conflicts with the following participants found %1' , false , $common_parts );
2005-11-09 00:15:14 +01:00
}
$conflicts [ $overlap [ 'id' ] . '-' . $this -> date2ts ( $overlap [ 'start' ])] =& $overlapping_events [ $k ];
2005-06-14 23:54:17 +02:00
}
}
// check if we are withing the allowed quantity and if not add all events using that resource
2008-07-11 10:17:22 +02:00
// seems this function is doing very strange things, it gives empty conflicts
2005-06-14 23:54:17 +02:00
foreach ( $max_quantity as $uid => $max )
{
if ( $quantity [ $uid ] > $max )
{
2006-03-05 11:26:03 +01:00
foreach (( array ) $possible_quantity_conflicts [ $uid ] as $conflict )
2005-06-14 23:54:17 +02:00
{
2006-03-05 11:26:03 +01:00
$conflicts [ $conflict [ 'id' ] . '-' . $this -> date2ts ( $conflict [ 'start' ])] =& $possible_quantity_conflicts [ $k ];
2005-06-14 23:54:17 +02:00
}
}
}
unset ( $possible_quantity_conflicts );
2008-05-08 00:12:25 +02:00
2005-06-14 23:54:17 +02:00
if ( count ( $conflicts ))
{
2005-11-09 00:15:14 +01:00
foreach ( $conflicts as $key => $conflict )
{
2009-10-29 10:25:54 +01:00
$conflict [ 'participants' ] = array_intersect_key ( $conflict [ 'participants' ], $event [ 'participants' ]);
2005-11-09 00:15:14 +01:00
if ( ! $this -> check_perms ( EGW_ACL_READ , $conflict ))
{
$conflicts [ $key ] = array (
'id' => $conflict [ 'id' ],
'title' => lang ( 'busy' ),
2009-10-29 10:25:54 +01:00
'participants' => $conflict [ 'participants' ],
2005-11-09 00:15:14 +01:00
'start' => $conflict [ 'start' ],
'end' => $conflict [ 'end' ],
);
}
}
if ( $this -> debug > 2 || $this -> debug == 'update' )
{
2009-08-04 19:14:16 +02:00
$this -> debug_message ( 'calendar_boupdate::update() %1 conflicts found %2' , false , count ( $conflicts ), $conflicts );
2005-11-09 00:15:14 +01:00
}
2005-06-14 23:54:17 +02:00
return $conflicts ;
2008-05-08 00:12:25 +02:00
}
2005-06-14 23:54:17 +02:00
}
2008-01-17 16:39:22 +01:00
2005-11-09 00:15:14 +01:00
//echo "saving $event[id]="; _debug_array($event);
$event2save = $event ;
2009-10-25 19:20:58 +01:00
if ( ! ( $cal_id = $this -> save ( $event , $ignore_acl , $updateTS )))
2005-06-14 23:54:17 +02:00
{
return $cal_id ;
}
2008-01-17 16:39:22 +01:00
2005-11-09 00:15:14 +01:00
$event = $this -> read ( $cal_id ); // we re-read the event, in case only partial information was update and we need the full info for the notifies
//echo "new $cal_id="; _debug_array($event);
2010-09-27 20:03:46 +02:00
if ( $old_event [ 'deleted' ] && $event [ 'deleted' ] == null )
{
// Restored, bring back links
egw_link :: restore ( 'calendar' , $cal_id );
2010-11-11 09:51:13 +01:00
}
2005-11-09 00:15:14 +01:00
if ( $this -> log_file )
{
$this -> log2file ( $event2save , $event , $old_event );
}
// send notifications
2011-03-09 18:26:20 +01:00
if ( ! $skip_notification )
2005-11-09 00:15:14 +01:00
{
2011-03-09 18:26:20 +01:00
if ( $new_event )
{
$this -> send_update ( MSG_ADDED , $event [ 'participants' ], '' , $event );
}
else // update existing event
{
$this -> check4update ( $event , $old_event );
}
2005-11-09 00:15:14 +01:00
}
2011-03-09 18:26:20 +01:00
2005-11-09 00:15:14 +01:00
// notify the link-class about the update, as other apps may be subscribt to it
2008-03-25 16:33:10 +01:00
egw_link :: notify_update ( 'calendar' , $cal_id , $event );
2005-06-14 23:54:17 +02:00
return $cal_id ;
}
2009-11-19 19:56:04 +01:00
/**
* Remove participants current user has no right to invite
*
* @ param array & $event new event
* @ param array $old_event = null old event with already invited participants
* @ return array removed participants because of missing invite grants
*/
public function remove_no_acl_invite ( array & $event , array $old_event = null )
{
if ( ! $this -> require_acl_invite )
{
return array (); // nothing to check, everyone can invite everyone else
}
if ( $event [ 'id' ] && is_null ( $old_event ))
{
$old_event = $this -> read ( $event [ 'id' ]);
}
$removed = array ();
2011-02-01 15:28:56 +01:00
foreach (( array ) $event [ 'participants' ] as $uid => $status )
2009-11-19 19:56:04 +01:00
{
2009-12-07 14:31:51 +01:00
if (( is_null ( $old_event ) || ! isset ( $old_event [ 'participants' ][ $uid ])) && ! $this -> check_acl_invite ( $uid ))
2009-11-19 19:56:04 +01:00
{
unset ( $event [ 'participants' ][ $uid ]); // remove participant
$removed [] = $uid ;
}
}
2009-11-24 14:13:41 +01:00
//echo "<p>".__METHOD__."($event[title],".($old_event?'$old_event':'NULL').") returning ".array2string($removed)."</p>";
2009-11-19 19:56:04 +01:00
return $removed ;
}
/**
* Check if current user is allowed to invite a given participant
*
* @ param int | string $uid
* @ return boolean
*/
public function check_acl_invite ( $uid )
{
if ( ! is_numeric ( $uid )) return true ; // nothing implemented for resources so far
2009-11-26 19:36:19 +01:00
2009-11-24 14:13:41 +01:00
if ( ! $this -> require_acl_invite )
{
$ret = true ; // no grant required
}
2009-12-07 14:31:51 +01:00
elseif ( $this -> require_acl_invite == 'groups' && $GLOBALS [ 'egw' ] -> accounts -> get_type ( $uid ) != 'g' )
2009-11-24 14:13:41 +01:00
{
$ret = true ; // grant only required for groups
}
else
2009-11-19 19:56:04 +01:00
{
2009-11-24 14:13:41 +01:00
$ret = $this -> check_perms ( EGW_ACL_INVITE , 0 , $uid );
2009-11-19 19:56:04 +01:00
}
2009-11-24 14:13:41 +01:00
//error_log(__METHOD__."($uid) = ".array2string($ret));
2009-12-07 14:31:51 +01:00
//echo "<p>".__METHOD__."($uid) require_acl_invite=$this->require_acl_invite returning ".array2string($ret)."</p>\n";
2009-11-24 14:13:41 +01:00
return $ret ;
2009-11-19 19:56:04 +01:00
}
2005-11-09 00:15:14 +01:00
/**
2009-03-20 09:17:54 +01:00
* Check for added , modified or deleted participants AND notify them
2005-11-09 00:15:14 +01:00
*
* @ param array $new_event the updated event
* @ param array $old_event the event before the update
2010-08-02 16:15:05 +02:00
* @ todo check if there is a real change , not assume every save is a change
2008-05-08 00:12:25 +02:00
*/
2005-11-09 00:15:14 +01:00
function check4update ( $new_event , $old_event )
{
2010-08-02 16:15:05 +02:00
//error_log(__METHOD__."($new_event[title])");
2005-11-09 00:15:14 +01:00
$modified = $added = $deleted = array ();
2008-05-08 00:12:25 +02:00
2009-08-04 19:14:16 +02:00
//echo "<p>calendar_boupdate::check4update() new participants = ".print_r($new_event['participants'],true).", old participants =".print_r($old_event['participants'],true)."</p>\n";
2005-11-09 00:15:14 +01:00
// Find modified and deleted participants ...
foreach ( $old_event [ 'participants' ] as $old_userid => $old_status )
{
if ( isset ( $new_event [ 'participants' ][ $old_userid ]))
{
$modified [ $old_userid ] = $new_event [ 'participants' ][ $old_userid ];
}
else
{
$deleted [ $old_userid ] = $old_status ;
}
}
2008-07-11 10:17:22 +02:00
// Find new participants ...
2010-04-14 10:41:45 +02:00
foreach (( array ) $new_event [ 'participants' ] as $new_userid => $new_status )
2005-11-09 00:15:14 +01:00
{
if ( ! isset ( $old_event [ 'participants' ][ $new_userid ]))
{
$added [ $new_userid ] = 'U' ;
}
}
2009-08-04 19:14:16 +02:00
//echo "<p>calendar_boupdate::check4update() added=".print_r($added,true).", modified=".print_r($modified,true).", deleted=".print_r($deleted,true)."</p>\n";
2005-11-09 00:15:14 +01:00
if ( count ( $added ) || count ( $modified ) || count ( $deleted ))
{
if ( count ( $added ))
{
$this -> send_update ( MSG_ADDED , $added , $old_event , $new_event );
}
if ( count ( $modified ))
{
$this -> send_update ( MSG_MODIFIED , $modified , $old_event , $new_event );
}
if ( count ( $deleted ))
{
$this -> send_update ( MSG_DISINVITE , $deleted , $new_event );
}
}
}
/**
* checks if $userid has requested ( in $part_prefs ) updates for $msg_type
*
* @ param int $userid numerical user - id
* @ param array $part_prefs preferces of the user $userid
* @ param int $msg_type type of the notification : MSG_ADDED , MSG_MODIFIED , MSG_ACCEPTED , ...
* @ param array $old_event Event before the change
* @ param array $new_event Event after the change
* @ return boolean true = update requested , flase otherwise
*/
function update_requested ( $userid , $part_prefs , $msg_type , $old_event , $new_event )
{
if ( $msg_type == MSG_ALARM )
{
return True ; // always True for now
}
$want_update = 0 ;
// the following switch falls through all cases, as each included the following too
//
2010-02-17 14:29:28 +01:00
$msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE || $msg_type == MSG_DELEGATED ;
2005-11-09 00:15:14 +01:00
switch ( $ru = $part_prefs [ 'calendar' ][ 'receive_updates' ])
{
case 'responses' :
2010-08-02 16:15:05 +02:00
++ $want_update ;
2005-11-09 00:15:14 +01:00
case 'modifications' :
2010-08-02 16:15:05 +02:00
if ( ! $msg_is_response )
2005-11-09 00:15:14 +01:00
{
++ $want_update ;
}
case 'time_change_4h' :
case 'time_change' :
$diff = max ( abs ( $this -> date2ts ( $old_event [ 'start' ]) - $this -> date2ts ( $new_event [ 'start' ])),
abs ( $this -> date2ts ( $old_event [ 'end' ]) - $this -> date2ts ( $new_event [ 'end' ])));
$check = $ru == 'time_change_4h' ? 4 * 60 * 60 - 1 : 0 ;
if ( $msg_type == MSG_MODIFIED && $diff > $check )
{
++ $want_update ;
}
case 'add_cancel' :
if ( $old_event [ 'owner' ] == $userid && $msg_is_response ||
$msg_type == MSG_DELETED || $msg_type == MSG_ADDED || $msg_type == MSG_DISINVITE )
{
++ $want_update ;
}
break ;
case 'no' :
break ;
}
2010-08-02 16:15:05 +02:00
//error_log(__METHOD__."(userid=$userid,,msg_type=$msg_type,...) msg_is_response=$msg_is_response, want_update=$want_update");
2005-11-09 00:15:14 +01:00
return $want_update > 0 ;
}
/**
* sends update - messages to certain participants of an event
*
* @ param int $msg_type type of the notification : MSG_ADDED , MSG_MODIFIED , MSG_ACCEPTED , ...
* @ param array $to_notify numerical user - ids as keys ( ! ) ( value is not used )
* @ param array $old_event Event before the change
2005-11-23 15:21:20 +01:00
* @ param array $new_event = null Event after the change
* @ param int $user = 0 User who started the notify , default current user
2007-11-22 09:29:16 +01:00
* @ return bool true / false
2005-11-09 00:15:14 +01:00
*/
2005-11-23 15:21:20 +01:00
function send_update ( $msg_type , $to_notify , $old_event , $new_event = null , $user = 0 )
2005-11-09 00:15:14 +01:00
{
2010-08-02 16:15:05 +02:00
//error_log(__METHOD__."($msg_type,".array2string($to_notify).",...)");
2005-11-09 00:15:14 +01:00
if ( ! is_array ( $to_notify ))
{
$to_notify = array ();
}
$disinvited = $msg_type == MSG_DISINVITE ? array_keys ( $to_notify ) : array ();
$owner = $old_event ? $old_event [ 'owner' ] : $new_event [ 'owner' ];
if ( $owner && ! isset ( $to_notify [ $owner ]) && $msg_type != MSG_ALARM )
{
$to_notify [ $owner ] = 'owner' ; // always include the event-owner
}
$version = $GLOBALS [ 'egw_info' ][ 'apps' ][ 'calendar' ][ 'version' ];
2006-10-16 21:50:22 +02:00
// ignore events in the past (give a tolerance of 10 seconds for the script)
if ( $old_event != False && $this -> date2ts ( $old_event [ 'start' ]) < ( $this -> now_su - 10 ))
2005-11-09 00:15:14 +01:00
{
return False ;
}
$temp_user = $GLOBALS [ 'egw_info' ][ 'user' ]; // save user-date of the enviroment to restore it after
if ( ! $user )
{
$user = $temp_user [ 'account_id' ];
}
if ( $GLOBALS [ 'egw' ] -> preferences -> account_id != $user )
{
2009-11-07 13:09:12 +01:00
$GLOBALS [ 'egw' ] -> preferences -> __construct ( $user );
2005-11-09 00:15:14 +01:00
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ] = $GLOBALS [ 'egw' ] -> preferences -> read_repository ();
}
2010-11-01 20:17:57 +01:00
$senderid = $this -> user ;
2005-11-09 00:15:14 +01:00
$event = $msg_type == MSG_ADDED || $msg_type == MSG_MODIFIED ? $new_event : $old_event ;
switch ( $msg_type )
{
case MSG_DELETED :
$action = lang ( 'Canceled' );
$msg = 'Canceled' ;
$msgtype = '"calendar";' ;
2007-08-27 19:17:36 +02:00
$method = 'CANCEL' ;
2005-11-09 00:15:14 +01:00
break ;
case MSG_MODIFIED :
$action = lang ( 'Modified' );
$msg = 'Modified' ;
$msgtype = '"calendar"; Version="' . $version . '"; Id="' . $new_event [ 'id' ] . '"' ;
2007-08-27 19:17:36 +02:00
$method = 'REQUEST' ;
2005-11-09 00:15:14 +01:00
break ;
case MSG_DISINVITE :
$action = lang ( 'Disinvited' );
$msg = 'Disinvited' ;
$msgtype = '"calendar";' ;
2007-08-27 19:17:36 +02:00
$method = 'CANCEL' ;
2005-11-09 00:15:14 +01:00
break ;
case MSG_ADDED :
$action = lang ( 'Added' );
$msg = 'Added' ;
$msgtype = '"calendar"; Version="' . $version . '"; Id="' . $new_event [ 'id' ] . '"' ;
2007-08-27 19:17:36 +02:00
$method = 'REQUEST' ;
2005-11-09 00:15:14 +01:00
break ;
case MSG_REJECTED :
$action = lang ( 'Rejected' );
$msg = 'Response' ;
$msgtype = '"calendar";' ;
2007-08-27 19:17:36 +02:00
$method = 'REPLY' ;
2005-11-09 00:15:14 +01:00
break ;
case MSG_TENTATIVE :
$action = lang ( 'Tentative' );
$msg = 'Response' ;
$msgtype = '"calendar";' ;
2007-08-27 19:17:36 +02:00
$method = 'REPLY' ;
2005-11-09 00:15:14 +01:00
break ;
case MSG_ACCEPTED :
$action = lang ( 'Accepted' );
$msg = 'Response' ;
$msgtype = '"calendar";' ;
2007-08-27 19:17:36 +02:00
$method = 'REPLY' ;
2005-11-09 00:15:14 +01:00
break ;
2010-02-17 14:29:28 +01:00
case MSG_DELEGATED :
$action = lang ( 'Delegated' );
$msg = 'Response' ;
$msgtype = '"calendar";' ;
$method = 'REPLY' ;
break ;
2005-11-09 00:15:14 +01:00
case MSG_ALARM :
$action = lang ( 'Alarm' );
$msg = 'Alarm' ;
$msgtype = '"calendar";' ;
2007-08-27 19:17:36 +02:00
$method = 'PUBLISH' ; // duno if thats right
2005-11-09 00:15:14 +01:00
break ;
default :
2007-08-27 19:17:36 +02:00
$method = 'PUBLISH' ;
2005-11-09 00:15:14 +01:00
}
$notify_msg = $this -> cal_prefs [ 'notify' . $msg ];
if ( empty ( $notify_msg ))
{
$notify_msg = $this -> cal_prefs [ 'notifyAdded' ]; // use a default
}
$details = $this -> _get_event_details ( $event , $action , $event_arr , $disinvited );
2008-05-08 00:12:25 +02:00
2005-11-23 15:21:20 +01:00
// add all group-members to the notification, unless they are already participants
foreach ( $to_notify as $userid => $statusid )
{
if ( is_numeric ( $userid ) && $GLOBALS [ 'egw' ] -> accounts -> get_type ( $userid ) == 'g' &&
( $members = $GLOBALS [ 'egw' ] -> accounts -> member ( $userid )))
{
foreach ( $members as $member )
{
$member = $member [ 'account_id' ];
if ( ! isset ( $to_notify [ $member ]))
{
$to_notify [ $member ] = 'G' ; // Group-invitation
}
}
}
}
2010-03-03 12:59:55 +01:00
$user_prefs = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ];
2010-11-01 20:17:57 +01:00
$startdate = new egw_time ( $event [ 'start' ]);
2010-11-16 14:39:19 +01:00
$enddate = new egw_time ( $event [ 'end' ]);
2010-11-01 20:17:57 +01:00
$modified = new egw_time ( $event [ 'modified' ]);
if ( $old_event != False ) $olddate = new egw_time ( $old_event [ 'start' ]);
2005-11-09 00:15:14 +01:00
foreach ( $to_notify as $userid => $statusid )
{
2011-05-27 22:32:46 +02:00
calendar_so :: split_status ( $statusid , $quantity , $role );
if ( $this -> debug > 0 ) error_log ( __METHOD__ . " trying to notify $userid , with $statusid ( $role ) " );
if ( ! is_numeric ( $userid ) && $userid [ 0 ] == 'e' )
{
if ( $role != 'CHAIR' || $msg_type == MSG_ALARM ) continue ; // only notify "external" chairs, but not for alarms
$userid = substr ( $userid , 1 );
}
elseif ( ! is_numeric ( $userid ))
2006-03-19 13:41:01 +01:00
{
$res_info = $this -> resource_info ( $userid );
$userid = $res_info [ 'responsible' ];
2008-05-08 00:12:25 +02:00
if ( ! isset ( $userid )) continue ;
2006-03-19 13:41:01 +01:00
}
2005-11-09 00:15:14 +01:00
if ( $statusid == 'R' || $GLOBALS [ 'egw' ] -> accounts -> get_type ( $userid ) == 'g' )
{
continue ; // dont notify rejected participants or groups
}
2010-03-03 12:59:55 +01:00
2010-03-07 00:06:43 +01:00
if ( $userid != $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] ||
( $userid == $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] &&
$user_prefs [ 'calendar' ][ 'receive_own_updates' ] == 1 ) ||
2010-03-03 13:14:10 +01:00
$msg_type == MSG_ALARM )
2005-11-09 00:15:14 +01:00
{
2011-05-27 22:32:46 +02:00
if ( is_numeric ( $userid ))
{
$preferences = CreateObject ( 'phpgwapi.preferences' , $userid );
$part_prefs = $preferences -> read_repository ();
2005-11-09 00:15:14 +01:00
2011-05-27 22:32:46 +02:00
if ( ! $this -> update_requested ( $userid , $part_prefs , $msg_type , $old_event , $new_event ))
{
continue ;
}
$GLOBALS [ 'egw' ] -> accounts -> get_account_name ( $userid , $lid , $details [ 'to-firstname' ], $details [ 'to-lastname' ]);
$details [ 'to-fullname' ] = common :: display_fullname ( '' , $details [ 'to-firstname' ], $details [ 'to-lastname' ]);
}
else // external email address: use current users preferences, plus some hardcoded settings (eg. ical notification)
2005-11-09 00:15:14 +01:00
{
2011-05-27 22:32:46 +02:00
$details [ 'to-fullname' ] = $userid ;
$part_prefs = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ];
$part_prefs [ 'calendar' ][ 'update_format' ] = 'ical' ; // use ical format
2005-11-09 00:15:14 +01:00
}
// event is in user-time of current user, now we need to calculate the tz-difference to the notified user and take it into account
2010-11-01 20:17:57 +01:00
if ( ! isset ( $part_prefs [ 'common' ][ 'tz' ])) $part_prefs [ 'common' ][ 'tz' ] = $GLOBALS [ 'egw_info' ][ 'server' ][ 'server_timezone' ];
$timezone = new DateTimeZone ( $part_prefs [ 'common' ][ 'tz' ]);
$timeformat = $part_prefs [ 'common' ][ 'timeformat' ];
switch ( $timeformat )
{
case '24' :
$timeformat = 'H:i' ;
break ;
case '12' :
$timeformat = 'h:i a' ;
break ;
}
$timeformat = $part_prefs [ 'common' ][ 'dateformat' ] . ', ' . $timeformat ;
2010-11-11 09:51:13 +01:00
2010-11-01 20:17:57 +01:00
$startdate -> setTimezone ( $timezone );
$details [ 'startdate' ] = $startdate -> format ( $timeformat );
2010-11-11 09:51:13 +01:00
2010-11-01 20:17:57 +01:00
$enddate -> setTimezone ( $timezone );
$details [ 'enddate' ] = $enddate -> format ( $timeformat );
2010-11-11 09:51:13 +01:00
2010-11-01 20:17:57 +01:00
$modified -> setTimezone ( $timezone );
$details [ 'updated' ] = $modified -> format ( $timeformat ) . ', ' . common :: grab_owner_name ( $event [ 'modifier' ]);
2010-11-11 09:51:13 +01:00
2010-11-01 20:17:57 +01:00
if ( $old_event != False )
{
$olddate -> setTimezone ( $timezone );
$details [ 'olddate' ] = $olddate -> format ( $timeformat );
}
2010-11-11 09:51:13 +01:00
2005-11-09 00:15:14 +01:00
list ( $subject , $body ) = explode ( " \n " , $GLOBALS [ 'egw' ] -> preferences -> parse_notify ( $notify_msg , $details ), 2 );
switch ( $part_prefs [ 'calendar' ][ 'update_format' ])
{
2009-10-18 14:46:18 +02:00
case 'ical' :
2011-05-27 22:32:46 +02:00
if ( is_null ( $ics ))
2009-10-18 14:46:18 +02:00
{
2011-05-27 22:32:46 +02:00
$calendar_ical = new calendar_ical ();
$calendar_ical -> setSupportedFields ( 'full' ); // full iCal fields+event TZ
$ics = $calendar_ical -> exportVCal ( array ( $event ), '2.0' , $method );
unset ( $calendar_ical );
2009-10-18 14:46:18 +02:00
}
2011-05-27 22:32:46 +02:00
$attachment = array (
'string' => $ics ,
'filename' => 'cal.ics' ,
'encoding' => '8bit' ,
'type' => 'text/calendar; method=' . $method ,
);
2009-10-18 14:46:18 +02:00
// fall through
case 'extended' :
2005-11-09 00:15:14 +01:00
$body .= " \n \n " . lang ( 'Event Details follow' ) . " : \n " ;
foreach ( $event_arr as $key => $val )
{
2011-05-27 22:32:46 +02:00
if ( ! empty ( $details [ $key ]))
{
2007-11-22 09:29:16 +01:00
switch ( $key ){
case 'access' :
case 'priority' :
case 'link' :
break ;
default :
$body .= sprintf ( " %-20s %s \n " , $val [ 'field' ] . ':' , $details [ $key ]);
break ;
}
2005-11-09 00:15:14 +01:00
}
}
break ;
}
2007-11-22 09:29:16 +01:00
// send via notification_app
2011-05-27 22:32:46 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'apps' ][ 'notifications' ][ 'enabled' ])
{
2007-11-29 19:31:24 +01:00
try {
2008-01-30 19:58:00 +01:00
$notification = new notifications ();
2007-11-29 19:31:24 +01:00
$notification -> set_receivers ( array ( $userid ));
$notification -> set_message ( $body );
$notification -> set_sender ( $senderid );
$notification -> set_subject ( $subject );
$notification -> set_links ( array ( $details [ 'link_arr' ]));
if ( is_array ( $attachment )) { $notification -> set_attachments ( array ( $attachment )); }
$notification -> send ();
}
catch ( Exception $exception ) {
2008-12-09 15:02:10 +01:00
error_log ( __METHOD__ . ' error while notifying user ' . $userid . ':' . $exception -> getMessage ());
2007-11-29 19:31:24 +01:00
continue ;
}
2011-05-27 22:32:46 +02:00
}
else
{
2008-12-09 15:02:10 +01:00
error_log ( __METHOD__ . ' cannot send any notifications because notifications is not installed' );
2006-09-25 12:29:03 +02:00
}
2005-11-09 00:15:14 +01:00
}
}
2009-11-07 13:09:12 +01:00
// restore the enviroment (preferences->read_repository() sets the timezone!)
2005-11-09 00:15:14 +01:00
$GLOBALS [ 'egw_info' ][ 'user' ] = $temp_user ;
2010-11-01 20:17:57 +01:00
if ( $GLOBALS [ 'egw' ] -> preferences -> account_id != $temp_user [ 'account_id' ])
2009-11-07 13:09:12 +01:00
{
$GLOBALS [ 'egw' ] -> preferences -> __construct ( $temp_user [ 'account_id' ]);
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ] = $GLOBALS [ 'egw' ] -> preferences -> read_repository ();
//echo "<p>".__METHOD__."() restored enviroment of #$temp_user[account_id] $temp_user[account_fullname]: tz={$GLOBALS['egw_info']['user']['preferences']['common']['tz']}</p>\n";
}
2007-11-22 09:29:16 +01:00
return true ;
2005-11-09 00:15:14 +01:00
}
function get_update_message ( $event , $added )
{
$details = $this -> _get_event_details ( $event , $added ? lang ( 'Added' ) : lang ( 'Modified' ), $nul );
$notify_msg = $this -> cal_prefs [ $added || empty ( $this -> cal_prefs [ 'notifyModified' ]) ? 'notifyAdded' : 'notifyModified' ];
return explode ( " \n " , $GLOBALS [ 'egw' ] -> preferences -> parse_notify ( $notify_msg , $details ), 2 );
}
2006-03-07 20:18:15 +01:00
/**
* Function called via async service , when an alarm is to be send
*
* @ param array $alarm array with keys owner , cal_id , all
2008-05-08 00:12:25 +02:00
* @ return boolean
2006-03-07 20:18:15 +01:00
*/
2005-11-09 00:15:14 +01:00
function send_alarm ( $alarm )
{
//echo "<p>bocalendar::send_alarm("; print_r($alarm); echo ")</p>\n";
$GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] = $this -> owner = $alarm [ 'owner' ];
2009-11-04 16:00:08 +01:00
$event_time_user = egw_time :: server2user ( $alarm [ 'time' ] + $alarm [ 'offset' ]); // alarm[time] is in server-time, read requires user-time
2006-03-07 20:18:15 +01:00
if ( ! $alarm [ 'owner' ] || ! $alarm [ 'cal_id' ] || ! ( $event = $this -> read ( $alarm [ 'cal_id' ], $event_time_user )))
2005-11-09 00:15:14 +01:00
{
return False ; // event not found
}
if ( $alarm [ 'all' ])
{
$to_notify = $event [ 'participants' ];
}
elseif ( $this -> check_perms ( EGW_ACL_READ , $event )) // checks agains $this->owner set to $alarm[owner]
{
$to_notify [ $alarm [ 'owner' ]] = 'A' ;
}
else
{
return False ; // no rights
}
2006-03-07 20:18:15 +01:00
$ret = $this -> send_update ( MSG_ALARM , $to_notify , $event , False , $alarm [ 'owner' ]);
// create a new alarm for recuring events for the next event, if one exists
if ( $event [ 'recur_type' ] && ( $event = $this -> read ( $alarm [ 'cal_id' ], $event_time_user + 1 )))
{
$alarm [ 'time' ] = $this -> date2ts ( $event [ 'start' ]) - $alarm [ 'offset' ];
$this -> save_alarm ( $alarm [ 'cal_id' ], $alarm );
}
return $ret ;
2005-11-09 00:15:14 +01:00
}
2005-06-14 23:54:17 +02:00
/**
2009-08-04 19:14:16 +02:00
* saves an event to the database , does NOT do any notifications , see calendar_boupdate :: update for that
2005-06-14 23:54:17 +02:00
*
* This methode converts from user to server time and handles the insertion of users and dates of repeating events
*
* @ param array $event
2009-07-15 22:35:56 +02:00
* @ param boolean $ignore_acl = false should we ignore the acl
2009-10-25 19:20:58 +01:00
* @ param boolean $updateTS = true update the content history of the event
2009-08-04 19:14:16 +02:00
* @ return int | boolean $cal_id > 0 or false on error ( eg . permission denied )
2005-06-14 23:54:17 +02:00
*/
2009-10-25 19:20:58 +01:00
function save ( $event , $ignore_acl = false , $updateTS = true )
2005-06-14 23:54:17 +02:00
{
2011-04-07 10:01:48 +02:00
//error_log(__METHOD__.'('.array2string($event).", $ignore_acl, $updateTS)");
2010-11-11 09:51:13 +01:00
2005-11-09 00:15:14 +01:00
// check if user has the permission to update / create the event
2009-07-15 22:35:56 +02:00
if ( ! $ignore_acl && ( $event [ 'id' ] && ! $this -> check_perms ( EGW_ACL_EDIT , $event [ 'id' ]) ||
2007-01-20 08:11:06 +01:00
! $event [ 'id' ] && ! $this -> check_perms ( EGW_ACL_EDIT , 0 , $event [ 'owner' ]) &&
2009-07-15 22:35:56 +02:00
! $this -> check_perms ( EGW_ACL_ADD , 0 , $event [ 'owner' ])))
2005-11-09 00:15:14 +01:00
{
return false ;
}
2008-01-17 16:39:22 +01:00
2010-02-17 14:29:28 +01:00
if ( $event [ 'id' ])
{
// invalidate the read-cache if it contains the event we store now
if ( $event [ 'id' ] == self :: $cached_event [ 'id' ]) self :: $cached_event = array ();
$old_event = $this -> read ( $event [ 'id' ], $event [ 'recurrence' ], false , 'server' );
}
else
{
$old_event = null ;
}
2005-11-09 00:15:14 +01:00
2010-04-23 15:52:45 +02:00
if ( ! isset ( $event [ 'whole_day' ])) $event [ 'whole_day' ] = $this -> isWholeDay ( $event );
2010-05-01 18:25:10 +02:00
$save_event = $event ;
2010-04-23 15:52:45 +02:00
if ( $event [ 'whole_day' ])
2010-04-23 08:15:18 +02:00
{
2010-06-23 16:52:55 +02:00
if ( ! empty ( $event [ 'start' ]))
{
$time = new egw_time ( $event [ 'start' ], egw_time :: $user_timezone );
$time =& $this -> so -> startOfDay ( $time );
$event [ 'start' ] = egw_time :: to ( $time , 'ts' );
$save_event [ 'start' ] = $time ;
}
if ( ! empty ( $event [ 'end' ]))
{
$time = new egw_time ( $event [ 'end' ], egw_time :: $user_timezone );
$time =& $this -> so -> startOfDay ( $time );
$time -> setTime ( 23 , 59 , 59 );
$event [ 'end' ] = egw_time :: to ( $time , 'ts' );
}
2010-05-01 18:25:10 +02:00
if ( ! empty ( $event [ 'recurrence' ]))
{
$time = new egw_time ( $event [ 'recurrence' ], egw_time :: $user_timezone );
$time =& $this -> so -> startOfDay ( $time );
$event [ 'recurrence' ] = egw_time :: to ( $time , 'ts' );
}
if ( ! empty ( $event [ 'recur_enddate' ]))
{
$time = new egw_time ( $event [ 'recur_enddate' ], egw_time :: $user_timezone );
$time =& $this -> so -> startOfDay ( $time );
$event [ 'recur_enddate' ] = egw_time :: to ( $time , 'ts' );
2010-05-03 14:18:57 +02:00
$time -> setUser ();
$save_event [ 'recur_enddate' ] = egw_time :: to ( $time , 'ts' );
2010-05-01 18:25:10 +02:00
}
2010-04-23 08:15:18 +02:00
$timestamps = array ( 'modified' , 'created' );
2010-05-01 18:25:10 +02:00
// all-day events are handled in server time
$event [ 'tzid' ] = $save_event [ 'tzid' ] = egw_time :: $server_timezone -> getName ();
2010-04-23 08:15:18 +02:00
}
else
{
$timestamps = array ( 'start' , 'end' , 'modified' , 'created' , 'recur_enddate' , 'recurrence' );
}
2005-11-09 00:15:14 +01:00
// we run all dates through date2ts, to adjust to server-time and the possible date-formats
2010-04-23 08:15:18 +02:00
foreach ( $timestamps as $ts )
2005-06-14 23:54:17 +02:00
{
// we convert here from user-time to timestamps in server-time!
if ( isset ( $event [ $ts ])) $event [ $ts ] = $event [ $ts ] ? $this -> date2ts ( $event [ $ts ], true ) : 0 ;
}
2009-11-04 16:00:08 +01:00
// convert tzid name to integer tz_id, of set user default
if ( empty ( $event [ 'tzid' ]) || ! ( $event [ 'tz_id' ] = calendar_timezones :: tz2id ( $event [ 'tzid' ])))
{
$event [ 'tz_id' ] = calendar_timezones :: tz2id ( $event [ 'tzid' ] = egw_time :: $user_timezone -> getName ());
}
2005-11-09 00:15:14 +01:00
// same with the recur exceptions
if ( isset ( $event [ 'recur_exception' ]) && is_array ( $event [ 'recur_exception' ]))
{
foreach ( $event [ 'recur_exception' ] as $n => $date )
{
2010-04-23 08:15:18 +02:00
if ( $event [ 'whole_day' ])
{
$time = new egw_time ( $date , egw_time :: $user_timezone );
$time =& $this -> so -> startOfDay ( $time );
$date = egw_time :: to ( $time , 'ts' );
}
else
{
$event [ 'recur_exception' ][ $n ] = $this -> date2ts ( $date , true );
}
2005-11-09 00:15:14 +01:00
}
}
// same with the alarms
if ( isset ( $event [ 'alarm' ]) && is_array ( $event [ 'alarm' ]))
{
foreach ( $event [ 'alarm' ] as $id => $alarm )
{
2011-03-07 14:49:08 +01:00
// recalculate alarms to also cope with moved events (beside server time adjustment)
$event [ 'alarm' ][ $id ][ 'time' ] = $event [ 'start' ] - $alarm [ 'offset' ];
}
}
// update all existing alarm times, in case alarm got moved and alarms are not include in $event
if ( $old_event && is_array ( $old_event [ 'alarm' ]))
{
foreach ( $old_event [ 'alarm' ] as $alarm )
{
$alarm [ 'time' ] = $event [ 'start' ] - $alarm [ 'offset' ];
$this -> so -> save_alarm ( $event [ 'id' ], $alarm , $this -> now );
2005-11-09 00:15:14 +01:00
}
}
2011-04-07 10:01:48 +02:00
// always update modification time (ctag depends on it!)
$event [ 'modified' ] = $this -> now ;
$event [ 'modifier' ] = $this -> user ;
2010-09-12 22:14:46 +02:00
if ( empty ( $event [ 'id' ]) && ( ! isset ( $event [ 'created' ]) || $event [ 'created' ] > $this -> now ))
2010-09-11 20:08:48 +02:00
{
$event [ 'created' ] = $this -> now ;
$event [ 'creator' ] = $this -> user ;
}
2009-07-15 22:35:56 +02:00
$set_recurrences = false ;
2009-08-10 11:24:39 +02:00
$set_recurrences_start = 0 ;
if (( $cal_id = $this -> so -> save ( $event , $set_recurrences , $set_recurrences_start , 0 , $event [ 'etag' ])) && $set_recurrences && $event [ 'recur_type' ] != MCAL_RECUR_NONE )
2005-06-14 23:54:17 +02:00
{
$save_event [ 'id' ] = $cal_id ;
2009-08-10 11:24:39 +02:00
// unset participants to enforce the default stati for all added recurrences
unset ( $save_event [ 'participants' ]);
$this -> set_recurrences ( $save_event , $set_recurrences_start );
2005-06-14 23:54:17 +02:00
}
2010-09-11 20:08:48 +02:00
if ( $updateTS ) $GLOBALS [ 'egw' ] -> contenthistory -> updateTimeStamp ( 'calendar' , $cal_id , $event [ 'id' ] ? 'modify' : 'add' , $this -> now );
2005-06-14 23:54:17 +02:00
2010-02-12 21:18:45 +01:00
// Update history
$tracking = new calendar_tracking ( $this );
2010-02-19 18:42:02 +01:00
$event [ 'category' ] = implode ( ',' , $this -> get_categories ( $event [ 'category' ]));
$old_event [ 'category' ] = implode ( ',' , $this -> get_categories ( $old_event [ 'category' ]));
2011-05-11 17:09:16 +02:00
if ( empty ( $event [ 'id' ]) && ! empty ( $cal_id )) $event [ 'id' ] = $cal_id ;
2010-02-12 21:18:45 +01:00
$tracking -> track ( $event , $old_event );
2005-06-14 23:54:17 +02:00
return $cal_id ;
}
2008-05-08 00:12:25 +02:00
2006-12-22 20:51:56 +01:00
/**
* Check if the current user has the necessary ACL rights to change the status of $uid
2008-05-08 00:12:25 +02:00
*
2006-12-22 20:51:56 +01:00
* For contacts we use edit rights of the owner of the event ( aka . edit rights of the event ) .
*
2009-08-04 19:14:16 +02:00
* @ param int | string $uid account_id or 1 - char type - identifer plus id ( eg . c15 for addressbook entry #15)
* @ param array | int $event event array or id of the event
2006-12-22 20:51:56 +01:00
* @ return boolean
*/
function check_status_perms ( $uid , $event )
{
2009-12-07 14:31:51 +01:00
if ( $uid [ 0 ] == 'c' || $uid [ 0 ] == 'e' ) // for contact we use the owner of the event
2006-12-22 20:51:56 +01:00
{
if ( ! is_array ( $event ) && ! ( $event = $this -> read ( $event ))) return false ;
return $this -> check_perms ( EGW_ACL_EDIT , 0 , $event [ 'owner' ]);
}
2009-12-07 14:31:51 +01:00
// check if we have a category acl for the event or not (null)
$access = $this -> check_cat_acl ( self :: CAT_ACL_STATUS , $event );
if ( ! is_null ( $access ))
2009-12-03 19:24:19 +01:00
{
2009-12-07 14:31:51 +01:00
return $access ;
2009-12-03 19:24:19 +01:00
}
2009-12-07 14:31:51 +01:00
// no access or denied access because of category acl --> regular check
2006-12-22 20:51:56 +01:00
if ( ! is_numeric ( $uid )) // this is eg. for resources (r123)
{
$resource = $this -> resource_info ( $uid );
return EGW_ACL_EDIT & $resource [ 'rights' ];
}
// regular user and groups
return $this -> check_perms ( EGW_ACL_EDIT , 0 , $uid );
}
2009-12-03 19:24:19 +01:00
/**
* Check if current user has a certain right on the categories of an event
*
* Not having the given right for a single category , means not having it !
*
* @ param int $right self :: CAT_ACL_ { ADD | STATUS }
* @ param int | array $event
* @ return boolean true if use has the right , false if not
2009-12-07 14:31:51 +01:00
* @ return boolean false = access denied because of cat acl , true access granted because of cat acl ,
* null = cat has no acl
2009-12-03 19:24:19 +01:00
*/
function check_cat_acl ( $right , $event )
{
if ( ! is_array ( $event )) $event = $this -> read ( $event );
2009-12-07 14:31:51 +01:00
$ret = null ;
2009-12-03 19:24:19 +01:00
if ( $event [ 'category' ])
{
foreach ( is_array ( $event [ 'category' ]) ? $event [ 'category' ] : explode ( ',' , $event [ 'category' ]) as $cat_id )
{
2009-12-07 14:31:51 +01:00
$access = self :: has_cat_right ( $right , $cat_id , $this -> user );
if ( $access === true )
2009-12-03 19:24:19 +01:00
{
2009-12-07 14:31:51 +01:00
$ret = true ;
2009-12-03 19:24:19 +01:00
break ;
}
2009-12-07 14:31:51 +01:00
if ( $access === false )
{
$ret = false ; // cat denies access --> check further cats
}
2009-12-03 19:24:19 +01:00
}
}
//echo "<p>".__METHOD__."($event[id]: $event[title], $right) = ".array2string($ret)."</p>\n";
return $ret ;
}
/**
* Array with $cat_id => $rights pairs for current user ( no entry means , cat is not limited by ACL ! )
*
* @ var array
*/
private static $cat_rights_cache ;
/**
* Get rights for a given category id
*
* @ param int $cat_id = null null to return array with all cats
* @ return array with account_id => right pairs
*/
public static function get_cat_rights ( $cat_id = null )
{
if ( ! isset ( self :: $cat_rights_cache ))
{
self :: $cat_rights_cache = egw_cache :: getSession ( 'calendar' , 'cat_rights' ,
array ( $GLOBALS [ 'egw' ] -> acl , 'get_location_grants' ), array ( 'L%' , 'calendar' ));
}
//echo "<p>".__METHOD__."($cat_id) = ".array2string($cat_id ? self::$cat_rights_cache['L'.$cat_id] : self::$cat_rights_cache)."</p>\n";
return $cat_id ? self :: $cat_rights_cache [ 'L' . $cat_id ] : self :: $cat_rights_cache ;
}
/**
* Set rights for a given single category and user
*
* @ param int $cat_id
* @ param int $user
* @ param int $rights self :: CAT_ACL_ { ADD | STATUS } or ' ed together
*/
public static function set_cat_rights ( $cat_id , $user , $rights )
{
//echo "<p>".__METHOD__."($cat_id,$user,$rights)</p>\n";
if ( ! isset ( self :: $cat_rights_cache )) self :: get_cat_rights ( $cat_id );
if (( int ) $rights != ( int ) self :: $cat_rights_cache [ 'L' . $cat_id ][ $user ])
{
if ( $rights )
{
self :: $cat_rights_cache [ 'L' . $cat_id ][ $user ] = $rights ;
$GLOBALS [ 'egw' ] -> acl -> add_repository ( 'calendar' , 'L' . $cat_id , $user , $rights );
}
else
{
unset ( self :: $cat_rights_cache [ 'L' . $cat_id ][ $user ]);
if ( ! self :: $cat_rights_cache [ 'L' . $cat_id ]) unset ( self :: $cat_rights_cache [ 'L' . $cat_id ]);
$GLOBALS [ 'egw' ] -> acl -> delete_repository ( 'calendar' , 'L' . $cat_id , $user );
}
egw_cache :: setSession ( 'calendar' , 'cat_rights' , self :: $cat_rights_cache );
}
}
/**
* Check if current user has a given right on a category ( if it ' s restricted ! )
*
* @ param int $cat_id
2009-12-07 14:31:51 +01:00
* @ return boolean false = access denied because of cat acl , true access granted because of cat acl ,
* null = cat has no acl
2009-12-03 19:24:19 +01:00
*/
public static function has_cat_right ( $right , $cat_id , $user )
{
static $cache ;
2009-12-07 14:31:51 +01:00
if ( ! isset ( $cache [ $cat_id ]))
2009-12-03 19:24:19 +01:00
{
$all = $own = 0 ;
$cat_rights = self :: get_cat_rights ( $cat_id );
if ( ! is_null ( $cat_rights ))
{
static $memberships ;
if ( is_null ( $memberships ))
{
$memberships = $GLOBALS [ 'egw' ] -> accounts -> memberships ( $user , true );
$memberships [] = $user ;
}
foreach ( $cat_rights as $uid => $value )
{
$all |= $value ;
if ( in_array ( $uid , $memberships )) $own |= $value ;
}
}
foreach ( array ( self :: CAT_ACL_ADD , self :: CAT_ACL_STATUS ) as $mask )
{
2009-12-07 14:31:51 +01:00
$cache [ $cat_id ][ $mask ] = ! ( $all & $mask ) ? null : !! ( $own & $mask );
2009-12-03 19:24:19 +01:00
}
}
//echo "<p>".__METHOD__."($right,$cat_id) all=$all, own=$own returning ".array2string($cache[$cat_id][$right])."</p>\n";
return $cache [ $cat_id ][ $right ];
}
2005-06-14 23:54:17 +02:00
/**
2005-11-09 00:15:14 +01:00
* set the status of one participant for a given recurrence or for all recurrences since now ( includes recur_date = 0 )
2005-06-14 23:54:17 +02:00
*
2009-08-04 19:14:16 +02:00
* @ param int | array $event event - array or id of the event
* @ param string | int $uid account_id or 1 - char type - identifer plus id ( eg . c15 for addressbook entry #15)
* @ param int | char $status numeric status ( defines ) or 1 - char code : 'R' , 'U' , 'T' or 'A'
2005-11-09 00:15:14 +01:00
* @ param int $recur_date = 0 date to change , or 0 = all since now
2009-07-15 22:35:56 +02:00
* @ param boolean $ignore_acl = false do not check the permisions for the $uid , if true
2009-10-25 19:20:58 +01:00
* @ param boolean $updateTS = true update the content history of the event
2005-11-09 00:15:14 +01:00
* @ return int number of changed recurrences
*/
2011-03-09 18:26:20 +01:00
function set_status ( $event , $uid , $status , $recur_date = 0 , $ignore_acl = false , $updateTS = true , $skip_notification = false )
2005-11-09 00:15:14 +01:00
{
2006-12-22 20:51:56 +01:00
$cal_id = is_array ( $event ) ? $event [ 'id' ] : $event ;
2009-08-04 19:14:16 +02:00
//echo "<p>calendar_boupdate::set_status($cal_id,$uid,$status,$recur_date)</p>\n";
2009-07-15 22:35:56 +02:00
if ( ! $cal_id || ( ! $ignore_acl && ! $this -> check_status_perms ( $uid , $event )))
2005-11-09 00:15:14 +01:00
{
return false ;
}
2009-11-26 19:36:19 +01:00
calendar_so :: split_status ( $status , $quantity , $role );
2010-01-29 22:42:54 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
" ( $cal_id , $uid , $status , $recur_date ) \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-02-17 14:29:28 +01:00
$old_event = $this -> read ( $cal_id , $recur_date , false , 'server' );
2010-03-07 00:06:43 +01:00
if (( $Ok = $this -> so -> set_status ( $cal_id , is_numeric ( $uid ) ? 'u' : $uid [ 0 ],
is_numeric ( $uid ) ? $uid : substr ( $uid , 1 ), $status ,
$recur_date ? $this -> date2ts ( $recur_date , true ) : 0 , $role )))
2005-11-09 00:15:14 +01:00
{
2010-09-11 20:08:48 +02:00
if ( $updateTS )
{
2010-09-12 22:14:46 +02:00
$GLOBALS [ 'egw' ] -> contenthistory -> updateTimeStamp ( 'calendar' , $cal_id , 'modify' , $this -> now );
2010-09-11 20:08:48 +02:00
}
2010-11-11 09:51:13 +01:00
2005-11-09 00:15:14 +01:00
static $status2msg = array (
'R' => MSG_REJECTED ,
'T' => MSG_TENTATIVE ,
'A' => MSG_ACCEPTED ,
2010-02-17 14:29:28 +01:00
'D' => MSG_DELEGATED ,
2005-11-09 00:15:14 +01:00
);
2011-03-09 18:26:20 +01:00
if ( isset ( $status2msg [ $status ]) && ! $skip_notification )
2005-11-09 00:15:14 +01:00
{
2006-12-22 20:51:56 +01:00
if ( ! is_array ( $event )) $event = $this -> read ( $cal_id );
2007-02-07 14:51:07 +01:00
if ( isset ( $recur_date )) $event = $this -> read ( $event [ 'id' ], $recur_date ); //re-read the actually edited recurring event
2005-11-09 00:15:14 +01:00
$this -> send_update ( $status2msg [ $status ], $event [ 'participants' ], $event );
}
2010-02-12 21:18:45 +01:00
// Update history
2010-02-17 14:29:28 +01:00
$event = $this -> read ( $cal_id , $recur_date , false , 'server' );
2010-02-12 21:18:45 +01:00
$tracking = new calendar_tracking ( $this );
$tracking -> track ( $event , $old_event );
2005-11-09 00:15:14 +01:00
}
2005-11-23 15:21:20 +01:00
return $Ok ;
2005-11-09 00:15:14 +01:00
}
2010-02-26 13:37:07 +01:00
/**
* update the status of all participant for a given recurrence or for all recurrences since now ( includes recur_date = 0 )
*
* @ param array $new_event event - array with the new stati
* @ param array $old_event event - array with the old stati
* @ param int $recur_date = 0 date to change , or 0 = all since now
*/
function update_status ( $new_event , $old_event , $recur_date = 0 )
{
if ( ! isset ( $new_event [ 'participants' ])) return ;
// check the old list against the new list
foreach ( $old_event [ 'participants' ] as $userid => $status )
{
if ( ! isset ( $new_event [ 'participants' ][ $userid ])){
// Attendee will be deleted this way
$new_event [ 'participants' ][ $userid ] = 'G' ;
}
elseif ( $new_event [ 'participants' ][ $userid ] == $status )
{
// Same status -- nothing to do.
unset ( $new_event [ 'participants' ][ $userid ]);
}
}
// write the changes
foreach ( $new_event [ 'participants' ] as $userid => $status )
{
$this -> set_status ( $old_event , $userid , $status , $recur_date , true , false );
}
}
2005-11-09 00:15:14 +01:00
/**
* deletes an event
*
* @ param int $cal_id id of the event to delete
* @ param int $recur_date = 0 if a single event from a series should be deleted , its date
2009-08-06 13:29:05 +02:00
* @ param boolean $ignore_acl = false true for no ACL check , default do ACL check
2005-11-09 00:15:14 +01:00
* @ return boolean true on success , false on error ( usually permission denied )
2005-06-14 23:54:17 +02:00
*/
2011-03-09 18:42:52 +01:00
function delete ( $cal_id , $recur_date = 0 , $ignore_acl = false , $skip_notification = false )
2005-06-14 23:54:17 +02:00
{
2005-11-09 00:15:14 +01:00
if ( ! ( $event = $this -> read ( $cal_id , $recur_date )) ||
2009-08-06 13:29:05 +02:00
! $ignore_acl && ! $this -> check_perms ( EGW_ACL_DELETE , $event ))
2005-11-09 00:15:14 +01:00
{
return false ;
}
2010-06-28 13:15:25 +02:00
// Don't send notification if the event has already been deleted
2011-03-09 18:42:52 +01:00
if ( ! $event [ 'deleted' ] && ! $skip_notification )
2010-06-28 13:15:25 +02:00
{
$this -> send_update ( MSG_DELETED , $event [ 'participants' ], $event );
}
2008-05-08 00:12:25 +02:00
2005-11-09 00:15:14 +01:00
if ( ! $recur_date || $event [ 'recur_type' ] == MCAL_RECUR_NONE )
{
2010-03-05 21:01:44 +01:00
$config = config :: read ( 'phpgwapi' );
2010-04-22 18:09:36 +02:00
if ( ! $config [ 'calendar_delete_history' ] || $event [ 'deleted' ])
{
2010-03-05 21:01:44 +01:00
$this -> so -> delete ( $cal_id );
2010-09-02 22:55:37 +02:00
// delete all links to the event
egw_link :: unlink ( 0 , 'calendar' , $cal_id );
2010-03-05 21:01:44 +01:00
}
2010-04-22 18:09:36 +02:00
elseif ( $config [ 'calendar_delete_history' ])
{
2010-09-27 20:03:46 +02:00
// mark all links to the event as deleted, but keep them
egw_link :: unlink ( 0 , 'calendar' , $cal_id , '' , '' , '' , true );
2011-04-07 10:01:48 +02:00
$event [ 'deleted' ] = $this -> now ;
2010-06-01 09:36:14 +02:00
$this -> save ( $event , $ignore_acl );
2010-05-28 09:24:34 +02:00
// Actually delete alarms
if ( isset ( $event [ 'alarm' ]) && is_array ( $event [ 'alarm' ]))
{
foreach ( $event [ 'alarm' ] as $id => $alarm )
{
$this -> delete_alarm ( $id );
}
}
2010-04-22 18:09:36 +02:00
}
2010-09-11 20:08:48 +02:00
$GLOBALS [ 'egw' ] -> contenthistory -> updateTimeStamp ( 'calendar' , $cal_id , 'delete' , $this -> now );
2005-11-09 00:15:14 +01:00
}
2011-04-07 10:01:48 +02:00
else // delete an exception
2005-11-09 00:15:14 +01:00
{
$event [ 'recur_exception' ][] = $recur_date = $this -> date2ts ( $event [ 'start' ]);
unset ( $event [ 'start' ]);
unset ( $event [ 'end' ]);
$this -> save ( $event ); // updates the content-history
}
2009-07-23 18:14:22 +02:00
if ( $event [ 'reference' ])
{
// evtl. delete recur_exception $event['recurrence'] from event with cal_id=$event['reference']
}
2005-06-14 23:54:17 +02:00
return true ;
}
2005-11-09 00:15:14 +01:00
/**
* helper for send_update and get_update_message
* @ internal
*/
function _get_event_details ( $event , $action , & $event_arr , $disinvited = array ())
{
$details = array ( // event-details for the notify-msg
'id' => $event [ 'id' ],
'action' => $action ,
);
$event_arr = $this -> event2array ( $event );
foreach ( $event_arr as $key => $val )
{
$details [ $key ] = $val [ 'data' ];
}
$details [ 'participants' ] = $details [ 'participants' ] ? implode ( " \n " , $details [ 'participants' ]) : '' ;
2006-03-21 12:25:31 +01:00
$event_arr [ 'link' ][ 'field' ] = lang ( 'URL' );
2007-02-10 19:49:55 +01:00
$eventStart_arr = $this -> date2array ( $event [ 'start' ]); // give this as 'date' to the link to pick the right recurrence for the participants state
2008-06-12 09:06:29 +02:00
$link = $GLOBALS [ 'egw_info' ][ 'server' ][ 'webserver_url' ] . '/index.php?menuaction=calendar.calendar_uiforms.edit&cal_id=' . $event [ 'id' ] . '&date=' . $eventStart_arr [ 'full' ] . '&no_popup=1' ;
2005-11-09 00:15:14 +01:00
// if url is only a path, try guessing the rest ;-)
2008-05-08 17:02:35 +02:00
if ( $link [ 0 ] == '/' )
2005-11-09 00:15:14 +01:00
{
2006-07-28 18:30:16 +02:00
$link = ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'enforce_ssl' ] || $_SERVER [ 'HTTPS' ] ? 'https://' : 'http://' ) .
( $GLOBALS [ 'egw_info' ][ 'server' ][ 'hostname' ] ? $GLOBALS [ 'egw_info' ][ 'server' ][ 'hostname' ] : $_SERVER [ 'HTTP_HOST' ]) .
2006-10-02 07:36:55 +02:00
$link ;
2005-11-09 00:15:14 +01:00
}
2006-07-28 18:30:16 +02:00
$event_arr [ 'link' ][ 'data' ] = $details [ 'link' ] = $link ;
2008-05-08 00:12:25 +02:00
2007-11-22 09:29:16 +01:00
/* this is needed for notification - app
* notification - app creates the link individual for
* every user , so we must provide a neutral link - style
2008-01-30 19:58:00 +01:00
* if calendar implements tracking in near future , this part can be deleted
2007-11-22 09:29:16 +01:00
*/
$link_arr = array ();
$link_arr [ 'text' ] = $event [ 'title' ];
2008-06-12 09:06:29 +02:00
$link_arr [ 'view' ] = array ( 'menuaction' => 'calendar.calendar_uiforms.edit' ,
2008-01-30 19:58:00 +01:00
'cal_id' => $event [ 'id' ],
'date' => $eventStart_arr [ 'full' ],
);
$link_arr [ 'popup' ] = '750x400' ;
2007-11-22 09:29:16 +01:00
$details [ 'link_arr' ] = $link_arr ;
2008-05-08 00:12:25 +02:00
2005-11-09 00:15:14 +01:00
$dis = array ();
foreach ( $disinvited as $uid )
{
$dis [] = $this -> participant_name ( $uid );
}
$details [ 'disinvited' ] = implode ( ', ' , $dis );
return $details ;
}
/**
* create array with name , translated name and readable content of each attributes of an event
*
* old function , so far only used by send_update ( therefor it ' s in bocalupdate and not bocal )
*
* @ param array $event event to use
* @ returns array of attributes with fieldname as key and array with the 'field' = translated name 'data' = readable content ( for participants this is an array ! )
*/
function event2array ( $event )
{
$var [ 'title' ] = Array (
'field' => lang ( 'Title' ),
'data' => $event [ 'title' ]
);
$var [ 'description' ] = Array (
'field' => lang ( 'Description' ),
'data' => $event [ 'description' ]
);
foreach ( explode ( ',' , $event [ 'category' ]) as $cat_id )
{
list ( $cat ) = $GLOBALS [ 'egw' ] -> categories -> return_single ( $cat_id );
$cat_string [] = stripslashes ( $cat [ 'name' ]);
}
$var [ 'category' ] = Array (
'field' => lang ( 'Category' ),
'data' => implode ( ', ' , $cat_string )
);
$var [ 'location' ] = Array (
'field' => lang ( 'Location' ),
'data' => $event [ 'location' ]
);
$var [ 'startdate' ] = Array (
'field' => lang ( 'Start Date/Time' ),
'data' => $this -> format_date ( $event [ 'start' ]),
);
$var [ 'enddate' ] = Array (
'field' => lang ( 'End Date/Time' ),
'data' => $this -> format_date ( $event [ 'end' ]),
);
$pri = Array (
0 => '' ,
1 => lang ( 'Low' ),
2 => lang ( 'Normal' ),
3 => lang ( 'High' )
);
$var [ 'priority' ] = Array (
'field' => lang ( 'Priority' ),
'data' => $pri [ $event [ 'priority' ]]
);
$var [ 'owner' ] = Array (
'field' => lang ( 'Owner' ),
2010-11-01 20:17:57 +01:00
'data' => common :: grab_owner_name ( $event [ 'owner' ])
2005-11-09 00:15:14 +01:00
);
$var [ 'updated' ] = Array (
'field' => lang ( 'Updated' ),
2010-11-01 20:17:57 +01:00
'data' => $this -> format_date ( $event [ 'modtime' ]) . ', ' . common :: grab_owner_name ( $event [ 'modifier' ])
2005-11-09 00:15:14 +01:00
);
$var [ 'access' ] = Array (
'field' => lang ( 'Access' ),
'data' => $event [ 'public' ] ? lang ( 'Public' ) : lang ( 'Private' )
);
if ( isset ( $event [ 'participants' ]) && is_array ( $event [ 'participants' ]))
{
$participants = $this -> participants ( $event , true );
}
$var [ 'participants' ] = Array (
'field' => lang ( 'Participants' ),
'data' => $participants
);
// Repeated Events
if ( $event [ 'recur_type' ] != MCAL_RECUR_NONE )
{
$var [ 'recur_type' ] = Array (
'field' => lang ( 'Repetition' ),
2005-12-02 16:34:45 +01:00
'data' => $this -> recure2string ( $event ),
2005-11-09 00:15:14 +01:00
);
}
return $var ;
}
/**
* log all updates to a file
*
* @ param array $event2save event - data before calling save
* @ param array $event_saved event - data read back from the DB
* @ param array $old_event = null event - data in the DB before calling save
* @ param string $type = 'update'
*/
function log2file ( $event2save , $event_saved , $old_event = null , $type = 'update' )
{
if ( ! ( $f = fopen ( $this -> log_file , 'a' )))
{
echo " <p>error opening ' $this->log_file ' !!!</p> \n " ;
return false ;
}
2011-05-27 22:32:46 +02:00
fwrite ( $f , $type . ': ' . common :: grab_owner_name ( $this -> user ) . ': ' . date ( 'r' ) . " \n " );
2005-11-09 00:15:14 +01:00
fwrite ( $f , " Time: time to save / saved time read back / old time before save \n " );
foreach ( array ( 'start' , 'end' ) as $name )
{
fwrite ( $f , $name . ': ' . ( isset ( $event2save [ $name ]) ? $this -> format_date ( $event2save [ $name ]) : 'not set' ) . ' / ' .
$this -> format_date ( $event_saved [ $name ]) . ' / ' .
( is_null ( $old_event ) ? 'no old event' : $this -> format_date ( $old_event [ $name ])) . " \n " );
}
foreach ( array ( 'event2save' , 'event_saved' , 'old_event' ) as $name )
{
fwrite ( $f , $name . ' = ' . print_r ( $$name , true ));
}
fwrite ( $f , " \n " );
fclose ( $f );
return true ;
}
/**
* saves a new or updated alarm
*
* @ param int $cal_id Id of the calendar - entry
* @ param array $alarm array with fields : text , owner , enabled , ..
* @ return string id of the alarm , or false on error ( eg . no perms )
*/
function save_alarm ( $cal_id , $alarm )
{
if ( ! $cal_id || ! $this -> check_perms ( EGW_ACL_EDIT , $alarm [ 'all' ] ? $cal_id : 0 , ! $alarm [ 'all' ] ? $alarm [ 'owner' ] : 0 ))
{
2006-03-07 20:18:15 +01:00
//echo "<p>no rights to save the alarm=".print_r($alarm,true)." to event($cal_id)</p>";
2005-11-09 00:15:14 +01:00
return false ; // no rights to add the alarm
}
$alarm [ 'time' ] = $this -> date2ts ( $alarm [ 'time' ], true ); // user to server-time
2010-09-11 20:08:48 +02:00
$GLOBALS [ 'egw' ] -> contenthistory -> updateTimeStamp ( 'calendar' , $cal_id , 'modify' , $this -> now );
2009-07-15 22:35:56 +02:00
2010-06-07 19:54:29 +02:00
return $this -> so -> save_alarm ( $cal_id , $alarm , $this -> now );
2005-11-09 00:15:14 +01:00
}
/**
* delete one alarms identified by its id
*
* @ param string $id alarm - id is a string of 'cal:' . $cal_id . ':' . $alarm_nr , it is used as the job - id too
* @ return int number of alarms deleted , false on error ( eg . no perms )
*/
function delete_alarm ( $id )
{
list (, $cal_id ) = explode ( ':' , $id );
if ( ! ( $alarm = $this -> so -> read_alarm ( $id )) || ! $cal_id || ! $this -> check_perms ( EGW_ACL_EDIT , $alarm [ 'all' ] ? $cal_id : 0 , ! $alarm [ 'all' ] ? $alarm [ 'owner' ] : 0 ))
{
return false ; // no rights to delete the alarm
}
2009-07-15 22:35:56 +02:00
2010-09-11 20:08:48 +02:00
$GLOBALS [ 'egw' ] -> contenthistory -> updateTimeStamp ( 'calendar' , $cal_id , 'modify' , $this -> now );
2009-07-15 22:35:56 +02:00
2010-06-07 19:54:29 +02:00
return $this -> so -> delete_alarm ( $id , $this -> now );
2005-11-09 00:15:14 +01:00
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
2009-11-18 15:46:25 +01:00
/**
* Find existing categories in database by name or add categories that do not exist yet
* currently used for ical / sif import
*
* @ param array $catname_list names of the categories which should be found or added
2011-05-15 20:25:16 +02:00
* @ param int | array $old_event = null match against existing event and expand the returned category ids
2009-11-18 15:46:25 +01:00
* by the ones the user normally does not see due to category permissions - used to preserve categories
* @ return array category ids ( found , added and preserved categories )
*/
2011-05-15 20:25:16 +02:00
function find_or_add_categories ( $catname_list , $old_event = null )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2011-05-15 20:25:16 +02:00
if ( is_array ( $old_event ) || $old_event > 0 )
2009-11-18 15:46:25 +01:00
{
// preserve categories without users read access
2011-05-15 20:25:16 +02:00
if ( ! is_array ( $old_event )) $old_event = $this -> read ( $old_event );
2009-11-18 15:46:25 +01:00
$old_categories = explode ( ',' , $old_event [ 'category' ]);
$old_cats_preserve = array ();
2009-12-01 11:24:55 +01:00
if ( is_array ( $old_categories ) && count ( $old_categories ) > 0 )
2009-11-18 15:46:25 +01:00
{
2009-12-01 11:24:55 +01:00
foreach ( $old_categories as $cat_id )
2009-11-18 15:46:25 +01:00
{
2009-12-01 11:24:55 +01:00
if ( ! $this -> categories -> check_perms ( EGW_ACL_READ , $cat_id ))
2009-11-18 15:46:25 +01:00
{
$old_cats_preserve [] = $cat_id ;
}
}
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
$cat_id_list = array ();
2010-03-07 00:06:43 +01:00
foreach (( array ) $catname_list as $cat_name )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2010-03-07 00:06:43 +01:00
$cat_name = trim ( $cat_name );
$cat_id = $this -> categories -> name2id ( $cat_name , 'X-' );
2010-03-02 19:01:41 +01:00
2010-03-07 00:06:43 +01:00
if ( ! $cat_id )
{
// some SyncML clients (mostly phones) add an X- to the category names
if ( strncmp ( $cat_name , 'X-' , 2 ) == 0 )
2007-12-11 10:29:50 +01:00
{
2010-03-07 00:06:43 +01:00
$cat_name = substr ( $cat_name , 2 );
2007-12-11 10:29:50 +01:00
}
2010-03-07 00:06:43 +01:00
$cat_id = $this -> categories -> add ( array ( 'name' => $cat_name , 'descr' => $cat_name , 'access' => 'private' ));
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
2010-03-07 00:06:43 +01:00
if ( $cat_id )
{
$cat_id_list [] = $cat_id ;
2007-12-11 10:29:50 +01:00
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
2009-11-19 19:56:04 +01:00
2009-12-01 11:24:55 +01:00
if ( is_array ( $old_cats_preserve ) && count ( $old_cats_preserve ) > 0 )
2009-11-18 15:46:25 +01:00
{
$cat_id_list = array_merge ( $cat_id_list , $old_cats_preserve );
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
if ( count ( $cat_id_list ) > 1 )
{
2007-12-11 10:29:50 +01:00
$cat_id_list = array_unique ( $cat_id_list );
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
sort ( $cat_id_list , SORT_NUMERIC );
}
2009-11-18 15:46:25 +01:00
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
return $cat_id_list ;
}
function get_categories ( $cat_id_list )
{
2007-12-11 10:29:50 +01:00
if ( ! is_array ( $cat_id_list ))
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2007-12-11 10:29:50 +01:00
$cat_id_list = explode ( ',' , $cat_id_list );
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
$cat_list = array ();
2009-12-01 11:24:55 +01:00
foreach ( $cat_id_list as $cat_id )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2009-11-19 19:56:04 +01:00
if ( $cat_id && $this -> categories -> check_perms ( EGW_ACL_READ , $cat_id ) &&
2009-11-18 15:46:25 +01:00
( $cat_name = $this -> categories -> id2name ( $cat_id )) && $cat_name != '--' )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2009-11-18 15:46:25 +01:00
$cat_list [] = $cat_name ;
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
}
return $cat_list ;
}
2009-07-15 22:35:56 +02:00
/**
* Try to find a matching db entry
*
* @ param array $event the vCalendar data we try to find
2010-02-11 08:33:32 +01:00
* @ param string filter = 'exact' exact -> find the matching entry
* check -> check ( consitency ) for identical matches
2010-01-29 22:42:54 +01:00
* relax -> be more tolerant
* master -> try to find a releated series master
2010-02-09 22:56:39 +01:00
* @ return array calendar_ids of matching entries
2009-07-15 22:35:56 +02:00
*/
2010-01-29 22:42:54 +01:00
function find_event ( $event , $filter = 'exact' )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
$matchingEvents = array ();
2009-12-01 11:24:55 +01:00
$query = array ();
2010-01-29 22:42:54 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
" ( $filter )[EVENT]: " . array2string ( $event ) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-09-12 01:15:05 +02:00
if ( ! isset ( $event [ 'recurrence' ])) $event [ 'recurrence' ] = 0 ;
2010-11-11 09:51:13 +01:00
2010-01-29 22:42:54 +01:00
if ( $filter == 'master' )
{
2010-03-07 00:06:43 +01:00
$query [] = 'recur_type!=' . MCAL_RECUR_NONE ;
$query [ 'cal_recurrence' ] = 0 ;
2010-02-03 13:24:42 +01:00
}
2010-09-12 22:14:46 +02:00
elseif ( $filter == 'exact' )
2010-09-12 01:15:05 +02:00
{
2010-09-12 22:14:46 +02:00
if ( $event [ 'recur_type' ] != MCAL_RECUR_NONE )
{
$query [] = 'recur_type=' . $event [ 'recur_type' ];
}
else
{
$query [] = 'recur_type IS NULL' ;
}
2010-11-11 09:51:13 +01:00
$query [ 'cal_recurrence' ] = $event [ 'recurrence' ];
2010-09-12 01:15:05 +02:00
}
2010-11-11 09:51:13 +01:00
2010-01-29 22:42:54 +01:00
if ( $event [ 'id' ])
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'(' . $event [ 'id' ] . " )[EventID] \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-03-07 00:06:43 +01:00
if (( $egwEvent = $this -> read ( $event [ 'id' ], 0 , false , 'server' )))
2010-01-29 22:42:54 +01:00
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'()[FOUND]:' . array2string ( $egwEvent ) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-03-07 00:06:43 +01:00
if ( $egwEvent [ 'recur_type' ] != MCAL_RECUR_NONE &&
( empty ( $event [ 'uid' ]) || $event [ 'uid' ] == $egwEvent [ 'uid' ]))
2010-01-29 22:42:54 +01:00
{
2010-03-07 00:06:43 +01:00
if ( $filter == 'master' )
{
$matchingEvents [] = $egwEvent [ 'id' ]; // we found the master
}
if ( $event [ 'recur_type' ] == $egwEvent [ 'recur_type' ])
2010-01-29 22:42:54 +01:00
{
2010-03-07 00:06:43 +01:00
$matchingEvents [] = $egwEvent [ 'id' ]; // we found the event
2010-01-29 22:42:54 +01:00
}
2010-03-07 00:06:43 +01:00
elseif ( $event [ 'recur_type' ] == MCAL_RECUR_NONE &&
$event [ 'recurrence' ] != 0 )
{
$exceptions = $this -> so -> get_recurrence_exceptions ( $egwEvent , $event [ 'tzid' ]);
if ( in_array ( $event [ 'recurrence' ], $exceptions ))
{
$matchingEvents [] = $egwEvent [ 'id' ] . ':' . ( int ) $event [ 'recurrence' ];
}
}
2010-05-19 11:20:15 +02:00
} elseif ( $filter != 'master' && ( $filter == 'exact' ||
$event [ 'recur_type' ] == $egwEvent [ 'recur_type' ] &&
2010-03-16 10:26:01 +01:00
strpos ( $egwEvent [ 'title' ], $event [ 'title' ]) === 0 ))
2010-03-07 00:06:43 +01:00
{
$matchingEvents [] = $egwEvent [ 'id' ]; // we found the event
2010-01-29 22:42:54 +01:00
}
}
2010-03-07 00:06:43 +01:00
if ( ! empty ( $matchingEvents ) || $filter == 'exact' ) return $matchingEvents ;
2010-01-29 22:42:54 +01:00
}
unset ( $event [ 'id' ]);
2010-03-09 18:03:41 +01:00
// No chance to find a master without [U]ID
if ( $filter == 'master' && empty ( $event [ 'uid' ])) return $matchingEvents ;
2010-01-29 22:42:54 +01:00
// only query calendars of users, we have READ-grants from
$users = array ();
foreach ( array_keys ( $this -> grants ) as $user )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
$user = trim ( $user );
if ( $this -> check_perms ( EGW_ACL_READ | EGW_ACL_READ_FOR_PARTICIPANTS | EGW_ACL_FREEBUSY , 0 , $user ))
{
if ( $user && ! in_array ( $user , $users )) // already added?
{
$users [] = $user ;
}
}
elseif ( $GLOBALS [ 'egw' ] -> accounts -> get_type ( $user ) != 'g' )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
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 ;
2009-09-25 00:29:22 +02:00
2010-01-29 22:42:54 +01:00
// 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 ))
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
foreach ( $members as $member )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
// 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' ];
}
2009-07-15 22:35:56 +02:00
}
}
2010-01-29 22:42:54 +01:00
}
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 ))
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
foreach ( $memberships as $group )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
if ( ! in_array ( $group [ 'account_id' ], $users ))
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
$users [] = $group [ 'account_id' ];
2009-07-15 22:35:56 +02:00
}
2009-09-25 00:29:22 +02:00
}
2010-01-29 22:42:54 +01:00
}
}
}
2010-03-07 00:06:43 +01:00
2010-02-11 22:04:10 +01:00
if ( $filter != 'master' && ( $filter != 'exact' || empty ( $event [ 'uid' ])))
2010-01-29 22:42:54 +01:00
{
2010-04-23 17:15:22 +02:00
if ( ! empty ( $event [ 'whole_day' ]))
2010-01-29 22:42:54 +01:00
{
2010-02-09 22:56:39 +01:00
if ( $filter == 'relax' )
{
$delta = 1800 ;
}
else
{
$delta = 60 ;
}
2010-01-29 22:42:54 +01:00
2010-02-09 22:56:39 +01:00
// 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' ]))
{
2010-02-11 08:33:32 +01:00
if ( $filter == 'relax' )
2010-02-09 22:56:39 +01:00
{
2010-02-11 08:33:32 +01:00
$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 ));
2010-02-09 22:56:39 +01:00
}
}
2010-03-09 18:03:41 +01:00
if ( $filter == 'relax' )
{
$matchFields = array ();
}
else
{
2010-05-14 11:28:46 +02:00
$matchFields = array ( 'priority' , 'public' );
2010-03-09 18:03:41 +01:00
}
2010-01-29 22:42:54 +01:00
foreach ( $matchFields as $key )
{
2010-02-22 11:02:33 +01:00
if ( isset ( $event [ $key ])) $query [ 'cal_' . $key ] = $event [ $key ];
2010-01-29 22:42:54 +01:00
}
}
2010-09-12 01:15:05 +02:00
2010-02-11 08:33:32 +01:00
if ( ! empty ( $event [ 'uid' ]))
2010-01-29 22:42:54 +01:00
{
$query [ 'cal_uid' ] = $event [ 'uid' ];
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'(' . $event [ 'uid' ] . " )[EventUID] \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
}
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'[QUERY]: ' . array2string ( $query ) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
if ( ! count ( $users ) || ! ( $foundEvents =
2010-06-01 11:26:00 +02:00
$this -> so -> search ( null , null , $users , 0 , 'owner' , false , 0 , array ( 'query' => $query ))))
2010-01-29 22:42:54 +01:00
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
" [NO MATCH] \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
return $matchingEvents ;
}
$pseudos = array ();
foreach ( $foundEvents as $egwEvent )
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'[FOUND]: ' . array2string ( $egwEvent ) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-11-11 09:51:13 +01:00
2010-02-17 14:29:28 +01:00
if ( in_array ( $egwEvent [ 'id' ], $matchingEvents )) continue ;
2010-11-11 09:51:13 +01:00
2010-01-29 22:42:54 +01:00
// convert timezone id of event to tzid (iCal id like 'Europe/Berlin')
if ( ! $egwEvent [ 'tz_id' ] || ! ( $egwEvent [ 'tzid' ] = calendar_timezones :: id2tz ( $egwEvent [ 'tz_id' ])))
{
$egwEvent [ 'tzid' ] = egw_time :: $server_timezone -> getName ();
}
2010-06-23 16:52:55 +02:00
if ( ! isset ( self :: $tz_cache [ $egwEvent [ 'tzid' ]]))
2010-03-07 00:06:43 +01:00
{
2010-06-23 16:52:55 +02:00
self :: $tz_cache [ $egwEvent [ 'tzid' ]] = calendar_timezones :: DateTimeZone ( $egwEvent [ 'tzid' ]);
}
if ( ! $event [ 'tzid' ])
{
$event [ 'tzid' ] = egw_time :: $server_timezone -> getName ();
}
if ( ! isset ( self :: $tz_cache [ $event [ 'tzid' ]]))
{
self :: $tz_cache [ $event [ 'tzid' ]] = calendar_timezones :: DateTimeZone ( $event [ 'tzid' ]);
}
2010-11-11 09:51:13 +01:00
2010-06-23 16:52:55 +02:00
if ( ! empty ( $event [ 'uid' ]))
{
if ( $filter == 'master' )
2010-03-07 00:06:43 +01:00
{
2010-06-23 16:52:55 +02:00
// We found the master
$matchingEvents = array ( $egwEvent [ 'id' ]);;
2010-03-07 00:06:43 +01:00
break ;
2010-11-11 09:51:13 +01:00
}
if ( $filter == 'exact' )
2010-03-07 00:06:43 +01:00
{
2010-06-23 16:52:55 +02:00
// UID found
if ( empty ( $event [ 'recurrence' ]))
2010-03-07 00:06:43 +01:00
{
2010-06-23 16:52:55 +02:00
$egwstart = new egw_time ( $egwEvent [ 'start' ], egw_time :: $server_timezone );
$egwstart -> setTimezone ( self :: $tz_cache [ $egwEvent [ 'tzid' ]]);
$dtstart = new egw_time ( $event [ 'start' ], egw_time :: $server_timezone );
$dtstart -> setTimezone ( self :: $tz_cache [ $event [ 'tzid' ]]);
if ( $egwEvent [ 'recur_type' ] == MCAL_RECUR_NONE &&
2010-11-11 09:51:13 +01:00
$event [ 'recur_type' ] == MCAL_RECUR_NONE ||
2010-06-23 16:52:55 +02:00
$egwEvent [ 'recur_type' ] != MCAL_RECUR_NONE &&
$event [ 'recur_type' ] != MCAL_RECUR_NONE )
{
if ( $egwEvent [ 'recur_type' ] == MCAL_RECUR_NONE &&
$egwstart -> format ( 'Ymd' ) == $dtstart -> format ( 'Ymd' ) ||
$egwEvent [ 'recur_type' ] != MCAL_RECUR_NONE )
{
// We found an exact match
$matchingEvents = array ( $egwEvent [ 'id' ]);
break ;
}
else
{
2010-11-11 09:51:13 +01:00
$matchingEvents [] = $egwEvent [ 'id' ];
2010-06-23 16:52:55 +02:00
}
}
continue ;
2010-03-07 00:06:43 +01:00
}
2010-06-23 16:52:55 +02:00
elseif ( $egwEvent [ 'recurrence' ] == $event [ 'recurrence' ])
2010-03-07 00:06:43 +01:00
{
2010-06-23 16:52:55 +02:00
// We found an exact match
$matchingEvents = array ( $egwEvent [ 'id' ]);
2010-03-07 00:06:43 +01:00
break ;
}
2010-06-23 16:52:55 +02:00
if ( $egwEvent [ 'recur_type' ] != MCAL_RECUR_NONE &&
$event [ 'recur_type' ] == MCAL_RECUR_NONE &&
! $egwEvent [ 'recurrence' ] && $event [ 'recurrence' ])
{
$exceptions = $this -> so -> get_recurrence_exceptions ( $egwEvent , $event [ 'tzid' ]);
if ( in_array ( $event [ 'recurrence' ], $exceptions ))
{
// We found a pseudo exception
$matchingEvents = array ( $egwEvent [ 'id' ] . ':' . ( int ) $event [ 'recurrence' ]);
break ;
}
}
continue ;
2010-03-07 00:06:43 +01:00
}
}
2010-01-29 22:42:54 +01:00
// check times
if ( $filter != 'relax' )
{
2010-04-23 17:15:22 +02:00
if ( empty ( $event [ 'whole_day' ]))
2010-01-29 22:42:54 +01:00
{
2010-04-23 17:15:22 +02:00
if ( abs ( $event [ 'end' ] - $egwEvent [ 'end' ]) >= 120 )
2009-09-25 00:29:22 +02:00
{
2010-01-29 22:42:54 +01:00
if ( $this -> log )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-04-23 17:15:22 +02:00
" () egwEvent length does not match! \n " , 3 , $this -> logfile );
2009-07-15 22:35:56 +02:00
}
2010-01-29 22:42:54 +01:00
continue ;
2009-07-15 22:35:56 +02:00
}
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
else
2010-01-29 22:42:54 +01:00
{
2010-04-23 17:15:22 +02:00
if ( ! $this -> so -> isWholeDay ( $egwEvent ))
2010-01-29 22:42:54 +01:00
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-04-23 17:15:22 +02:00
" () egwEvent is not a whole-day event! \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
continue ;
}
}
}
2009-07-15 22:35:56 +02:00
2010-02-09 22:56:39 +01:00
// check for real match
2010-03-09 18:03:41 +01:00
$matchFields = array ( 'title' , 'description' );
if ( $filter != 'relax' )
2010-02-09 22:56:39 +01:00
{
2010-03-09 18:03:41 +01:00
$matchFields [] = 'location' ;
2010-02-09 22:56:39 +01:00
}
foreach ( $matchFields as $key )
{
if ( ! empty ( $event [ $key ]) && ( empty ( $egwEvent [ $key ])
2010-05-14 15:32:08 +02:00
|| strpos ( str_replace ( " \r \n " , " \n " , $egwEvent [ $key ]), $event [ $key ]) !== 0 ))
2010-02-09 22:56:39 +01:00
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
" () event[ $key ] differ: ' " . $event [ $key ] .
2010-03-09 18:03:41 +01:00
" ' <> ' " . $egwEvent [ $key ] . " ' \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
continue 2 ; // next foundEvent
}
}
2010-03-09 18:03:41 +01:00
if ( is_array ( $event [ 'category' ]))
2010-01-29 22:42:54 +01:00
{
// 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__ .
2010-03-09 18:03:41 +01:00
" () egwEvent category $cat_id is missing! \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
continue 2 ;
}
}
$newCategories = array_diff ( $event [ 'category' ], $egwCategories );
if ( ! empty ( $newCategories ))
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'() event has additional categories:'
. array2string ( $newCategories ) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
continue ;
2009-09-25 00:29:22 +02:00
}
2009-07-15 22:35:56 +02:00
}
2010-03-09 18:03:41 +01:00
if ( $filter != 'relax' )
2010-01-29 22:42:54 +01:00
{
// check participants
if ( is_array ( $event [ 'participants' ]))
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
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__ .
2010-03-09 18:03:41 +01:00
" () additional event['participants']: $attendee\n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
continue 2 ;
}
else
{
unset ( $egwEvent [ 'participants' ][ $attendee ]);
}
}
2010-09-12 22:14:46 +02:00
// ORGANIZER and Groups may be missing
2010-01-29 22:42:54 +01:00
unset ( $egwEvent [ 'participants' ][ $egwEvent [ 'owner' ]]);
2010-09-12 22:14:46 +02:00
foreach ( $egwEvent [ 'participants' ] as $attendee => $status )
{
if ( is_numeric ( $attendee ) && $attendee < 0 )
{
unset ( $egwEvent [ 'participants' ][ $attendee ]);
}
}
2010-01-29 22:42:54 +01:00
if ( ! empty ( $egwEvent [ 'participants' ]))
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
'() missing event[participants]: ' .
2010-03-09 18:03:41 +01:00
array2string ( $egwEvent [ 'participants' ]) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
continue ;
}
2009-07-15 22:35:56 +02:00
}
2010-01-29 22:42:54 +01:00
}
2009-07-15 22:35:56 +02:00
2010-03-09 18:03:41 +01:00
if ( $event [ 'recur_type' ] == MCAL_RECUR_NONE )
2009-07-15 22:35:56 +02:00
{
2010-03-09 18:03:41 +01:00
if ( $egwEvent [ 'recur_type' ] != MCAL_RECUR_NONE )
2010-01-29 22:42:54 +01:00
{
2010-03-09 18:03:41 +01:00
// We found a pseudo Exception
$pseudos [] = $egwEvent [ 'id' ] . ':' . $event [ 'start' ];
continue ;
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
}
elseif ( $filter != 'relax' )
{
// check exceptions
// $exceptions[$remote_ts] = $egw_ts
$exceptions = $this -> so -> get_recurrence_exceptions ( $egwEvent , $event [ '$tzid' ], 0 , 0 , 'map' );
if ( is_array ( $event [ 'recur_exception' ]))
2010-01-29 22:42:54 +01:00
{
2010-03-09 18:03:41 +01:00
foreach ( $event [ 'recur_exception' ] as $key => $day )
2010-01-29 22:42:54 +01:00
{
2010-03-09 18:03:41 +01:00
if ( isset ( $exceptions [ $day ]))
2010-01-29 22:42:54 +01:00
{
2010-03-09 18:03:41 +01:00
unset ( $exceptions [ $day ]);
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
else
2010-01-29 22:42:54 +01:00
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
" () additional event['recur_exception']: $day\n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
continue 2 ;
}
}
if ( ! empty ( $exceptions ))
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
'() missing event[recur_exception]: ' .
2010-09-12 22:14:46 +02:00
array2string ( $event [ 'recur_exception' ]) . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
continue ;
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
}
2010-01-29 22:42:54 +01:00
2010-03-09 18:03:41 +01:00
// check recurrence information
foreach ( array ( 'recur_type' , 'recur_interval' , 'recur_enddate' ) as $key )
{
if ( isset ( $event [ $key ])
&& $event [ $key ] != $egwEvent [ $key ])
2010-01-29 22:42:54 +01:00
{
2010-03-09 18:03:41 +01:00
if ( $this -> log )
2010-01-29 22:42:54 +01:00
{
2010-03-09 18:03:41 +01:00
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
" () events[ $key ] differ: " . $event [ $key ] .
' <> ' . $egwEvent [ $key ] . " \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-03-09 18:03:41 +01:00
continue 2 ;
2010-01-29 22:42:54 +01:00
}
}
2009-07-15 22:35:56 +02:00
}
2010-01-29 22:42:54 +01:00
$matchingEvents [] = $egwEvent [ 'id' ]; // exact match
}
2010-11-11 09:51:13 +01:00
2010-09-12 22:14:46 +02:00
if ( $filter == 'exact' && ! empty ( $event [ 'uid' ]) && count ( $matchingEvents ) > 1
|| $filter != 'master' && ! empty ( $egwEvent [ 'recur_type' ]) && empty ( $event [ 'recur_type' ]))
2010-06-23 16:52:55 +02:00
{
// Unknown exception for existing series
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
" () new exception for series found. \n " , 3 , $this -> logfile );
}
$matchingEvents = array ();
}
2010-11-11 09:51:13 +01:00
2010-01-29 22:42:54 +01:00
// append pseudos as last entries
$matchingEvents = array_merge ( $matchingEvents , $pseudos );
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'[MATCHES]:' . array2string ( $matchingEvents ) . " \n " , 3 , $this -> logfile );
2009-07-15 22:35:56 +02:00
}
2010-01-29 22:42:54 +01:00
return $matchingEvents ;
2009-07-15 22:35:56 +02:00
}
2010-01-14 18:01:30 +01:00
/**
* 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
2010-01-29 22:42:54 +01:00
* SERIES - PSEUDO - EXCEPTION event is a status only exception
2010-01-14 18:01:30 +01:00
* 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
2010-01-29 22:42:54 +01:00
* master_event => for event type SERIES - EXCEPTION , SERIES - PSEUDO - EXCEPTION or SERIES - EXCEPTION - PROPAGATE
2010-01-14 18:01:30 +01:00
* the corresponding series master event array
* NOTE : this param is false if event is of type SERIES - MASTER
*/
2010-02-10 20:47:39 +01:00
function get_event_info ( $event )
{
$type = 'SINGLE' ; // default
$master_event = false ; //default
$stored_event = false ;
$recurrence_event = false ;
$wasPseudo = false ;
2010-01-14 18:01:30 +01:00
2010-02-10 20:47:39 +01:00
if (( $foundEvents = $this -> find_event ( $event , 'exact' )))
{
// We found the exact match
$eventID = array_shift ( $foundEvents );
if ( strstr ( $eventID , ':' ))
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
$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 ;
2010-01-14 18:01:30 +01:00
}
else
{
2010-02-10 20:47:39 +01:00
$stored_event = $this -> read ( $eventID , 0 , false , 'server' );
}
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 )
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
" ()[MASTER]: $eventID\n " , 3 , $this -> logfile );
2010-02-10 20:47:39 +01:00
}
2010-06-23 16:52:55 +02:00
$type = 'SERIES-EXCEPTION' ;
2010-02-10 20:47:39 +01:00
if (( $master_event = $this -> read ( $eventID , 0 , false , 'server' )))
{
2010-06-23 16:52:55 +02:00
if ( isset ( $stored_event [ 'id' ]) &&
$master_event [ 'id' ] != $stored_event [ 'id' ])
2010-01-29 22:42:54 +01:00
{
2010-06-23 16:52:55 +02:00
break ; // this is an existing exception
2010-01-14 18:01:30 +01:00
}
2010-02-10 20:47:39 +01:00
elseif ( isset ( $event [ 'recurrence' ]) &&
in_array ( $event [ 'recurrence' ], $master_event [ 'recur_exception' ]))
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
$type = 'SERIES-PSEUDO-EXCEPTION' ; // could also be a real one
2010-02-17 14:29:28 +01:00
$recurrence_event = $master_event ;
$recurrence_event [ 'start' ] = $event [ 'recurrence' ];
$recurrence_event [ 'end' ] -= $master_event [ 'start' ] - $event [ 'recurrence' ];
2010-02-10 20:47:39 +01:00
break ;
2010-01-29 22:42:54 +01:00
}
2010-02-10 20:47:39 +01:00
elseif ( in_array ( $event [ 'start' ], $master_event [ 'recur_exception' ]))
2010-01-29 22:42:54 +01:00
{
2010-02-10 20:47:39 +01:00
$type = 'SERIES-PSEUDO-EXCEPTION' ; // new pseudo exception?
2010-02-17 14:29:28 +01:00
$recurrence_event = $master_event ;
$recurrence_event [ 'start' ] = $event [ 'start' ];
$recurrence_event [ 'end' ] -= $master_event [ 'start' ] - $event [ 'start' ];
2010-02-10 20:47:39 +01:00
break ;
2010-01-29 22:42:54 +01:00
}
2010-02-10 20:47:39 +01:00
else
2010-01-29 22:42:54 +01:00
{
2010-02-10 20:47:39 +01:00
// try to find a suitable pseudo exception date
$egw_rrule = calendar_rrule :: event2rrule ( $master_event , false );
2010-02-17 14:29:28 +01:00
$egw_rrule -> current = clone $egw_rrule -> time ;
2010-02-10 20:47:39 +01:00
while ( $egw_rrule -> valid ())
2010-01-29 22:42:54 +01:00
{
2010-02-10 20:47:39 +01:00
$occurrence = egw_time :: to ( $egw_rrule -> current (), 'server' );
if ( $this -> log )
2010-02-03 13:24:42 +01:00
{
2010-02-10 20:47:39 +01:00
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ .
2010-03-09 18:03:41 +01:00
'() try occurrence ' . $egw_rrule -> current ()
. " ( $occurrence ) \n " , 3 , $this -> logfile );
2010-01-29 22:42:54 +01:00
}
2010-02-10 20:47:39 +01:00
if ( $event [ 'start' ] == $occurrence )
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
$type = 'SERIES-PSEUDO-EXCEPTION' ; // let's try a pseudo exception
2010-02-17 14:29:28 +01:00
$recurrence_event = $master_event ;
$recurrence_event [ 'start' ] = $occurrence ;
$recurrence_event [ 'end' ] -= $master_event [ 'start' ] - $occurrence ;
2010-02-10 20:47:39 +01:00
break 2 ;
2010-01-29 22:42:54 +01:00
}
2010-02-10 20:47:39 +01:00
if ( isset ( $event [ 'recurrence' ]) && $event [ 'recurrence' ] == $occurrence )
2010-01-29 22:42:54 +01:00
{
2010-02-10 20:47:39 +01:00
$type = 'SERIES-EXCEPTION-PROPAGATE' ;
if ( $stored_event )
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
unset ( $stored_event [ 'id' ]); // signal the true exception
$stored_event [ 'recur_type' ] = MCAL_RECUR_NONE ;
2010-01-14 18:01:30 +01:00
}
2010-02-10 20:47:39 +01:00
break 2 ;
2010-01-14 18:01:30 +01:00
}
2010-02-10 20:47:39 +01:00
$egw_rrule -> next_no_exception ();
2010-01-14 18:01:30 +01:00
}
}
}
2010-02-10 20:47:39 +01:00
}
}
2010-01-29 22:42:54 +01:00
2010-02-10 20:47:39 +01:00
// 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 )
2010-03-16 10:26:01 +01:00
{
2010-02-10 20:47:39 +01:00
if ( ! empty ( $event [ $key ]) && $recurrence_event [ $key ] != $event [ $key ])
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
if ( $wasPseudo )
2010-01-29 22:42:54 +01:00
{
2010-02-10 20:47:39 +01:00
// We started with a pseudo exception
$type = 'SERIES-EXCEPTION-PROPAGATE' ;
}
else
{
$type = 'SERIES-EXCEPTION' ;
}
2010-02-03 13:24:42 +01:00
2010-02-10 20:47:39 +01:00
if ( $stored_event )
{
unset ( $stored_event [ 'id' ]); // signal the true exception
$stored_event [ 'recur_type' ] = MCAL_RECUR_NONE ;
2010-01-29 22:42:54 +01:00
}
2010-02-10 20:47:39 +01:00
break ;
2010-01-14 18:01:30 +01:00
}
2010-03-16 10:26:01 +01:00
}
2010-02-10 20:47:39 +01:00
// 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' ]);
}
2010-01-14 18:01:30 +01:00
2010-02-10 20:47:39 +01:00
// check ACL
if ( is_array ( $master_event ))
{
$acl_edit = $this -> check_perms ( EGW_ACL_EDIT , $master_event [ 'id' ]);
}
else
{
if ( is_array ( $stored_event ))
2010-01-14 18:01:30 +01:00
{
2010-02-10 20:47:39 +01:00
$acl_edit = $this -> check_perms ( EGW_ACL_EDIT , $stored_event [ 'id' ]);
2010-01-14 18:01:30 +01:00
}
else
{
2010-02-10 20:47:39 +01:00
$acl_edit = true ; // new event
2010-01-14 18:01:30 +01:00
}
2010-02-10 20:47:39 +01:00
}
2010-01-14 18:01:30 +01:00
2010-02-10 20:47:39 +01:00
return array (
'type' => $type ,
'acl_edit' => $acl_edit ,
'stored_event' => $stored_event ,
'master_event' => $master_event ,
);
2010-01-14 18:01:30 +01:00
}
2010-01-29 22:42:54 +01:00
2010-02-09 22:56:39 +01:00
/**
2010-02-04 13:08:03 +01:00
* Translates all timestamps for a given event from server - time to user - time .
2010-01-29 22:42:54 +01:00
* The update () and save () methods expect timestamps in user - time .
* @ param & $event the event we are working on
*
*/
function server2usertime ( & $event )
{
// we run all dates through date2usertime, to adjust to user-time
foreach ( array ( 'start' , 'end' , 'recur_enddate' , 'recurrence' ) as $ts )
{
// we convert here from server-time to timestamps in user-time!
if ( isset ( $event [ $ts ])) $event [ $ts ] = $event [ $ts ] ? $this -> date2usertime ( $event [ $ts ]) : 0 ;
}
// same with the recur exceptions
if ( isset ( $event [ 'recur_exception' ]) && is_array ( $event [ 'recur_exception' ]))
{
foreach ( $event [ 'recur_exception' ] as $n => $date )
{
$event [ 'recur_exception' ][ $n ] = $this -> date2usertime ( $date );
}
}
// same with the alarms
if ( isset ( $event [ 'alarm' ]) && is_array ( $event [ 'alarm' ]))
{
foreach ( $event [ 'alarm' ] as $id => $alarm )
{
$event [ 'alarm' ][ $id ][ 'time' ] = $this -> date2usertime ( $alarm [ 'time' ]);
}
}
}
2010-04-22 22:27:14 +02:00
/**
2011-06-14 20:16:08 +02:00
* Delete events that are more than $age years old
*
* Purges old events from the database
*
* @ param int | float $age How many years old the event must be before it is deleted
*/
2010-04-22 22:27:14 +02:00
function purge ( $age )
{
2011-06-14 20:16:08 +02:00
$this -> so -> purge ( time () - 365 * 24 * 3600 * ( float ) $age );
2010-04-22 22:27:14 +02:00
}
2010-03-25 21:23:22 +01:00
}