2004-11-07 15:38:00 +01:00
< ? php
2007-03-09 12:39:47 +01:00
/**
2011-04-06 21:26:10 +02:00
* EGroupware - Calendar ' s storage - object
2007-03-09 12:39:47 +01:00
*
* @ link http :// www . egroupware . org
* @ package calendar
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2009-11-04 08:57:55 +01:00
* @ author Christian Binder < christian - AT - jaytraxx . de >
2009-07-15 22:35:56 +02:00
* @ author Joerg Lehrke < jlehrke @ noc . de >
2011-04-06 14:56:40 +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 $
*/
2004-11-07 15:38:00 +01:00
/**
2005-11-09 00:15:14 +01:00
* some necessary defines used by the calendar
*/
2009-08-04 19:14:16 +02:00
if ( ! extension_loaded ( 'mcal' ))
2005-11-09 00:15:14 +01:00
{
define ( 'MCAL_RECUR_NONE' , 0 );
define ( 'MCAL_RECUR_DAILY' , 1 );
define ( 'MCAL_RECUR_WEEKLY' , 2 );
define ( 'MCAL_RECUR_MONTHLY_MDAY' , 3 );
define ( 'MCAL_RECUR_MONTHLY_WDAY' , 4 );
define ( 'MCAL_RECUR_YEARLY' , 5 );
define ( 'MCAL_RECUR_SECONDLY' , 6 );
define ( 'MCAL_RECUR_MINUTELY' , 7 );
define ( 'MCAL_RECUR_HOURLY' , 8 );
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
define ( 'MCAL_M_SUNDAY' , 1 );
define ( 'MCAL_M_MONDAY' , 2 );
define ( 'MCAL_M_TUESDAY' , 4 );
define ( 'MCAL_M_WEDNESDAY' , 8 );
define ( 'MCAL_M_THURSDAY' , 16 );
define ( 'MCAL_M_FRIDAY' , 32 );
define ( 'MCAL_M_SATURDAY' , 64 );
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
define ( 'MCAL_M_WEEKDAYS' , 62 );
define ( 'MCAL_M_WEEKEND' , 65 );
define ( 'MCAL_M_ALLDAYS' , 127 );
}
define ( 'REJECTED' , 0 );
define ( 'NO_RESPONSE' , 1 );
define ( 'TENTATIVE' , 2 );
define ( 'ACCEPTED' , 3 );
2010-02-17 14:29:28 +01:00
define ( 'DELEGATED' , 4 );
2005-11-09 00:15:14 +01:00
2009-08-10 11:24:39 +02:00
define ( 'HOUR_s' , 60 * 60 );
define ( 'DAY_s' , 24 * HOUR_s );
define ( 'WEEK_s' , 7 * DAY_s );
2005-11-09 00:15:14 +01:00
/**
* Class to store all calendar data ( storage object )
2004-11-07 15:38:00 +01:00
*
2012-09-18 10:02:56 +02:00
* Tables used by calendar_so :
* - egw_cal : general calendar data : cal_id , title , describtion , locations , range - start and - end dates
* - egw_cal_dates : start - and enddates ( multiple entry per cal_id for recuring events ! ), recur_exception flag
2007-10-09 10:50:06 +02:00
* - egw_cal_user : participant info including status ( multiple entries per cal_id AND startdate for recuring events )
2012-09-18 10:02:56 +02:00
* - egw_cal_repeats : recur - data : type , interval , days etc .
2005-11-09 00:15:14 +01:00
* - egw_cal_extra : custom fields ( multiple entries per cal_id possible )
2006-03-29 09:01:18 +02:00
*
2012-08-11 12:01:02 +02:00
* The new UI , BO and SO classes have a strict definition , in which timezone they operate :
2004-11-07 15:38:00 +01:00
* 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
2012-01-23 08:41:29 +01:00
*
* DB - model uses egw_cal_user . cal_status = 'X' for participants who got deleted . They never get returned by
* read or search methods , but influence the ctag of the deleted users calendar !
2012-09-18 10:02:56 +02:00
*
* range_start / _end in main - table contains start and end of whole event series ( range_end is NULL for unlimited recuring events ),
* saving the need to always join dates table , to query non - enumerating recuring events ( like CalDAV or ActiveSync does ) .
* This effectivly stores MIN ( cal_start ) and MAX ( cal_end ) permanently as column in main - table and improves speed tremendiously
* ( few milisecs instead of more then 2 minutes on huge installations ) !
* It ' s set in calendar_so :: save from start and end or recur_enddate , so nothing changes for higher level classes .
*
* @ ToDo drop egw_cal_repeats table in favor of a rrule colum in main table ( saves always used left join and allows to store all sorts of rrules )
2004-11-07 15:38:00 +01:00
*/
2008-06-07 19:45:33 +02:00
class calendar_so
2004-11-07 15:38:00 +01:00
{
/**
* name of the main calendar table and prefix for all other calendar tables
*/
2005-11-09 00:15:14 +01:00
var $cal_table = 'egw_cal' ;
var $extra_table , $repeats_table , $user_table , $dates_table , $all_tables ;
2006-03-29 09:01:18 +02:00
2004-11-07 15:38:00 +01:00
/**
2009-05-06 11:11:37 +02:00
* reference to global db - object
2008-05-08 00:12:25 +02:00
*
2007-05-07 10:27:50 +02:00
* @ var egw_db
2004-11-07 15:38:00 +01:00
*/
var $db ;
2007-05-07 10:27:50 +02:00
/**
* instance of the async object
*
* @ var asyncservice
*/
var $async ;
2008-07-18 13:36:09 +02:00
/**
* SQL to sort by status U , T , A , R
*
*/
const STATUS_SORT = " CASE cal_status WHEN 'U' THEN 1 WHEN 'T' THEN 2 WHEN 'A' THEN 3 WHEN 'R' THEN 4 ELSE 0 END ASC " ;
2005-11-09 00:15:14 +01:00
2009-11-19 11:13:17 +01:00
/**
* Cached timezone data
*
* @ var array id => data
*/
protected static $tz_cache = array ();
2004-11-07 15:38:00 +01:00
/**
* Constructor of the socal class
*/
2008-06-07 19:45:33 +02:00
function __construct ()
2004-11-07 15:38:00 +01:00
{
2008-03-21 21:30:19 +01:00
$this -> async = $GLOBALS [ 'egw' ] -> asyncservice ;
2008-03-15 15:10:20 +01:00
$this -> db = $GLOBALS [ 'egw' ] -> db ;
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
$this -> all_tables = array ( $this -> cal_table );
foreach ( array ( 'extra' , 'repeats' , 'user' , 'dates' ) as $name )
2004-11-07 15:38:00 +01:00
{
$vname = $name . '_table' ;
2005-11-09 00:15:14 +01:00
$this -> all_tables [] = $this -> $vname = $this -> cal_table . '_' . $name ;
2004-11-07 15:38:00 +01:00
}
}
2006-03-29 09:01:18 +02:00
2004-11-07 15:38:00 +01:00
/**
* reads one or more calendar entries
*
* All times ( start , end and modified ) are returned as timesstamps in servertime !
*
2009-07-15 22:35:56 +02:00
* @ param int | array | string $ids id or array of id ' s of the entries to read , or string with a single uid
2010-01-15 09:49:36 +01:00
* @ param int $recur_date = 0 if set read the next recurrence at or after the timestamp , default 0 = read the initital one
2011-04-06 21:26:10 +02:00
* @ return array | boolean array with cal_id => event array pairs or false if entry not found
2004-11-07 15:38:00 +01:00
*/
2005-11-09 00:15:14 +01:00
function read ( $ids , $recur_date = 0 )
2004-11-07 15:38:00 +01:00
{
2012-12-07 15:10:51 +01:00
//_debug_array(__METHOD__.__LINE__.'#'.$recur_date.'#'.function_backtrace());
2009-08-06 13:29:05 +02:00
if ( isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'syncml' ][ 'minimum_uid_length' ]))
{
2009-07-15 22:35:56 +02:00
$minimum_uid_length = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'syncml' ][ 'minimum_uid_length' ];
2009-08-06 13:29:05 +02:00
}
else
{
2009-07-15 22:35:56 +02:00
$minimum_uid_length = 8 ;
}
2005-11-09 00:15:14 +01:00
//echo "<p>socal::read(".print_r($ids,true).",$recur_date)<br />\n".function_backtrace()."<p>\n";
2012-09-18 10:02:56 +02:00
$cols = self :: get_columns ( 'calendar' , $this -> cal_table );
$cols [ 0 ] = $this -> db -> to_varchar ( $this -> cal_table . '.cal_id' );
$cols = " $this->repeats_table .recur_type, $this->repeats_table .recur_interval, $this->repeats_table .recur_data, " . implode ( ',' , $cols );
$join = " LEFT JOIN $this->repeats_table ON $this->cal_table .cal_id= $this->repeats_table .cal_id " ;
2005-11-09 00:15:14 +01:00
$where = array ();
2011-04-06 21:26:10 +02:00
if ( is_scalar ( $ids ) && ! is_numeric ( $ids )) // a single uid
2005-11-09 00:15:14 +01:00
{
2009-10-25 19:20:00 +01:00
// We want only the parents to match
2005-11-09 00:15:14 +01:00
$where [ 'cal_uid' ] = $ids ;
2009-07-15 22:35:56 +02:00
$where [ 'cal_reference' ] = 0 ;
2010-01-29 22:42:54 +01:00
$where [ 'cal_recurrence' ] = 0 ;
2005-11-09 00:15:14 +01:00
}
2011-04-06 21:26:10 +02:00
elseif ( is_array ( $ids ) && isset ( $ids [ count ( $ids ) - 1 ]) || is_scalar ( $ids )) // one or more cal_id's
{
$where [ 'cal_id' ] = $ids ;
}
else // array with column => value pairs
{
$where = $ids ;
2011-10-17 16:06:21 +02:00
unset ( $ids ); // otherwise users get not read!
2011-04-06 21:26:10 +02:00
}
if ( isset ( $where [ 'cal_id' ])) // prevent non-unique column-name cal_id
{
$where [] = $this -> db -> expression ( $this -> cal_table , $this -> cal_table . '.' , array (
'cal_id' => $where [ 'cal_id' ],
));
unset ( $where [ 'cal_id' ]);
}
2005-11-09 00:15:14 +01:00
if (( int ) $recur_date )
{
$where [] = 'cal_start >= ' . ( int ) $recur_date ;
2012-09-18 10:02:56 +02:00
$group_by = 'GROUP BY ' . $cols ;
$cols .= ',MIN(cal_start) AS cal_start,MIN(cal_end) AS cal_end' ;
$join = " JOIN $this->dates_table ON $this->cal_table .cal_id= $this->dates_table .cal_id $join " ;
}
else
{
$cols .= ',range_start AS cal_start,(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id) AS cal_end' ;
2005-11-09 00:15:14 +01:00
}
2012-09-18 10:02:56 +02:00
$cols .= ',range_end AS recur_enddate' ;
2008-03-15 15:10:20 +01:00
$events = array ();
2012-09-18 10:02:56 +02:00
foreach ( $this -> db -> select ( $this -> cal_table , $cols , $where , __LINE__ , __FILE__ , false , $group_by , 'calendar' , 0 , $join ) as $row )
2004-11-07 15:38:00 +01:00
{
2012-09-18 10:02:56 +02:00
if ( ! $row [ 'recur_type' ])
{
$row [ 'recur_type' ] = MCAL_RECUR_NONE ;
unset ( $row [ 'recur_enddate' ]);
}
2012-08-11 12:01:02 +02:00
$row [ 'recur_exception' ] = $row [ 'alarm' ] = array ();
2008-03-15 15:10:20 +01:00
$events [ $row [ 'cal_id' ]] = egw_db :: strip_array_keys ( $row , 'cal_' );
2005-11-09 00:15:14 +01:00
// if a uid was supplied, convert it for the further code to an id
2008-03-15 15:10:20 +01:00
if ( ! is_array ( $ids ) && ! is_numeric ( $ids )) $ids = $row [ 'cal_id' ];
2004-11-07 15:38:00 +01:00
}
if ( ! $events ) return false ;
2006-03-29 09:01:18 +02:00
2009-08-04 19:14:16 +02:00
foreach ( $events as & $event )
{
if ( ! isset ( $event [ 'uid' ]) || strlen ( $event [ 'uid' ]) < $minimum_uid_length )
{
2009-07-15 22:35:56 +02:00
// event (without uid), not strong enough uid => create new uid
2012-08-11 12:01:02 +02:00
$event [ 'uid' ] = common :: generate_uid ( 'calendar' , $event [ 'id' ]);
2009-07-15 22:35:56 +02:00
$this -> db -> update ( $this -> cal_table , array ( 'cal_uid' => $event [ 'uid' ]),
array ( 'cal_id' => $event [ 'id' ]), __LINE__ , __FILE__ , 'calendar' );
}
2012-08-11 12:01:02 +02:00
if ( ! ( int ) $recur_date && $event [ 'recur_type' ] != MCAL_RECUR_NONE )
2010-02-17 14:29:28 +01:00
{
2012-08-11 12:01:02 +02:00
foreach ( $this -> db -> select ( $this -> dates_table , 'cal_id,cal_start' , array (
'cal_id' => $ids ,
'recur_exception' => true ,
), __LINE__ , __FILE__ , false , 'ORDER BY cal_id,cal_start' , 'calendar' ) as $row )
2010-02-17 14:29:28 +01:00
{
2012-08-11 12:01:02 +02:00
$events [ $row [ 'cal_id' ]][ 'recur_exception' ][] = $row [ 'cal_start' ];
2010-02-17 14:29:28 +01:00
}
2012-08-11 12:01:02 +02:00
break ; // as above select read all exceptions (and I dont think too short uid problem still exists)
}
2012-12-07 15:10:51 +01:00
// make sure we fetch only real exceptions (deleted occurrences of a series should not show up)
if (( $recur_date && $event [ 'recur_type' ] != MCAL_RECUR_NONE ))
{
//_debug_array(__METHOD__.__LINE__.' recur_date:'.$recur_date.' check cal_start:'.$event['start']);
foreach (( $i = $this -> db -> select ( $this -> dates_table , 'cal_id,cal_start' , array (
'cal_id' => $event [ 'id' ],
'cal_start' => $event [ 'start' ],
'recur_exception' => true ,
), __LINE__ , __FILE__ , false , '' , 'calendar' )) as $row )
{
$isException [ $row [ 'cal_id' ]] = true ;
}
//_debug_array($i->sql.'-> found Rows:'.$i->_numOfRows);
if ( $isException [ $event [ 'id' ]])
{
$x = $this -> db -> select ( $this -> cal_table , 'cal_id' , array (
'cal_uid' => $event [ 'uid' ],
'cal_recurrence' => $event [ 'start' ],
'cal_deleted' => NULL
), __LINE__ , __FILE__ , false , '' , 'calendar' );
//_debug_array(__METHOD__.__LINE__.$x->sql.'-> found Rows:'.$x->_numOfRows);
if ( empty ( $x -> _numOfRows ))
{
$e = $this -> read ( $event [ 'id' ], $event [ 'start' ] + 1 );
$event = $e [ $event [ 'id' ]];
break ;
}
else
{
//real exception -> should we return it? probably not, so we live with the result of the next occurrence of the series
}
}
}
2009-07-15 22:35:56 +02:00
}
2005-11-09 00:15:14 +01:00
// check if we have a real recurance, if not set $recur_date=0
if ( is_array ( $ids ) || $events [( int ) $ids ][ 'recur_type' ] == MCAL_RECUR_NONE )
2004-11-07 15:38:00 +01:00
{
2005-11-09 00:15:14 +01:00
$recur_date = 0 ;
}
else // adjust the given recurance to the real time, it can be a date without time(!)
{
2007-03-02 14:14:17 +01:00
if ( $recur_date )
{
// also remember recur_date, maybe we need it later, duno now
2011-08-03 14:35:42 +02:00
$recur_date = array ( 0 , $events [ $ids ][ 'recur_date' ] = $events [ $ids ][ 'start' ]);
2007-03-02 14:14:17 +01:00
}
2004-11-07 15:38:00 +01:00
}
2007-03-02 14:14:17 +01:00
2011-08-03 14:35:42 +02:00
// participants, if a recur_date give, we read that recurance, plus the one users from the default entry with recur_date=0
// sorting by cal_recur_date ASC makes sure recurence status always overwrites series status
2008-03-15 15:10:20 +01:00
foreach ( $this -> db -> select ( $this -> user_table , '*' , array (
2005-11-09 00:15:14 +01:00
'cal_id' => $ids ,
'cal_recur_date' => $recur_date ,
2012-01-23 08:41:29 +01:00
" cal_status != 'X' " ,
2011-08-03 14:35:42 +02:00
), __LINE__ , __FILE__ , false , 'ORDER BY cal_user_type DESC,cal_recur_date ASC,' . self :: STATUS_SORT , 'calendar' ) as $row ) // DESC puts users before resources and contacts
2004-11-07 15:38:00 +01:00
{
2009-08-06 13:29:05 +02:00
// combine all participant data in uid and status values
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
$status = self :: combine_status ( $row [ 'cal_status' ], $row [ 'cal_quantity' ], $row [ 'cal_role' ]);
$events [ $row [ 'cal_id' ]][ 'participants' ][ $uid ] = $status ;
$events [ $row [ 'cal_id' ]][ 'participant_types' ][ $row [ 'cal_user_type' ]][ $row [ 'cal_user_id' ]] = $status ;
2011-11-09 18:53:42 +01:00
}
2004-11-07 15:38:00 +01:00
// custom fields
2008-03-15 15:10:20 +01:00
foreach ( $this -> db -> select ( $this -> extra_table , '*' , array ( 'cal_id' => $ids ), __LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
2004-11-07 15:38:00 +01:00
{
$events [ $row [ 'cal_id' ]][ '#' . $row [ 'cal_extra_name' ]] = $row [ 'cal_extra_value' ];
}
2006-03-29 09:01:18 +02:00
2004-11-07 15:38:00 +01:00
// alarms, atm. we read all alarms in the system, as this can be done in a single query
foreach (( array ) $this -> async -> read ( 'cal' . ( is_array ( $ids ) ? '' : ':' . ( int ) $ids ) . ':%' ) as $id => $job )
{
list (, $cal_id ) = explode ( ':' , $id );
if ( ! isset ( $events [ $cal_id ])) continue ; // not needed
2006-03-29 09:01:18 +02:00
2004-11-07 15:38:00 +01:00
$alarm = $job [ 'data' ]; // text, enabled
$alarm [ 'id' ] = $id ;
$alarm [ 'time' ] = $job [ 'next' ];
$events [ $cal_id ][ 'alarm' ][ $id ] = $alarm ;
}
//echo "<p>socal::read(".print_r($ids,true).")=<pre>".print_r($events,true)."</pre>\n";
return $events ;
}
2010-01-14 18:01:30 +01:00
2011-04-06 14:46:21 +02:00
/**
* Get maximum modification time of events for given participants and optional owned by them
*
* This includes ALL recurences of an event series
*
2012-09-27 17:46:08 +02:00
* @ param int | string | array $users one or mulitple calendar users
2011-04-06 14:46:21 +02:00
* @ param booelan $owner_too = false if true return also events owned by given users
2012-03-12 09:20:36 +01:00
* @ param boolean $master_only = false only check recurance master ( egw_cal_user . recur_date = 0 )
2011-04-06 14:46:21 +02:00
* @ return int maximum modification timestamp
*/
2012-03-12 09:20:36 +01:00
function get_ctag ( $users , $owner_too = false , $master_only = false )
2011-04-06 14:46:21 +02:00
{
2012-09-25 13:54:41 +02:00
static $ctags = array (); // some per-request caching
$signature = serialize ( func_get_args ());
if ( isset ( $ctags [ $signature ])) return $ctags [ $signature ];
2012-09-27 17:46:08 +02:00
$types = array ();
foreach (( array ) $users as $uid )
{
self :: split_user ( $uid , $type , $id );
$types [ $type ][] = $id ;
}
foreach ( $types as $type => $ids )
{
$where = array (
'cal_user_type' => $type ,
'cal_user_id' => $ids ,
);
if ( count ( $types ) > 1 )
{
$types [ $type ] = $this -> db -> expression ( $this -> user_table , $where );
}
}
if ( count ( $types ) > 1 )
{
$where [] = '(' . explode ( ' OR ' , $types ) . ')' ;
}
2012-03-12 09:20:36 +01:00
if ( $master_only )
{
$where [ 'cal_recur_date' ] = 0 ;
}
2011-04-06 14:46:21 +02:00
if ( $owner_too )
{
2012-09-27 17:46:08 +02:00
// owner can only by users, no groups or resources
2011-04-06 14:46:21 +02:00
foreach ( $users as $key => $user )
{
2012-09-27 17:46:08 +02:00
if ( ! ( $user > 0 )) unset ( $users [ $key ]);
2011-04-06 14:46:21 +02:00
}
$where = $this -> db -> expression ( $this -> user_table , '(' , $where , ' OR ' ) .
$this -> db -> expression ( $this -> cal_table , array (
'cal_owner' => $users ,
), ')' );
}
2012-09-27 17:46:08 +02:00
return $ctags [ $signature ] = $this -> db -> select ( $this -> user_table , 'MAX(cal_modified)' ,
2012-09-25 13:54:41 +02:00
$where , __LINE__ , __FILE__ , false , '' , 'calendar' , 0 , 'JOIN egw_cal ON egw_cal.cal_id=egw_cal_user.cal_id' ) -> fetchColumn ();
2009-12-27 05:21:33 +01:00
}
2005-11-09 00:15:14 +01:00
/**
2010-10-14 18:16:02 +02:00
* generate SQL to filter after a given category ( incl . subcategories )
2006-03-29 09:01:18 +02:00
*
2009-05-05 00:39:27 +02:00
* @ param array | int $cat_id cat - id or array of cat - ids , or ! $cat_id for none
2005-11-09 00:15:14 +01:00
* @ return string SQL to include in the query
*/
function cat_filter ( $cat_id )
{
$sql = '' ;
if ( $cat_id )
{
2010-10-14 18:16:02 +02:00
$cats = $GLOBALS [ 'egw' ] -> categories -> return_all_children ( $cat_id );
2005-11-09 00:15:14 +01:00
array_walk ( $cats , create_function ( '&$val,$key' , '$val = (int) $val;' ));
2011-07-19 09:32:28 +02:00
if ( is_array ( $cat_id ) && count ( $cat_id ) == 1 ) $cat_id = $cat_id [ 0 ];
2007-12-13 17:04:34 +01:00
$sql = '(cal_category' . ( count ( $cats ) > 1 ? " IN (' " . implode ( " ',' " , $cats ) . " ') " : '=' . $this -> db -> quote (( int ) $cat_id ));
2005-11-09 00:15:14 +01:00
foreach ( $cats as $cat )
{
$sql .= ' OR ' . $this -> db -> concat ( " ',' " , 'cal_category' , " ',' " ) . ' LIKE ' . $this -> db -> quote ( '%,' . $cat . ',%' );
}
$sql .= ') ' ;
}
return $sql ;
}
/**
* Searches / lists calendar entries , including repeating ones
*
* @ param int $start startdate of the search / list ( servertime )
* @ param int $end enddate of the search / list ( servertime )
2009-05-05 00:39:27 +02:00
* @ param int | array $users user - id or array of user - id ' s , ! $users means all entries regardless of users
2010-10-14 18:16:02 +02:00
* @ param int | array $cat_id = 0 mixed category - id or array of cat - id ' s ( incl . all sub - categories ), default 0 = all
2012-09-26 16:30:47 +02:00
* @ param string $filter = 'all' string filter - name : all ( not rejected ), accepted , unknown , tentative , rejected or everything ( incl . rejected , deleted )
2009-05-05 00:39:27 +02:00
* @ param int | boolean $offset = False offset for a limited query or False ( default )
2005-11-09 00:15:14 +01:00
* @ param int $num_rows = 0 number of rows to return if offset set , default 0 = use default in user prefs
2010-05-20 17:12:59 +02:00
* @ param array $params = array ()
2010-06-01 11:28:37 +02:00
* @ param string | array $params [ 'query' ] string : pattern so search for , if unset or empty all matching entries are returned ( no search )
2010-05-20 17:12:59 +02:00
* Please Note : a search never returns repeating events more then once AND does not honor start + end date !!!
2010-06-01 11:28:37 +02:00
* array : everything is directly used as $where
2010-05-20 17:12:59 +02:00
* @ param string $params [ 'order' ] = 'cal_start' column - names plus optional DESC | ASC separted by comma
* @ param string $params [ 'sql_filter' ] sql to be and ' ed into query ( fully quoted )
* @ param string | array $params [ 'cols' ] what to select , default " $this->repeats_table .*, $this->cal_table .*,cal_start,cal_end,cal_recur_date " ,
2009-10-03 09:32:05 +02:00
* if specified and not false an iterator for the rows is returned
2010-05-20 17:12:59 +02:00
* @ param string $params [ 'append' ] SQL to append to the query before $order , eg . for a GROUP BY clause
* @ param array $params [ 'cfs' ] custom fields to query , null = none , array () = all , or array with cfs names
* @ param array $params [ 'users' ] raw parameter as passed to calendar_bo :: search () no memberships resolved !
2012-03-12 09:20:36 +01:00
* @ param boolean $params [ 'master_only' ] = false , true only take into account participants / status from master ( for AS )
2011-04-05 17:32:20 +02:00
* @ param int $remove_rejected_by_user = null add join to remove entry , if given user has rejected it
2009-07-15 22:35:56 +02:00
* @ return array of cal_ids , or false if error in the parameters
2005-11-09 00:15:14 +01:00
*
* ToDo : search custom - fields too
*/
2011-04-05 17:32:20 +02:00
function & search ( $start , $end , $users , $cat_id = 0 , $filter = 'all' , $offset = False , $num_rows = 0 , array $params = array (), $remove_rejected_by_user = null )
2005-11-09 00:15:14 +01:00
{
2011-04-06 10:03:32 +02:00
//error_log(__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($offset).",$num_rows,".array2string($params).') '.function_backtrace());
2009-05-05 00:39:27 +02:00
2010-10-28 11:22:01 +02:00
$cols = self :: get_columns ( 'calendar' , $this -> cal_table );
$cols [ 0 ] = $this -> db -> to_varchar ( $this -> cal_table . '.cal_id' );
2012-09-18 10:02:56 +02:00
$cols = isset ( $params [ 'cols' ]) ? $params [ 'cols' ] : " $this->repeats_table .recur_type, $this->repeats_table .recur_interval, $this->repeats_table .recur_data,range_end AS recur_enddate, " . implode ( ',' , $cols ) . " ,cal_start,cal_end, $this->user_table .cal_recur_date " ;
2005-11-09 00:15:14 +01:00
$where = array ();
2010-05-20 17:12:59 +02:00
if ( is_array ( $params [ 'query' ]))
2005-11-09 00:15:14 +01:00
{
2010-05-20 17:12:59 +02:00
$where = $params [ 'query' ];
2005-11-09 00:15:14 +01:00
}
2010-05-20 17:12:59 +02:00
elseif ( $params [ 'query' ])
2005-11-09 00:15:14 +01:00
{
foreach ( array ( 'cal_title' , 'cal_description' , 'cal_location' ) as $col )
{
2010-06-22 18:59:04 +02:00
$to_or [] = $col . ' ' . $this -> db -> capabilities [ egw_db :: CAPABILITY_CASE_INSENSITIV_LIKE ] . ' ' . $this -> db -> quote ( '%' . $params [ 'query' ] . '%' );
2005-11-09 00:15:14 +01:00
}
$where [] = '(' . implode ( ' OR ' , $to_or ) . ')' ;
2011-03-01 00:43:34 +01:00
// Searching - restrict private to own or private grant
2011-08-16 12:20:40 +02:00
if ( ! isset ( $params [ 'private_grants' ]))
{
$params [ 'private_grants' ] = $GLOBALS [ 'egw' ] -> acl -> get_ids_for_location ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ], EGW_ACL_PRIVATE , 'calendar' );
$params [ 'private_grants' ][] = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ]; // db query does NOT return current user
}
$private_filter = '(cal_public=1 OR cal_public=0 AND ' . $this -> db -> expression ( $this -> cal_table , array ( 'cal_owner' => $params [ 'private_grants' ])) . ')' ;
2011-03-01 00:43:34 +01:00
$where [] = $private_filter ;
2005-11-09 00:15:14 +01:00
}
2010-05-20 17:12:59 +02:00
if ( ! empty ( $params [ 'sql_filter' ]) && is_string ( $params [ 'sql_filter' ]))
2009-12-27 05:21:33 +01:00
{
2010-05-20 17:12:59 +02:00
$where [] = $params [ 'sql_filter' ];
2009-12-27 05:21:33 +01:00
}
2005-11-09 00:15:14 +01:00
if ( $users )
{
$users_by_type = array ();
2009-05-05 00:39:27 +02:00
foreach (( array ) $users as $user )
2005-11-09 00:15:14 +01:00
{
if ( is_numeric ( $user ))
{
$users_by_type [ 'u' ][] = ( int ) $user ;
}
elseif ( is_numeric ( substr ( $user , 1 )))
{
2009-05-05 00:39:27 +02:00
$users_by_type [ $user [ 0 ]][] = ( int ) substr ( $user , 1 );
2005-11-09 00:15:14 +01:00
}
}
2011-08-03 18:13:56 +02:00
$to_or = $user_or = array ();
$owner_or = null ;
2010-04-15 15:41:57 +02:00
$useUnionQuery = $this -> db -> capabilities [ 'distinct_on_text' ] && $this -> db -> capabilities [ 'union' ];
2008-03-15 15:10:20 +01:00
$table_def = $this -> db -> get_table_definitions ( 'calendar' , $this -> user_table );
2005-11-09 00:15:14 +01:00
foreach ( $users_by_type as $type => $ids )
{
2010-04-15 14:52:35 +02:00
// when we are able to use Union Querys, we do not OR our query, we save the needed parts for later construction of the union
if ( $useUnionQuery )
2009-09-29 21:58:51 +02:00
{
2011-04-05 17:32:20 +02:00
$user_or [] = $this -> db -> expression ( $table_def , $this -> user_table . '.' , array (
2010-04-15 14:52:35 +02:00
'cal_user_type' => $type ,
2011-04-05 17:32:20 +02:00
), ' AND ' . $this -> user_table . '.' , array (
2010-04-15 14:52:35 +02:00
'cal_user_id' => $ids ,
));
2011-08-03 18:13:56 +02:00
if ( $type == 'u' && $filter == 'owner' )
2010-04-15 14:52:35 +02:00
{
$cal_table_def = $this -> db -> get_table_definitions ( 'calendar' , $this -> cal_table );
2012-09-18 13:40:08 +02:00
// only users can be owners, no need to add groups
$user_ids = array ();
foreach ( $ids as $user_id ) if ( $GLOBALS [ 'egw' ] -> accounts -> get_type ( $user_id ) === 'u' ) $user_ids [] = $user_id ;
$owner_or = $this -> db -> expression ( $cal_table_def , array ( 'cal_owner' => $user_ids ));
2010-04-15 14:52:35 +02:00
}
}
else
{
2011-04-05 17:32:20 +02:00
$to_or [] = $this -> db -> expression ( $table_def , $this -> user_table . '.' , array (
2010-04-15 14:52:35 +02:00
'cal_user_type' => $type ,
2011-04-05 17:32:20 +02:00
), ' AND ' . $this -> user_table . '.' , array (
2010-04-15 14:52:35 +02:00
'cal_user_id' => $ids ,
));
if ( $type == 'u' && ( $filter == 'owner' ))
{
$cal_table_def = $this -> db -> get_table_definitions ( 'calendar' , $this -> cal_table );
$to_or [] = $this -> db -> expression ( $cal_table_def , array ( 'cal_owner' => $ids ));
}
2009-09-29 21:58:51 +02:00
}
2005-11-09 00:15:14 +01:00
}
2010-04-15 14:52:35 +02:00
// this is only used, when we cannot use UNIONS
if ( ! $useUnionQuery ) $where [] = '(' . implode ( ' OR ' , $to_or ) . ')' ;
2006-03-29 09:01:18 +02:00
2012-09-26 16:30:47 +02:00
if ( $filter != 'deleted' && $filter != 'everything' )
2010-04-23 21:11:24 +02:00
{
2010-09-09 11:11:57 +02:00
$where [] = 'cal_deleted IS NULL' ;
2010-03-05 21:01:44 +01:00
}
2009-09-29 21:58:51 +02:00
switch ( $filter )
{
2012-09-26 16:30:47 +02:00
case 'everything' : // no filter at all
break ;
2010-04-26 09:08:23 +02:00
case 'showonlypublic' :
2010-05-20 17:12:59 +02:00
$where [ 'cal_public' ] = 1 ;
2012-01-23 08:41:29 +01:00
$where [] = " $this->user_table .cal_status NOT IN ('R','X') " ; break ;
2010-03-05 21:01:44 +01:00
case 'deleted' :
2012-11-14 17:25:21 +01:00
$where [] = 'cal_deleted IS NOT NULL' ; break ;
2009-09-29 21:58:51 +02:00
case 'unknown' :
2011-04-05 17:32:20 +02:00
$where [] = " $this->user_table .cal_status='U' " ; break ;
2011-05-15 20:25:16 +02:00
case 'not-unknown' :
2012-01-23 08:41:29 +01:00
$where [] = " $this->user_table .cal_status NOT IN ('U','X') " ; break ;
2009-09-29 21:58:51 +02:00
case 'accepted' :
2011-04-05 17:32:20 +02:00
$where [] = " $this->user_table .cal_status='A' " ; break ;
2009-09-29 21:58:51 +02:00
case 'tentative' :
2011-04-05 17:32:20 +02:00
$where [] = " $this->user_table .cal_status='T' " ; break ;
2009-09-29 21:58:51 +02:00
case 'rejected' :
2011-04-05 17:32:20 +02:00
$where [] = " $this->user_table .cal_status='R' " ; break ;
2010-02-17 14:29:28 +01:00
case 'delegated' :
2011-04-05 17:32:20 +02:00
$where [] = " $this->user_table .cal_status='D' " ; break ;
2009-09-29 21:58:51 +02:00
case 'all' :
2010-01-29 22:42:54 +01:00
case 'owner' :
2012-01-23 08:41:29 +01:00
$where [] = " $this->user_table .cal_status!='X' " ; break ;
2009-09-29 21:58:51 +02:00
break ;
default :
2012-01-23 08:41:29 +01:00
$where [] = " $this->user_table .cal_status NOT IN ('R','X') " ;
2009-09-29 21:58:51 +02:00
break ;
}
2005-11-09 00:15:14 +01:00
}
if ( $cat_id )
{
$where [] = $this -> cat_filter ( $cat_id );
}
2011-04-05 17:32:20 +02:00
if ( $start )
{
if ( $params [ 'enum_recuring' ])
{
$where [] = ( int ) $start . ' < cal_end' ;
}
else
{
2012-09-18 10:02:56 +02:00
$where [] = '(' . (( int ) $start ) . ' < range_end OR range_end IS NULL)' ;
2011-04-05 17:32:20 +02:00
}
}
2012-09-28 11:31:41 +02:00
if ( ! preg_match ( '/^[a-z_ ,c]+$/i' , $params [ 'order' ])) $params [ 'order' ] = 'cal_start' ; // gard against SQL injection
2011-08-03 18:13:56 +02:00
// if not enum recuring events, we have to use minimum start- AND end-dates, otherwise we get more then one event per cal_id!
2011-04-05 17:32:20 +02:00
if ( ! $params [ 'enum_recuring' ])
{
$where [] = " $this->user_table .cal_recur_date=0 " ;
2012-09-18 10:02:56 +02:00
$cols = str_replace ( array ( 'cal_start' , 'cal_end' ), array ( 'range_start AS cal_start' , '(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id) AS cal_end' ), $cols );
2012-09-28 11:31:41 +02:00
// in case cal_start is used in a query, eg. calendar_ical::find_event
$where = str_replace ( array ( 'cal_start' , 'cal_end' ), array ( 'range_start' , '(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id)' ), $where );
$params [ 'order' ] = str_replace ( 'cal_start' , 'range_start' , $params [ 'order' ]);
2012-09-18 10:02:56 +02:00
if ( $end ) $where [] = ( int ) $end . ' > range_start' ;
}
elseif ( $end ) $where [] = ( int ) $end . ' > cal_start' ;
2006-03-29 09:01:18 +02:00
2012-09-26 16:30:47 +02:00
if ( $remove_rejected_by_user && $filter != 'everything' )
2011-04-05 17:32:20 +02:00
{
2011-04-09 16:41:15 +02:00
$rejected_by_user_join = " LEFT JOIN $this->user_table rejected_by_user " .
2011-04-05 17:32:20 +02:00
" ON $this->cal_table .cal_id=rejected_by_user.cal_id " .
" AND rejected_by_user.cal_user_type='u' " .
" AND rejected_by_user.cal_user_id= " . $this -> db -> quote ( $remove_rejected_by_user ) .
2012-10-02 14:56:17 +02:00
" AND " . ( ! $params [ 'enum_recuring' ] ? 'rejected_by_user.cal_recur_date=0' :
'(recur_type IS NULL AND rejected_by_user.cal_recur_date=0 OR cal_start=rejected_by_user.cal_recur_date)' );
2011-04-09 16:41:15 +02:00
$or_required = array (
'rejected_by_user.cal_status IS NULL' ,
2012-01-23 08:41:29 +01:00
" rejected_by_user.cal_status NOT IN ('R','X') " ,
2011-04-09 16:41:15 +02:00
);
if ( $filter == 'owner' ) $or_required [] = 'cal_owner=' . ( int ) $remove_rejected_by_user ;
$where [] = '(' . implode ( ' OR ' , $or_required ) . ')' ;
2011-04-05 17:32:20 +02:00
}
2011-04-09 16:41:15 +02:00
//$starttime = microtime(true);
2010-04-15 14:52:35 +02:00
if ( $useUnionQuery )
2006-03-03 13:03:33 +01:00
{
2010-01-21 04:00:53 +01:00
// allow apps to supply participants and/or icons
2010-05-21 17:05:31 +02:00
if ( ! isset ( $params [ 'cols' ])) $cols .= ',NULL AS participants,NULL AS icons' ;
2010-03-06 16:49:59 +01:00
2006-03-29 09:01:18 +02:00
// changed the original OR in the query into a union, to speed up the query execution under MySQL 5
$select = array (
'table' => $this -> cal_table ,
2012-09-18 10:02:56 +02:00
'join' => " JOIN $this->user_table ON $this->cal_table .cal_id= $this->user_table .cal_id LEFT JOIN $this->repeats_table ON $this->cal_table .cal_id= $this->repeats_table .cal_id $rejected_by_user_join " ,
2009-05-05 00:39:27 +02:00
'cols' => $cols ,
2006-03-29 09:01:18 +02:00
'where' => $where ,
2008-03-15 15:10:20 +01:00
'app' => 'calendar' ,
2011-04-05 17:32:20 +02:00
'append' => $params [ 'append' ] . ' ' . $group_by ,
2006-03-29 09:01:18 +02:00
);
2012-09-18 10:02:56 +02:00
if ( $params [ 'enum_recuring' ]) // dates table join only needed to enum recuring events
{
$select [ 'join' ] = " JOIN $this->dates_table ON $this->cal_table .cal_id= $this->dates_table .cal_id " . $select [ 'join' ];
}
2011-04-05 17:32:20 +02:00
$selects = array ();
2010-04-23 08:52:48 +02:00
// we check if there are parts to use for the construction of our UNION query,
2010-04-15 14:52:35 +02:00
// as replace the OR by construction of a suitable UNION for performance reasons
2011-08-03 18:13:56 +02:00
if ( $owner_or || $user_or )
2010-04-15 14:52:35 +02:00
{
2011-04-05 17:32:20 +02:00
foreach ( $user_or as $user_sql )
2010-04-15 14:52:35 +02:00
{
2011-04-05 17:32:20 +02:00
$selects [] = $select ;
$selects [ count ( $selects ) - 1 ][ 'where' ][] = $user_sql ;
if ( $params [ 'enum_recuring' ])
2010-04-15 15:41:57 +02:00
{
2012-11-14 17:25:21 +01:00
$selects [ count ( $selects ) - 1 ][ 'where' ][] = " recur_type IS NULL AND $this->user_table .cal_recur_date=0 " ;
2010-04-16 09:55:43 +02:00
$selects [] = $select ;
2011-04-05 17:32:20 +02:00
$selects [ count ( $selects ) - 1 ][ 'where' ][] = $user_sql ;
$selects [ count ( $selects ) - 1 ][ 'where' ][] = " $this->user_table .cal_recur_date=cal_start " ;
2010-04-15 15:41:57 +02:00
}
2010-04-15 14:52:35 +02:00
}
2011-04-05 17:32:20 +02:00
// if the query is to be filtered by owner we need to add more selects for the union
if ( $owner_or )
2010-04-15 14:52:35 +02:00
{
2011-04-05 17:32:20 +02:00
$selects [] = $select ;
$selects [ count ( $selects ) - 1 ][ 'where' ][] = $owner_or ;
if ( $params [ 'enum_recuring' ])
2010-04-15 15:41:57 +02:00
{
2012-11-14 17:25:21 +01:00
$selects [ count ( $selects ) - 1 ][ 'where' ][] = " recur_type IS NULL AND $this->user_table .cal_recur_date=0 " ;
2010-04-16 09:55:43 +02:00
$selects [] = $select ;
2011-04-05 17:32:20 +02:00
$selects [ count ( $selects ) - 1 ][ 'where' ][] = $owner_or ;
$selects [ count ( $selects ) - 1 ][ 'where' ][] = " $this->user_table .cal_recur_date=cal_start " ;
2010-04-15 15:41:57 +02:00
}
2010-04-15 14:52:35 +02:00
}
}
else
{
// if the query is to be filtered by neither by user nor owner (should not happen?) we need 2 selects for the union
2011-04-05 17:32:20 +02:00
$selects [] = $select ;
if ( $params [ 'enum_recuring' ])
{
2012-11-14 17:25:21 +01:00
$selects [ count ( $selects ) - 1 ][ 'where' ][] = " recur_type IS NULL AND $this->user_table .cal_recur_date=0 " ;
2011-04-05 17:32:20 +02:00
$selects [] = $select ;
$selects [ count ( $selects ) - 1 ][ 'where' ][] = " $this->user_table .cal_recur_date=cal_start " ;
}
2010-04-15 14:52:35 +02:00
}
2011-04-05 17:32:20 +02:00
if ( is_numeric ( $offset ) && ! $params [ 'no_total' ]) // get the total too
2006-03-29 09:01:18 +02:00
{
2011-04-05 17:32:20 +02:00
$save_selects = $selects ;
2006-03-29 09:01:18 +02:00
// we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns)
2010-04-23 08:52:48 +02:00
foreach ( array_keys ( $selects ) as $key )
2010-04-16 09:55:43 +02:00
{
2012-09-19 12:27:28 +02:00
$selects [ $key ][ 'cols' ] = " DISTINCT $this->repeats_table .recur_type,range_end AS recur_enddate, $this->repeats_table .recur_interval, $this->repeats_table .recur_data, " . $this -> db -> to_varchar ( $this -> cal_table . '.cal_id' ) . " ,cal_start,cal_end, $this->user_table .cal_recur_date " ;
2011-04-05 17:32:20 +02:00
if ( ! $params [ 'enum_recuring' ])
{
$selects [ $key ][ 'cols' ] = str_replace ( 'cal_start' , 'MIN(cal_start) AS cal_start' , $selects [ $key ][ 'cols' ]);
}
2010-04-16 09:55:43 +02:00
}
2010-06-01 11:28:37 +02:00
if ( ! isset ( $param [ 'cols' ])) self :: get_union_selects ( $selects , $start , $end , $users , $cat_id , $filter , $params [ 'query' ], $params [ 'users' ]);
2006-03-03 13:03:33 +01:00
2008-03-15 15:10:20 +01:00
$this -> total = $this -> db -> union ( $selects , __LINE__ , __FILE__ ) -> NumRows ();
2005-11-09 00:15:14 +01:00
2011-04-05 17:32:20 +02:00
// restore original cols / selects
$selects = $save_selects ; unset ( $save_selects );
2006-03-29 09:01:18 +02:00
}
2010-06-01 11:28:37 +02:00
if ( ! isset ( $param [ 'cols' ])) self :: get_union_selects ( $selects , $start , $end , $users , $cat_id , $filter , $params [ 'query' ], $params [ 'users' ]);
2010-10-28 11:22:01 +02:00
2010-05-20 17:12:59 +02:00
$rs = $this -> db -> union ( $selects , __LINE__ , __FILE__ , $params [ 'order' ], $offset , $num_rows );
2006-03-29 09:01:18 +02:00
}
else // MsSQL oder MySQL 3.23
2005-11-09 00:15:14 +01:00
{
2012-09-18 10:02:56 +02:00
$where [] = " (recur_type IS NULL AND $this->user_table .cal_recur_date=0) " ; // OR $this->user_table.cal_recur_date=cal_start)";
2006-03-29 09:01:18 +02:00
//_debug_array($where);
if ( is_numeric ( $offset )) // get the total too
{
// we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns)
2012-09-18 10:02:56 +02:00
$this -> total = $this -> db -> select ( $this -> cal_table , " DISTINCT " . $cols , //$this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date",
2008-03-15 15:10:20 +01:00
$where , __LINE__ , __FILE__ , false , '' , 'calendar' , 0 ,
2012-09-18 10:02:56 +02:00
// dates table join only needed to enum recuring events
( $params [ 'enum_recuring' ] ? " JOIN $this->dates_table ON $this->cal_table .cal_id= $this->dates_table .cal_id " : '' ) .
" JOIN $this->user_table ON $this->cal_table .cal_id= $this->user_table .cal_id $rejected_by_user_join LEFT JOIN $this->repeats_table ON $this->cal_table .cal_id= $this->repeats_table .cal_id " ) -> NumRows ();
2006-03-29 09:01:18 +02:00
}
2009-05-05 00:39:27 +02:00
$rs = $this -> db -> select ( $this -> cal_table ,( $this -> db -> capabilities [ 'distinct_on_text' ] ? 'DISTINCT ' : '' ) . $cols ,
2010-05-20 17:12:59 +02:00
$where , __LINE__ , __FILE__ , $offset , $params [ 'append' ] . ' ORDER BY ' . $params [ 'order' ], 'calendar' , $num_rows ,
2011-04-05 17:32:20 +02:00
" JOIN $this->dates_table ON $this->cal_table .cal_id= $this->dates_table .cal_id JOIN $this->user_table ON $this->cal_table .cal_id= $this->user_table .cal_id $rejected_by_user_join LEFT JOIN $this->repeats_table ON $this->cal_table .cal_id= $this->repeats_table .cal_id " );
2006-03-02 08:02:33 +01:00
}
2011-04-09 16:41:15 +02:00
//error_log(__METHOD__."() useUnionQuery=$useUnionQuery --> query took ".(microtime(true)-$starttime));
2010-06-01 11:28:37 +02:00
if ( isset ( $params [ 'cols' ]))
2009-05-05 00:39:27 +02:00
{
return $rs ; // if colums are specified we return the recordset / iterator
}
2006-03-29 09:01:18 +02:00
$events = $ids = $recur_dates = $recur_ids = array ();
2008-03-15 15:10:20 +01:00
foreach ( $rs as $row )
2005-11-09 00:15:14 +01:00
{
2010-01-19 23:20:44 +01:00
$id = $row [ 'cal_id' ];
if ( is_numeric ( $id )) $ids [] = $id ;
2008-03-20 13:49:34 +01:00
if ( $row [ 'cal_recur_date' ])
2005-11-09 00:15:14 +01:00
{
2008-03-20 13:49:34 +01:00
$id .= '-' . $row [ 'cal_recur_date' ];
$recur_dates [] = $row [ 'cal_recur_date' ];
2005-11-09 00:15:14 +01:00
}
2010-01-21 04:00:53 +01:00
if ( $row [ 'participants' ])
2010-02-01 11:35:05 +01:00
{
$row [ 'participants' ] = explode ( ',' , $row [ 'participants' ]);
$row [ 'participants' ] = array_combine ( $row [ 'participants' ],
2012-09-18 10:02:56 +02:00
array_fill ( 0 , count ( $row [ 'participants' ]), '' ));
2010-02-01 11:35:05 +01:00
}
else
{
$row [ 'participants' ] = array ();
}
2012-08-11 12:01:02 +02:00
$row [ 'recur_exception' ] = $row [ 'alarm' ] = array ();
2005-11-09 00:15:14 +01:00
2011-08-03 14:35:42 +02:00
// compile a list of recurrences per cal_id
if ( ! in_array ( $id ,( array ) $recur_ids [ $row [ 'cal_id' ]])) $recur_ids [ $row [ 'cal_id' ]][] = $id ;
2008-03-15 15:10:20 +01:00
$events [ $id ] = egw_db :: strip_array_keys ( $row , 'cal_' );
2005-11-09 00:15:14 +01:00
}
2010-01-19 23:20:44 +01:00
//_debug_array($events);
2010-02-01 11:35:05 +01:00
if ( count ( $ids ))
2005-11-09 00:15:14 +01:00
{
2012-03-12 09:20:36 +01:00
$ids = array_unique ( $ids );
2005-11-09 00:15:14 +01:00
// now ready all users with the given cal_id AND (cal_recur_date=0 or the fitting recur-date)
// This will always read the first entry of each recuring event too, we eliminate it later
$recur_dates [] = 0 ;
2012-09-26 16:30:47 +02:00
$utcal_id_view = " (SELECT * FROM " . $this -> user_table . " WHERE cal_id IN ( " . implode ( ',' , $ids ) . " ) " .
( $filter != 'everything' ? " AND cal_status!='X' " : '' ) . " ) utcalid " ;
2009-03-16 14:50:03 +01:00
//$utrecurdate_view = " (select * from ".$this->user_table." where cal_recur_date in (".implode(',',array_unique($recur_dates)).")) utrecurdates ";
foreach ( $this -> db -> select ( $utcal_id_view , '*' , array (
//'cal_id' => array_unique($ids),
'cal_recur_date' => $recur_dates ,
2011-04-05 17:32:20 +02:00
), __LINE__ , __FILE__ , false , 'ORDER BY cal_id,cal_user_type DESC,' . self :: STATUS_SORT , 'calendar' , $num_rows , $join = '' ,
2009-03-16 14:50:03 +01:00
$this -> db -> get_table_definitions ( 'calendar' , $this -> user_table )) as $row ) // DESC puts users before resources and contacts
2005-11-09 00:15:14 +01:00
{
$id = $row [ 'cal_id' ];
if ( $row [ 'cal_recur_date' ]) $id .= '-' . $row [ 'cal_recur_date' ];
2006-03-29 09:01:18 +02:00
2009-08-06 13:29:05 +02:00
// combine all participant data in uid and status values
2011-08-03 14:35:42 +02:00
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
$status = self :: combine_status ( $row [ 'cal_status' ], $row [ 'cal_quantity' ], $row [ 'cal_role' ]);
// set accept/reject/tentative of series for all recurrences
if ( ! $row [ 'cal_recur_date' ])
{
foreach (( array ) $recur_ids [ $row [ 'cal_id' ]] as $i )
{
if ( isset ( $events [ $i ]) && ! isset ( $events [ $i ][ 'participants' ][ $uid ]))
{
$events [ $i ][ 'participants' ][ $uid ] = $status ;
}
}
}
// set data, if recurrence is requested
if ( isset ( $events [ $id ])) $events [ $id ][ 'participants' ][ $uid ] = $status ;
2012-03-12 09:20:36 +01:00
}
2012-08-11 12:01:02 +02:00
// query recurrance exceptions, if needed
if ( ! $params [ 'enum_recuring' ])
{
foreach ( $this -> db -> select ( $this -> dates_table , 'cal_id,cal_start' , array (
'cal_id' => $ids ,
'recur_exception' => true ,
), __LINE__ , __FILE__ , false , 'ORDER BY cal_id,cal_start' , 'calendar' ) as $row )
{
2012-10-07 19:10:36 +02:00
$events [ $row [ 'cal_id' ]][ 'recur_exception' ][] = $row [ 'cal_start' ];
2012-08-11 12:01:02 +02:00
}
}
2009-10-03 09:32:05 +02:00
//custom fields are not shown in the regular views, so we only query them, if explicitly required
2010-05-20 17:12:59 +02:00
if ( ! is_null ( $params [ 'cfs' ]))
2005-11-09 00:15:14 +01:00
{
2009-11-03 17:13:45 +01:00
$where = array ( 'cal_id' => $ids );
2010-05-20 17:12:59 +02:00
if ( $params [ 'cfs' ]) $where [ 'cal_extra_name' ] = $params [ 'cfs' ];
2009-11-03 17:13:45 +01:00
foreach ( $this -> db -> select ( $this -> extra_table , '*' , $where ,
2009-10-03 09:32:05 +02:00
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
2005-11-09 00:15:14 +01:00
{
2009-10-03 09:32:05 +02:00
foreach (( array ) $recur_ids [ $row [ 'cal_id' ]] as $id )
2005-11-09 00:15:14 +01:00
{
2009-10-03 09:32:05 +02:00
if ( isset ( $events [ $id ]))
{
$events [ $id ][ '#' . $row [ 'cal_extra_name' ]] = $row [ 'cal_extra_value' ];
}
2005-11-09 00:15:14 +01:00
}
}
}
// alarms, atm. we read all alarms in the system, as this can be done in a single query
foreach (( array ) $this -> async -> read ( 'cal' . ( is_array ( $ids ) ? '' : ':' . ( int ) $ids ) . ':%' ) as $id => $job )
{
2006-03-07 20:03:42 +01:00
list (, $cal_id ) = explode ( ':' , $id );
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
$alarm = $job [ 'data' ]; // text, enabled
$alarm [ 'id' ] = $id ;
$alarm [ 'time' ] = $job [ 'next' ];
2006-03-29 09:01:18 +02:00
2006-03-07 20:03:42 +01:00
$event_start = $alarm [ 'time' ] + $alarm [ 'offset' ];
2006-03-29 09:01:18 +02:00
2006-03-07 20:03:42 +01:00
if ( isset ( $events [ $cal_id ])) // none recuring event
{
$events [ $cal_id ][ 'alarm' ][ $id ] = $alarm ;
}
elseif ( isset ( $events [ $cal_id . '-' . $event_start ])) // recuring event
{
$events [ $cal_id . '-' . $event_start ][ 'alarm' ][ $id ] = $alarm ;
2006-03-29 09:01:18 +02:00
}
2005-11-09 00:15:14 +01:00
}
}
//echo "<p>socal::search\n"; _debug_array($events);
2011-04-05 17:32:20 +02:00
//error_log(__METHOD__."(,filter=".array2string($params['query']).",offset=$offset, num_rows=$num_rows) returning ".count($events)." entries".($offset!==false?" total=$this->total":'').' '.function_backtrace());
2005-11-09 00:15:14 +01:00
return $events ;
}
2010-01-29 22:42:54 +01:00
2010-01-22 00:36:05 +01:00
/**
* Data returned by calendar_search_union hook
*/
private static $integration_data ;
2006-03-29 09:01:18 +02:00
2010-01-19 23:20:44 +01:00
/**
* Ask other apps if they want to participate in calendar search / display
*
* @ param & $selects parts of union query
* @ param $start see search ()
* @ param $end
2010-05-20 17:12:59 +02:00
* @ param $users as used in calendar_so ( $users_raw plus all members and memberships added by calendar_bo )
2010-01-19 23:20:44 +01:00
* @ param $cat_id
* @ param $filter
* @ param $query
2010-05-20 17:12:59 +02:00
* @ param $users_raw as passed to calendar_bo :: search ( no members and memberships added )
2010-01-19 23:20:44 +01:00
*/
2010-05-20 17:12:59 +02:00
private static function get_union_selects ( array & $selects , $start , $end , $users , $cat_id , $filter , $query , $users_raw )
2010-01-19 23:20:44 +01:00
{
2011-04-05 17:32:20 +02:00
if ( in_array ( basename ( $_SERVER [ 'SCRIPT_FILENAME' ]), array ( 'groupdav.php' , 'rpc.php' , 'xmlrpc.php' , '/activesync/index.php' )) ||
2010-05-21 17:05:31 +02:00
! in_array ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ], array ( 'calendar' , 'home' )))
2010-01-24 04:50:40 +01:00
{
2010-01-29 22:42:54 +01:00
return ; // disable integration for GroupDAV, SyncML, ...
2010-01-24 04:50:40 +01:00
}
2010-01-22 00:36:05 +01:00
self :: $integration_data = $GLOBALS [ 'egw' ] -> hooks -> process ( array (
2010-01-19 23:20:44 +01:00
'location' => 'calendar_search_union' ,
2010-01-29 22:42:54 +01:00
'cols' => $selects [ 0 ][ 'cols' ], // cols to return
2010-01-19 23:20:44 +01:00
'start' => $start ,
'end' => $end ,
'users' => $users ,
2010-05-20 17:12:59 +02:00
'users_raw' => $users_raw ,
2010-01-19 23:20:44 +01:00
'cat_id' => $cat_id ,
'filter' => $filter ,
'query' => $query ,
));
2010-01-22 00:36:05 +01:00
foreach ( self :: $integration_data as $app => $data )
2010-01-19 23:20:44 +01:00
{
if ( is_array ( $data [ 'selects' ]))
{
2010-01-22 00:36:05 +01:00
//echo $app; _debug_array($data);
2010-01-19 23:20:44 +01:00
$selects = array_merge ( $selects , $data [ 'selects' ]);
}
}
}
2010-01-29 22:42:54 +01:00
2010-01-22 00:36:05 +01:00
/**
* Get data from last 'calendar_search_union' hook call
2010-01-29 22:42:54 +01:00
*
2010-01-22 00:36:05 +01:00
* @ return array
*/
public static function get_integration_data ()
{
return self :: $integration_data ;
}
2010-01-29 22:42:54 +01:00
2010-04-23 08:52:48 +02:00
/**
* Return union cols constructed from application cols and required cols
*
* Every col not supplied in $app_cols get returned as NULL .
*
* @ param array $app_cols required name => own name pairs
* @ param string | array $required array or comma separated column names or table .*
* @ param string $required_app = 'calendar'
* @ return string cols for union query to match ones supplied in $required
*/
public static function union_cols ( array $app_cols , $required , $required_app = 'calendar' )
{
// remove evtl. used DISTINCT, we currently dont need it
if (( $distinct = substr ( $required , 0 , 9 ) == 'DISTINCT ' ))
{
$required = substr ( $required , 9 );
}
$return_cols = array ();
foreach ( is_array ( $required ) ? $required : explode ( ',' , $required ) as $cols )
{
if ( substr ( $cols , - 2 ) == '.*' )
{
$cols = self :: get_columns ( $required_app , substr ( $cols , 0 , - 2 ));
}
elseif ( strpos ( $cols , ' AS ' ) !== false )
{
list (, $cols ) = explode ( ' AS ' , $cols );
}
foreach (( array ) $cols as $col )
{
if ( substr ( $col , 0 , 7 ) == 'egw_cal' ) // remove table name
{
2010-04-27 20:35:14 +02:00
$col = preg_replace ( '/^egw_cal[a-z_]*\./' , '' , $col );
2010-04-23 08:52:48 +02:00
}
if ( isset ( $app_cols [ $col ]))
{
$return_cols [] = $app_cols [ $col ];
}
else
{
$return_cols [] = 'NULL' ;
}
}
}
return implode ( ',' , $return_cols );
}
/**
* Get columns of given table , taking into account historically different column order of egw_cal table
*
* @ param string $app
* @ param string $table
* @ return array of column names
*/
static private function get_columns ( $app , $table )
{
if ( $table != 'egw_cal' )
{
$table_def = $GLOBALS [ 'egw' ] -> db -> get_table_definitions ( $app , $table );
$cols = array_keys ( $table_def [ 'fd' ]);
}
else
{
// special handling for egw_cal, as old databases have a different column order!!!
$cols =& egw_cache :: getSession ( __CLASS__ , $table );
if ( is_null ( $cols ))
{
$meta = $GLOBALS [ 'egw' ] -> db -> metadata ( $table , true );
$cols = array_keys ( $meta [ 'meta' ]);
}
}
return $cols ;
}
2005-11-09 00:15:14 +01:00
/**
* Checks for conflicts
*/
/* folowing SQL checks for conflicts completly on DB level
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
SELECT cal_user_type , cal_user_id , SUM ( cal_quantity )
FROM egw_cal , egw_cal_dates , egw_cal_user
LEFT JOIN egw_cal_repeats ON egw_cal . cal_id = egw_cal_repeats . cal_id
WHERE egw_cal . cal_id = egw_cal_dates . cal_id
AND egw_cal . cal_id = egw_cal_user . cal_id
AND (
recur_type IS NULL
AND cal_recur_date = 0
OR cal_recur_date = cal_start
)
AND (
(
cal_user_type = 'u' # user of the checked event
AND cal_user_id
IN ( 7 , 5 )
)
AND 1118822400 < cal_end # start- and end-time of the checked event
AND cal_start < 1118833200
)
AND egw_cal . cal_id != 26 # id of the checked event
AND cal_non_blocking != 1
AND cal_status != 'R'
GROUP BY cal_user_type , cal_user_id
ORDER BY cal_user_type , cal_usre_id
2006-03-29 09:01:18 +02:00
*/
2005-11-09 00:15:14 +01:00
/**
* Saves or creates an event
*
* We always set cal_modified and cal_modifier and for new events cal_uid .
* All other column are only written if they are set in the $event parameter !
*
* @ param array $event
* @ param boolean & $set_recurrences on return : true if the recurrences need to be written , false otherwise
2009-08-10 11:24:39 +02:00
* @ param int & $set_recurrences_start = 0 on return : time from which on the recurrences should be rebuilt , default 0 = all
2005-11-09 00:15:14 +01:00
* @ param int $change_since = 0 time from which on the repetitions should be changed , default 0 = all
2008-05-08 00:12:25 +02:00
* @ param int & $etag etag = null etag to check or null , on return new etag
2009-05-05 00:39:27 +02:00
* @ return boolean | int false on error , 0 if etag does not match , cal_id otherwise
2005-11-09 00:15:14 +01:00
*/
2009-08-10 11:24:39 +02:00
function save ( $event , & $set_recurrences , & $set_recurrences_start = 0 , $change_since = 0 , & $etag = null )
2005-11-09 00:15:14 +01:00
{
2009-07-23 18:14:22 +02:00
if ( isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'syncml' ][ 'minimum_uid_length' ]))
{
2009-07-15 22:35:56 +02:00
$minimum_uid_length = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'syncml' ][ 'minimum_uid_length' ];
2011-12-22 15:03:12 +01:00
if ( empty ( $minimum_uid_length ) || $minimum_uid_length <= 1 ) $minimum_uid_length = 8 ; // we just do not accept no uid, or uid way to short!
2009-07-23 18:14:22 +02:00
}
else
{
2009-07-15 22:35:56 +02:00
$minimum_uid_length = 8 ;
}
2010-02-17 14:29:28 +01:00
$old_min = $old_duration = 0 ;
2009-08-07 12:20:58 +02:00
//error_log(__METHOD__.'('.array2string($event).",$set_recurrences,$change_since,$etag)");
2005-11-09 00:15:14 +01:00
$cal_id = ( int ) $event [ 'id' ];
unset ( $event [ 'id' ]);
$set_recurrences = ! $cal_id && $event [ 'recur_type' ] != MCAL_RECUR_NONE ;
2006-03-29 09:01:18 +02:00
2009-11-27 07:46:32 +01:00
if ( $event [ 'recur_type' ] != MCAL_RECUR_NONE &&
! ( int ) $event [ 'recur_interval' ])
{
$event [ 'recur_interval' ] = 1 ;
}
2005-11-09 00:15:14 +01:00
// add colum prefix 'cal_' if there's not already a 'recur_' prefix
foreach ( $event as $col => $val )
{
2012-09-18 10:02:56 +02:00
if ( $col [ 0 ] != '#' && substr ( $col , 0 , 6 ) != 'recur_' && substr ( $col , 0 , 6 ) != 'range_' && $col != 'alarm' && $col != 'tz_id' && $col != 'caldav_name' )
2005-11-09 00:15:14 +01:00
{
$event [ 'cal_' . $col ] = $val ;
unset ( $event [ $col ]);
}
}
2012-10-02 18:43:49 +02:00
// set range_start/_end, but only if we have cal_start/_end, as otherwise we destroy present values!
if ( isset ( $event [ 'cal_start' ])) $event [ 'range_start' ] = $event [ 'cal_start' ];
if ( isset ( $event [ 'cal_end' ]))
{
$event [ 'range_end' ] = $event [ 'recur_type' ] == MCAL_RECUR_NONE ? $event [ 'cal_end' ] :
( $event [ 'recur_enddate' ] ? $event [ 'recur_enddate' ] : null );
}
2010-01-29 22:42:54 +01:00
// ensure that we find mathing entries later on
if ( ! is_array ( $event [ 'cal_category' ]))
{
$categories = array_unique ( explode ( ',' , $event [ 'cal_category' ]));
sort ( $categories );
}
else
{
$categories = array_unique ( $event [ 'cal_category' ]);
}
sort ( $categories , SORT_NUMERIC );
$event [ 'cal_category' ] = implode ( ',' , $categories );
2005-11-09 00:15:14 +01:00
2008-05-08 00:12:25 +02:00
if ( $cal_id )
2008-01-15 09:21:25 +01:00
{
2012-10-23 16:55:41 +02:00
// query old recurrance information, before updating main table, where recur_endate is now stored
if ( $event [ 'recur_type' ] != MCAL_RECUR_NONE )
{
$old_repeats = $this -> db -> select ( $this -> repeats_table , " $this->repeats_table .*,range_end AS recur_enddate " ,
" $this->repeats_table .cal_id= " . ( int ) $cal_id , __LINE__ , __FILE__ ,
false , '' , 'calendar' , 0 , " JOIN $this->cal_table ON $this->repeats_table .cal_id= $this->cal_table .cal_id " ) -> fetch ();
}
2008-05-08 00:12:25 +02:00
$where = array ( 'cal_id' => $cal_id );
2009-11-12 20:11:27 +01:00
// read only timezone id, to check if it is changed
if ( $event [ 'recur_type' ] != MCAL_RECUR_NONE )
{
$old_tz_id = $this -> db -> select ( $this -> cal_table , 'tz_id' , $where , __LINE__ , __FILE__ , 'calendar' ) -> fetchColumn ();
}
2008-05-08 00:12:25 +02:00
if ( ! is_null ( $etag )) $where [ 'cal_etag' ] = $etag ;
unset ( $event [ 'cal_etag' ]);
$event [] = 'cal_etag=cal_etag+1' ; // always update the etag, even if none given to check
$this -> db -> update ( $this -> cal_table , $event , $where , __LINE__ , __FILE__ , 'calendar' );
if ( ! is_null ( $etag ) && $this -> db -> affected_rows () < 1 )
2008-01-15 09:21:25 +01:00
{
2008-05-08 00:12:25 +02:00
return 0 ; // wrong etag, someone else updated the entry
2008-01-15 09:21:25 +01:00
}
2008-05-08 00:12:25 +02:00
if ( ! is_null ( $etag )) ++ $etag ;
2005-11-09 00:15:14 +01:00
}
else
{
2009-07-15 22:35:56 +02:00
// new event
2005-11-09 00:15:14 +01:00
if ( ! $event [ 'cal_owner' ]) $event [ 'cal_owner' ] = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ];
if ( ! $event [ 'cal_id' ] && ! isset ( $event [ 'cal_uid' ])) $event [ 'cal_uid' ] = '' ; // uid is NOT NULL!
2008-03-17 10:06:08 +01:00
$this -> db -> insert ( $this -> cal_table , $event , false , __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
if ( ! ( $cal_id = $this -> db -> get_last_insert_id ( $this -> cal_table , 'cal_id' )))
{
return false ;
}
2008-05-08 00:12:25 +02:00
$etag = 0 ;
2009-07-15 22:35:56 +02:00
}
2011-04-06 21:26:10 +02:00
$update = array ();
// event without uid or not strong enough uid
2009-07-23 18:14:22 +02:00
if ( ! isset ( $event [ 'cal_uid' ]) || strlen ( $event [ 'cal_uid' ]) < $minimum_uid_length )
{
2012-08-11 12:01:02 +02:00
$update [ 'cal_uid' ] = $event [ 'cal_uid' ] = common :: generate_uid ( 'calendar' , $cal_id );
2011-04-06 21:26:10 +02:00
}
// set caldav_name, if not given by caller
if ( empty ( $event [ 'caldav_name' ]) && version_compare ( $GLOBALS [ 'egw_info' ][ 'apps' ][ 'calendar' ][ 'version' ], '1.9.003' , '>=' ))
{
$update [ 'caldav_name' ] = $event [ 'caldav_name' ] = $cal_id . '.ics' ;
}
if ( $update )
{
$this -> db -> update ( $this -> cal_table , $update , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
2010-10-28 11:22:01 +02:00
2010-06-26 17:58:33 +02:00
if ( $event [ 'recur_type' ] == MCAL_RECUR_NONE )
{
$this -> db -> delete ( $this -> dates_table , array (
'cal_id' => $cal_id ),
__LINE__ , __FILE__ , 'calendar' );
2010-10-28 11:22:01 +02:00
2010-06-26 17:58:33 +02:00
// delete all user-records, with recur-date != 0
$this -> db -> delete ( $this -> user_table , array (
'cal_id' => $cal_id , 'cal_recur_date != 0' ),
__LINE__ , __FILE__ , 'calendar' );
2010-10-28 11:22:01 +02:00
2010-06-26 17:58:33 +02:00
$this -> db -> delete ( $this -> repeats_table , array (
'cal_id' => $cal_id ),
__LINE__ , __FILE__ , 'calendar' );
}
else // write information about recuring event, if recur_type is present in the array
2005-11-09 00:15:14 +01:00
{
2009-07-17 19:16:34 +02:00
// fetch information about the currently saved (old) event
$old_min = ( int ) $this -> db -> select ( $this -> dates_table , 'MIN(cal_start)' , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , false , '' , 'calendar' ) -> fetchColumn ();
$old_duration = ( int ) $this -> db -> select ( $this -> dates_table , 'MIN(cal_end)' , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , false , '' , 'calendar' ) -> fetchColumn () - $old_min ;
2012-08-11 12:01:02 +02:00
$old_exceptions = array ();
foreach ( $this -> db -> select ( $this -> dates_table , 'cal_start' , array (
'cal_id' => $cal_id ,
'recur_exception' => true
), __LINE__ , __FILE__ , false , 'ORDER BY cal_start' , 'calendar' ) as $row )
2010-03-07 00:06:43 +01:00
{
2012-08-11 12:01:02 +02:00
$old_exceptions [] = $row [ 'cal_start' ];
2010-03-07 00:06:43 +01:00
}
2009-07-15 22:35:56 +02:00
2009-08-10 11:24:39 +02:00
$event [ 'recur_exception' ] = is_array ( $event [ 'recur_exception' ]) ? $event [ 'recur_exception' ] : array ();
2010-03-07 00:06:43 +01:00
if ( ! empty ( $event [ 'recur_exception' ]))
{
sort ( $event [ 'recur_exception' ]);
}
2012-08-11 12:01:02 +02:00
$where = array (
'cal_id' => $cal_id ,
'cal_recur_date' => 0 ,
);
2010-03-07 00:06:43 +01:00
$old_participants = array ();
foreach ( $this -> db -> select ( $this -> user_table , 'cal_user_type,cal_user_id,cal_status,cal_quantity,cal_role' , $where ,
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
$status = self :: combine_status ( $row [ 'cal_status' ], $row [ 'cal_quantity' ], $row [ 'cal_role' ]);
$old_participants [ $uid ] = $status ;
}
2009-08-10 11:24:39 +02:00
// re-check: did so much recurrence data change that we have to rebuild it from scratch?
if ( ! $set_recurrences )
2005-11-09 00:15:14 +01:00
{
2009-08-10 11:24:39 +02:00
$set_recurrences = ( isset ( $event [ 'cal_start' ]) && ( int ) $old_min != ( int ) $event [ 'cal_start' ]) ||
$event [ 'recur_type' ] != $old_repeats [ 'recur_type' ] || $event [ 'recur_data' ] != $old_repeats [ 'recur_data' ] ||
2009-11-12 20:11:27 +01:00
( int ) $event [ 'recur_interval' ] != ( int ) $old_repeats [ 'recur_interval' ] || $event [ 'tz_id' ] != $old_tz_id ;
2005-11-09 00:15:14 +01:00
}
2009-08-10 11:24:39 +02:00
if ( $set_recurrences )
2005-11-09 00:15:14 +01:00
{
2009-08-10 11:24:39 +02:00
// too much recurrence data has changed, we have to do a rebuild from scratch
// delete all, but the lowest dates record
$this -> db -> delete ( $this -> dates_table , array (
'cal_id' => $cal_id ,
'cal_start > ' . ( int ) $old_min ,
), __LINE__ , __FILE__ , 'calendar' );
2009-07-17 19:16:34 +02:00
2009-08-10 11:24:39 +02:00
// delete all user-records, with recur-date != 0
$this -> db -> delete ( $this -> user_table , array (
'cal_id' => $cal_id ,
'cal_recur_date != 0' ,
), __LINE__ , __FILE__ , 'calendar' );
}
else
2009-07-17 19:16:34 +02:00
{
2009-08-10 11:24:39 +02:00
// we adjust some possibly changed recurrences manually
// deleted exceptions: re-insert recurrences into the user and dates table
2010-03-07 00:06:43 +01:00
if ( count ( $deleted_exceptions = array_diff ( $old_exceptions , $event [ 'recur_exception' ])))
2009-07-17 19:16:34 +02:00
{
2010-03-07 00:06:43 +01:00
if ( isset ( $event [ 'cal_participants' ]))
{
$participants = $event [ 'cal_participants' ];
}
else
{
// use old default
$participants = $old_participants ;
}
2009-08-10 11:24:39 +02:00
foreach ( $deleted_exceptions as $id => $deleted_exception )
2009-07-17 19:16:34 +02:00
{
2009-08-10 11:24:39 +02:00
// rebuild participants for the re-inserted recurrence
$this -> recurrence ( $cal_id , $deleted_exception , $deleted_exception + $old_duration , $participants );
2009-07-17 19:16:34 +02:00
}
}
2009-08-10 11:24:39 +02:00
// check if recurrence enddate was adjusted
if ( isset ( $event [ 'recur_enddate' ]))
{
// recurrences need to be truncated
if (( int ) $event [ 'recur_enddate' ] > 0 &&
(( int ) $old_repeats [ 'recur_enddate' ] == 0 || ( int ) $old_repeats [ 'recur_enddate' ] > ( int ) $event [ 'recur_enddate' ])
)
{
2012-10-23 12:55:54 +02:00
$this -> db -> delete ( $this -> user_table , array ( 'cal_id' => $cal_id , 'cal_recur_date >= ' . ( $event [ 'recur_enddate' ] + 1 * DAY_s )), __LINE__ , __FILE__ , 'calendar' );
$this -> db -> delete ( $this -> dates_table , array ( 'cal_id' => $cal_id , 'cal_start >= ' . ( $event [ 'recur_enddate' ] + 1 * DAY_s )), __LINE__ , __FILE__ , 'calendar' );
2009-08-10 11:24:39 +02:00
}
// recurrences need to be expanded
if ((( int ) $event [ 'recur_enddate' ] == 0 && ( int ) $old_repeats [ 'recur_enddate' ] > 0 )
|| (( int ) $event [ 'recur_enddate' ] > 0 && ( int ) $old_repeats [ 'recur_enddate' ] > 0 && ( int ) $old_repeats [ 'recur_enddate' ] < ( int ) $event [ 'recur_enddate' ])
)
{
$set_recurrences = true ;
$set_recurrences_start = ( $old_repeats [ 'recur_enddate' ] + 1 * DAY_s );
}
2012-10-23 16:55:41 +02:00
//error_log(__METHOD__."() event[recur_enddate]=$event[recur_enddate], old_repeats[recur_enddate]=$old_repeats[recur_enddate] --> set_recurrences=".array2string($set_recurrences).", set_recurrences_start=$set_recurrences_start");
2009-08-10 11:24:39 +02:00
}
// truncate recurrences by given exceptions
if ( count ( $event [ 'recur_exception' ]))
{
2012-08-11 12:01:02 +02:00
// added and existing exceptions: delete the execeptions from the user table, it could be the first time
2009-08-10 11:24:39 +02:00
$this -> db -> delete ( $this -> user_table , array ( 'cal_id' => $cal_id , 'cal_recur_date' => $event [ 'recur_exception' ]), __LINE__ , __FILE__ , 'calendar' );
2012-08-11 12:01:02 +02:00
// update recur_exception flag based on current exceptions
$this -> db -> update ( $this -> dates_table , 'recur_exception=' . $this -> db -> expression ( $this -> dates_table , array (
'cal_start' => $event [ 'recur_exception' ],
)), array (
'cal_id' => $cal_id ,
), __LINE__ , __FILE__ , 'calendar' );
2009-08-10 11:24:39 +02:00
}
2005-11-09 00:15:14 +01:00
}
2009-07-23 18:14:22 +02:00
2009-08-10 11:24:39 +02:00
// write the repeats table
2008-05-16 13:02:46 +02:00
unset ( $event [ 0 ]); // unset the 'etag=etag+1', as it's not in the repeats table
2010-06-26 17:58:33 +02:00
$this -> db -> insert ( $this -> repeats_table , $event , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
// update start- and endtime if present in the event-array, evtl. we need to move all recurrences
if ( isset ( $event [ 'cal_start' ]) && isset ( $event [ 'cal_end' ]))
{
2010-02-17 14:29:28 +01:00
$this -> move ( $cal_id , $event [ 'cal_start' ], $event [ 'cal_end' ], ! $cal_id ? false : $change_since , $old_min , $old_min + $old_duration );
2005-11-09 00:15:14 +01:00
}
// update participants if present in the event-array
if ( isset ( $event [ 'cal_participants' ]))
{
$this -> participants ( $cal_id , $event [ 'cal_participants' ], ! $cal_id ? false : $change_since );
}
// Custom fields
foreach ( $event as $name => $value )
{
if ( $name [ 0 ] == '#' )
{
2011-08-03 14:35:42 +02:00
if ( is_array ( $value ) && array_key_exists ( 'id' , $value ))
2011-05-12 17:03:26 +02:00
{
//error_log(__METHOD__.__LINE__."$name => ".array2string($value).function_backtrace());
$value = $value [ 'id' ];
//error_log(__METHOD__.__LINE__."$name => ".array2string($value));
}
2009-09-27 09:59:01 +02:00
if ( $value )
2005-11-09 00:15:14 +01:00
{
$this -> db -> insert ( $this -> extra_table , array (
2009-09-27 09:59:01 +02:00
'cal_extra_value' => is_array ( $value ) ? implode ( ',' , $value ) : $value ,
2005-11-09 00:15:14 +01:00
), array (
'cal_id' => $cal_id ,
'cal_extra_name' => substr ( $name , 1 ),
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
else
{
$this -> db -> delete ( $this -> extra_table , array (
'cal_id' => $cal_id ,
'cal_extra_name' => substr ( $name , 1 ),
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2006-03-29 09:01:18 +02:00
}
2005-11-09 00:15:14 +01:00
}
}
2010-06-08 18:36:55 +02:00
// updating or saving the alarms; new alarms have a temporary numeric id!
2005-11-09 00:15:14 +01:00
if ( is_array ( $event [ 'alarm' ]))
{
foreach ( $event [ 'alarm' ] as $id => $alarm )
{
2010-06-08 18:36:55 +02:00
if ( is_numeric ( $id )) unset ( $alarm [ 'id' ]); // unset the temporary id to add the alarm
2005-11-09 00:15:14 +01:00
2008-11-17 20:06:27 +01:00
if ( ! isset ( $alarm [ 'offset' ]))
{
$alarm [ 'offset' ] = $event [ 'cal_start' ] - $alarm [ 'time' ];
}
elseif ( ! isset ( $alarm [ 'time' ]))
{
$alarm [ 'time' ] = $event [ 'cal_start' ] - $alarm [ 'offset' ];
}
2010-10-28 11:22:01 +02:00
2012-07-18 08:45:09 +02:00
if ( $alarm [ 'time' ] < time () && ! self :: shift_alarm ( $event , $alarm ))
2008-11-17 20:06:27 +01:00
{
2012-07-18 08:45:09 +02:00
continue ; // pgoerzen: don't add alarm in the past
2010-06-08 18:36:55 +02:00
}
2005-11-09 00:15:14 +01:00
$this -> save_alarm ( $cal_id , $alarm );
}
}
2008-05-08 00:12:25 +02:00
if ( is_null ( $etag ))
{
2009-06-08 18:21:14 +02:00
$etag = $this -> db -> select ( $this -> cal_table , 'cal_etag' , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , false , '' , 'calendar' ) -> fetchColumn ();
2008-05-08 00:12:25 +02:00
}
2012-09-28 09:53:14 +02:00
// if event is an exception: update modified of master, to force etag, ctag and sync-token change
if ( $event [ 'cal_reference' ])
{
$this -> updateModified ( $event [ 'cal_reference' ]);
}
2005-11-09 00:15:14 +01:00
return $cal_id ;
}
2006-03-29 09:01:18 +02:00
2012-07-18 08:45:09 +02:00
/**
* Shift alarm on recurring events to next future recurrence
*
* @ param array $event event with optional 'cal_' prefix in keys
* @ param array & $alarm
* @ return boolean true if alarm could be shifted , false if not
*/
public static function shift_alarm ( array $event , array & $alarm )
{
if ( $event [ 'recur_type' ] == MCAL_RECUR_NONE )
{
return false ;
}
$start = ( int ) time () + $alarm [ 'offset' ];
2012-07-18 11:13:59 +02:00
$event = egw_db :: strip_array_keys ( $event , 'cal_' );
2012-07-18 08:45:09 +02:00
$rrule = calendar_rrule :: event2rrule ( $event , false );
foreach ( $rrule as $time )
{
if ( $start < ( $ts = egw_time :: to ( $time , 'server' )))
{
$alarm [ 'time' ] = $ts - $alarm [ 'offset' ];
return true ;
}
}
return false ;
}
2005-11-09 00:15:14 +01:00
/**
* moves an event to an other start - and end - time taken into account the evtl . recurrences of the event ( ! )
*
* @ param int $cal_id
* @ param int $start new starttime
* @ param int $end new endtime
2009-05-05 00:39:27 +02:00
* @ param int | boolean $change_since = 0 false = new entry , > 0 time from which on the repetitions should be changed , default 0 = all
2005-11-09 00:15:14 +01:00
* @ param int $old_start = 0 old starttime or ( default ) 0 , to query it from the db
* @ param int $old_end = 0 old starttime or ( default ) 0
2009-11-04 08:57:55 +01:00
* @ todo Recalculate recurrences , if timezone changes
2009-05-05 00:39:27 +02:00
* @ return int | boolean number of moved recurrences or false on error
2005-11-09 00:15:14 +01:00
*/
2009-11-04 16:00:08 +01:00
function move ( $cal_id , $start , $end , $change_since = 0 , $old_start = 0 , $old_end = 0 )
2005-11-09 00:15:14 +01:00
{
//echo "<p>socal::move($cal_id,$start,$end,$change_since,$old_start,$old_end)</p>\n";
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
if ( ! ( int ) $cal_id ) return false ;
if ( ! $old_start )
{
2008-03-15 15:10:20 +01:00
if ( $change_since !== false ) $row = $this -> db -> select ( $this -> dates_table , 'MIN(cal_start) AS cal_start,MIN(cal_end) AS cal_end' ,
array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , false , '' , 'calendar' ) -> fetch ();
2005-11-09 00:15:14 +01:00
// if no recurrence found, create one with the new dates
2008-03-15 15:10:20 +01:00
if ( $change_since === false || ! $row || ! $row [ 'cal_start' ] || ! $row [ 'cal_end' ])
2005-11-09 00:15:14 +01:00
{
$this -> db -> insert ( $this -> dates_table , array (
'cal_id' => $cal_id ,
'cal_start' => $start ,
'cal_end' => $end ,
2008-03-15 15:10:20 +01:00
), false , __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
return 1 ;
}
$move_start = ( int ) ( $start - $row [ 'cal_start' ]);
$move_end = ( int ) ( $end - $row [ 'cal_end' ]);
}
else
{
$move_start = ( int ) ( $start - $old_start );
$move_end = ( int ) ( $end - $old_end );
}
2008-03-15 15:10:20 +01:00
$where = 'cal_id=' . ( int ) $cal_id ;
2005-11-09 00:15:14 +01:00
if ( $move_start )
{
// move the recur-date of the participants
$this -> db -> query ( " UPDATE $this->user_table SET cal_recur_date=cal_recur_date+ $move_start WHERE $where AND cal_recur_date " .
2009-08-09 09:51:27 +02:00
(( int ) $change_since ? '>= ' . ( int ) $change_since : '!= 0' ), __LINE__ , __FILE__ );
2005-11-09 00:15:14 +01:00
}
if ( $move_start || $move_end )
{
// move the event and it's recurrences
2009-11-04 16:00:08 +01:00
$this -> db -> query ( " UPDATE $this->dates_table SET cal_start=cal_start+ $move_start ,cal_end=cal_end+ $move_end WHERE $where " .
2008-03-15 15:10:20 +01:00
(( int ) $change_since ? ' AND cal_start >= ' . ( int ) $change_since : '' ), __LINE__ , __FILE__ );
2005-11-09 00:15:14 +01:00
}
return $this -> db -> affected_rows ();
}
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
/**
* combines user_type and user_id into a single string or integer ( for users )
*
* @ param string $user_type 1 - char type : 'u' = user , ...
2008-05-08 17:02:35 +02:00
* @ param string | int $user_id id
* @ return string | int combined id
2005-11-09 00:15:14 +01:00
*/
2009-08-06 13:29:05 +02:00
static function combine_user ( $user_type , $user_id )
2005-11-09 00:15:14 +01:00
{
if ( ! $user_type || $user_type == 'u' )
{
return ( int ) $user_id ;
}
return $user_type . $user_id ;
}
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
/**
* splits the combined user_type and user_id into a single values
*
2008-05-08 17:02:35 +02:00
* @ param string | int $uid
* @ param string & $user_type 1 - char type : 'u' = user , ...
* @ param string | int & $user_id id
2005-11-09 00:15:14 +01:00
*/
2009-08-06 13:29:05 +02:00
static function split_user ( $uid , & $user_type , & $user_id )
2005-11-09 00:15:14 +01:00
{
if ( is_numeric ( $uid ))
{
$user_type = 'u' ;
$user_id = ( int ) $uid ;
}
else
{
$user_type = $uid [ 0 ];
2008-05-08 17:02:35 +02:00
$user_id = substr ( $uid , 1 );
2005-11-09 00:15:14 +01:00
}
}
2006-03-29 09:01:18 +02:00
2009-08-06 13:29:05 +02:00
/**
* Combine status , quantity and role into one value
*
* @ param string $status
2009-10-13 10:58:54 +02:00
* @ param int $quantity = 1
* @ param string $role = 'REQ-PARTICIPANT'
2009-08-06 13:29:05 +02:00
* @ return string
*/
2009-10-13 10:58:54 +02:00
static function combine_status ( $status , $quantity = 1 , $role = 'REQ-PARTICIPANT' )
2009-08-06 13:29:05 +02:00
{
if (( int ) $quantity > 1 ) $status .= ( int ) $quantity ;
if ( $role != 'REQ-PARTICIPANT' ) $status .= $role ;
return $status ;
}
/**
* splits the combined status , quantity and role
*
* @ param string & $status I : combined value , O : status letter : U , T , A , R
* @ param int & $quantity only O : quantity
* @ param string & $role only O : role
*/
static function split_status ( & $status , & $quantity , & $role )
{
$quantity = 1 ;
$role = 'REQ-PARTICIPANT' ;
if ( strlen ( $status ) > 1 && preg_match ( '/^.([0-9]*)(.*)$/' , $status , $matches ))
{
if (( int ) $matches [ 1 ] > 0 ) $quantity = ( int ) $matches [ 1 ];
2009-10-12 21:16:42 +02:00
if ( $matches [ 2 ]) $role = $matches [ 2 ];
2009-08-06 13:29:05 +02:00
$status = $status [ 0 ];
}
2009-11-26 19:36:19 +01:00
elseif ( $status === true )
{
$status = 'U' ;
}
2009-08-06 13:29:05 +02:00
}
2005-11-09 00:15:14 +01:00
/**
* updates the participants of an event , taken into account the evtl . recurrences of the event ( ! )
2009-08-17 16:45:42 +02:00
* this method just adds new participants or removes not longer set participants
2010-03-07 00:06:43 +01:00
* this method does never overwrite existing entries ( except the 0 - recurrence and for delete )
2006-03-29 09:01:18 +02:00
*
2005-11-09 00:15:14 +01:00
* @ param int $cal_id
2009-08-04 19:14:16 +02:00
* @ param array $participants uid => status pairs
2010-03-07 00:06:43 +01:00
* @ param int | boolean $change_since = 0 , false = new event ,
* 0 = all , > 0 time from which on the repetitions should be changed
2009-08-17 16:45:42 +02:00
* @ param boolean $add_only = false
* false = add AND delete participants if needed ( full list of participants required in $participants )
* true = only add participants if needed , no participant will be deleted ( participants to check / add required in $participants )
2009-05-05 00:39:27 +02:00
* @ return int | boolean number of updated recurrences or false on error
2005-11-09 00:15:14 +01:00
*/
2009-08-17 16:45:42 +02:00
function participants ( $cal_id , $participants , $change_since = 0 , $add_only = false )
2005-11-09 00:15:14 +01:00
{
2010-11-11 09:51:13 +01:00
//error_log(__METHOD__."($cal_id,".array2string($participants).",$change_since,$add_only");
2009-11-29 15:03:45 +01:00
$recurrences = array ();
2005-11-23 15:21:20 +01:00
// remove group-invitations, they are NOT stored in the db
foreach ( $participants as $uid => $status )
{
2009-08-06 13:29:05 +02:00
if ( $status [ 0 ] == 'G' )
2005-11-23 15:21:20 +01:00
{
unset ( $participants [ $uid ]);
}
}
2005-11-09 00:15:14 +01:00
$where = array ( 'cal_id' => $cal_id );
if (( int ) $change_since )
{
2011-01-06 06:19:10 +01:00
$where [] = '(cal_recur_date=0 OR cal_recur_date >= ' . ( int ) $change_since . ')' ;
2005-11-09 00:15:14 +01:00
}
2009-09-27 09:59:01 +02:00
2009-11-29 15:03:45 +01:00
if ( $change_since !== false )
2005-11-09 00:15:14 +01:00
{
2009-11-29 15:03:45 +01:00
// find all existing recurrences
foreach ( $this -> db -> select ( $this -> user_table , 'DISTINCT cal_recur_date' , $where , __LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
$recurrences [] = $row [ 'cal_recur_date' ];
}
// update existing entries
2011-01-06 06:19:10 +01:00
$existing_entries = $this -> db -> select ( $this -> user_table , '*' , $where , __LINE__ , __FILE__ , false , 'ORDER BY cal_recur_date DESC' , 'calendar' );
2009-08-17 16:45:42 +02:00
// create a full list of participants which already exist in the db
2011-01-06 06:19:10 +01:00
// with status, quantity and role of the earliest recurence
2009-08-17 16:45:42 +02:00
$old_participants = array ();
foreach ( $existing_entries as $row )
2005-11-09 00:15:14 +01:00
{
2011-01-06 06:19:10 +01:00
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
if ( $row [ 'cal_recur_date' ] || ! isset ( $old_participants [ $uid ]))
{
$old_participants [ $uid ] = self :: combine_status ( $row [ 'cal_status' ], $row [ 'cal_quantity' ], $row [ 'cal_role' ]);
}
2009-08-17 16:45:42 +02:00
}
// tag participants which should be deleted
if ( $add_only === false )
{
$deleted = array ();
foreach ( $existing_entries as $row )
2005-11-09 00:15:14 +01:00
{
2009-08-17 16:45:42 +02:00
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
// delete not longer set participants
if ( ! isset ( $participants [ $uid ]))
{
$deleted [ $row [ 'cal_user_type' ]][] = $row [ 'cal_user_id' ];
}
2005-11-09 00:15:14 +01:00
}
}
2010-11-11 09:51:13 +01:00
// only keep added OR status (incl. quantity!) changed participants for further steps
// we do not touch unchanged (!) existing ones
foreach ( $participants as $uid => $status )
{
if ( $old_participants [ $uid ] === $status )
{
unset ( $participants [ $uid ]);
}
}
2009-08-17 16:45:42 +02:00
// delete participants tagged for delete
if ( $add_only === false && count ( $deleted ))
2005-11-09 00:15:14 +01:00
{
$to_or = array ();
2008-03-15 15:10:20 +01:00
$table_def = $this -> db -> get_table_definitions ( 'calendar' , $this -> user_table );
2005-11-09 00:15:14 +01:00
foreach ( $deleted as $type => $ids )
{
2008-03-15 15:10:20 +01:00
$to_or [] = $this -> db -> expression ( $table_def , array (
2005-11-09 00:15:14 +01:00
'cal_user_type' => $type ,
'cal_user_id' => $ids ,
));
}
2009-10-07 06:23:42 +02:00
$where [ 1 ] = '(' . implode ( ' OR ' , $to_or ) . ')' ;
2012-01-23 08:41:29 +01:00
$this -> db -> update ( $this -> user_table , array ( 'cal_status' => 'X' ), $where , __LINE__ , __FILE__ , 'calendar' );
2009-10-07 06:23:42 +02:00
unset ( $where [ 1 ]);
2005-11-09 00:15:14 +01:00
}
}
2009-09-27 09:59:01 +02:00
2009-08-17 16:45:42 +02:00
if ( count ( $participants )) // participants which need to be added
2005-11-09 00:15:14 +01:00
{
2009-11-29 15:03:45 +01:00
if ( ! count ( $recurrences )) $recurrences [] = 0 ; // insert the default recurrence
2005-11-09 00:15:14 +01:00
2009-08-17 16:45:42 +02:00
// update participants
2005-11-09 00:15:14 +01:00
foreach ( $participants as $uid => $status )
{
2010-03-07 00:06:43 +01:00
$type = $id = $quantity = $role = null ;
2009-08-06 13:29:05 +02:00
self :: split_user ( $uid , $type , $id );
self :: split_status ( $status , $quantity , $role );
2009-08-04 19:14:16 +02:00
$set = array (
2009-08-06 13:29:05 +02:00
'cal_status' => $status ,
'cal_quantity' => $quantity ,
'cal_role' => $role ,
2009-08-04 19:14:16 +02:00
);
2005-11-09 00:15:14 +01:00
foreach ( $recurrences as $recur_date )
{
2009-08-04 19:14:16 +02:00
$this -> db -> insert ( $this -> user_table , $set , array (
2007-06-11 19:13:43 +02:00
'cal_id' => $cal_id ,
'cal_recur_date' => $recur_date ,
2005-11-09 00:15:14 +01:00
'cal_user_type' => $type ,
'cal_user_id' => $id ,
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
}
}
2006-03-29 09:01:18 +02:00
return true ;
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 )
*
* @ param int $cal_id
2006-12-25 13:16:40 +01:00
* @ param char $user_type 'u' regular user , 'r' resource , 'c' contact
2005-11-09 00:15:14 +01:00
* @ param int $user_id
2009-07-15 22:35:56 +02:00
* @ 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-08-04 19:14:16 +02:00
* @ param string $role = null role to set if ! is_null ( $role )
2005-11-09 00:15:14 +01:00
* @ return int number of changed recurrences
*/
2009-08-04 19:14:16 +02:00
function set_status ( $cal_id , $user_type , $user_id , $status , $recur_date = 0 , $role = null )
2005-11-09 00:15:14 +01:00
{
static $status_code_short = array (
REJECTED => 'R' ,
NO_RESPONSE => 'U' ,
TENTATIVE => 'T' ,
2010-02-17 14:29:28 +01:00
ACCEPTED => 'A' ,
DELEGATED => 'D'
2005-11-09 00:15:14 +01:00
);
2009-03-24 09:06:05 +01:00
if ( ! ( int ) $cal_id || ! ( int ) $user_id && $user_type != 'e' )
{
return false ;
}
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
if ( is_numeric ( $status )) $status = $status_code_short [ $status ];
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
$where = array (
'cal_id' => $cal_id ,
'cal_user_type' => $user_type ? $user_type : 'u' ,
'cal_user_id' => $user_id ,
);
if (( int ) $recur_date )
{
$where [ 'cal_recur_date' ] = $recur_date ;
}
else
{
$where [] = '(cal_recur_date=0 OR cal_recur_date >= ' . time () . ')' ;
}
2009-08-17 16:45:42 +02:00
2005-11-23 15:21:20 +01:00
if ( $status == 'G' ) // remove group invitations, as we dont store them in the db
{
2008-03-15 15:10:20 +01:00
$this -> db -> delete ( $this -> user_table , $where , __LINE__ , __FILE__ , 'calendar' );
2005-11-23 15:21:20 +01:00
}
else
{
2009-08-04 19:14:16 +02:00
$set = array ( 'cal_status' => $status );
2009-11-26 19:36:19 +01:00
if ( ! is_null ( $role ) && $role != 'REQ-PARTICIPANT' ) $set [ 'cal_role' ] = $role ;
2009-08-04 19:14:16 +02:00
$this -> db -> insert ( $this -> user_table , $set , $where , __LINE__ , __FILE__ , 'calendar' );
2005-11-23 15:21:20 +01:00
}
2012-09-25 13:54:41 +02:00
// update modified and modifier in main table
if (( $ret = $this -> db -> affected_rows ()))
{
2012-09-28 09:53:14 +02:00
$this -> updateModified ( $cal_id );
// if event is an exception: update modified of master, to force etag, ctag and sync-token change
2012-09-28 10:07:12 +02:00
if (( $master_id = $this -> db -> select ( $this -> cal_table , 'cal_reference' , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ ) -> fetchColumn ()))
2012-09-28 09:53:14 +02:00
{
$this -> updateModified ( $master_id );
}
2012-09-25 13:54:41 +02:00
}
2009-07-15 22:35:56 +02:00
//error_log(__METHOD__."($cal_id,$user_type,$user_id,$status,$recur_date) = $ret");
2009-08-04 19:14:16 +02:00
return $ret ;
2005-11-09 00:15:14 +01:00
}
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
/**
* creates or update a recurrence in the dates and users table
*
2006-03-29 09:01:18 +02:00
* @ param int $cal_id
2005-11-09 00:15:14 +01:00
* @ param int $start
* @ param int $end
* @ param array $participants uid => status pairs
*/
function recurrence ( $cal_id , $start , $end , $participants )
{
//echo "<p>socal::recurrence($cal_id,$start,$end,".print_r($participants,true).")</p>\n";
$this -> db -> insert ( $this -> dates_table , array (
'cal_end' => $end ,
), array (
'cal_id' => $cal_id ,
'cal_start' => $start ,
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2006-03-29 09:01:18 +02:00
2011-11-09 18:53:42 +01:00
if ( ! is_array ( $participants ))
{
error_log ( __METHOD__ . " ( $cal_id , $start , $end , " . array2string ( $participants ) . " ) participants is NO array! " . function_backtrace ());
}
2005-11-09 00:15:14 +01:00
foreach ( $participants as $uid => $status )
{
2005-11-23 15:21:20 +01:00
if ( $status == 'G' ) continue ; // dont save group-invitations
2009-07-15 22:35:56 +02:00
$type = '' ;
$id = null ;
2009-11-26 21:21:16 +01:00
self :: split_user ( $uid , $type , $id );
self :: split_status ( $status , $quantity , $role );
2005-11-09 00:15:14 +01:00
$this -> db -> insert ( $this -> user_table , array (
2009-11-26 19:36:19 +01:00
'cal_status' => $status ,
'cal_quantity' => $quantity ,
'cal_role' => $role
2005-11-09 00:15:14 +01:00
), array (
'cal_id' => $cal_id ,
'cal_recur_date' => $start ,
'cal_user_type' => $type ,
'cal_user_id' => $id ,
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
}
/**
* Get all unfinished recuring events ( or all users ) after a given time
*
2006-03-29 09:01:18 +02:00
* @ param int $time
2005-11-09 00:15:14 +01:00
* @ return array with cal_id => max ( cal_start ) pairs
*/
function unfinished_recuring ( $time )
{
2008-03-15 15:10:20 +01:00
$ids = array ();
2012-09-19 12:27:28 +02:00
foreach ( $rs = $this -> db -> select ( $this -> repeats_table , " $this->repeats_table .cal_id,MAX(cal_start) AS cal_start " ,
'(range_end IS NULL OR range_end > ' . ( int ) $time . ')' ,
__LINE__ , __FILE__ , false , " GROUP BY $this->repeats_table .cal_id,range_end " , 'calendar' , 0 ,
" JOIN $this->cal_table ON $this->repeats_table .cal_id= $this->cal_table .cal_id " .
" JOIN $this->dates_table ON $this->repeats_table .cal_id= $this->dates_table .cal_id " ) as $row )
2005-11-09 00:15:14 +01:00
{
$ids [ $row [ 'cal_id' ]] = $row [ 'cal_start' ];
}
2012-09-19 12:27:28 +02:00
//error_log(__METHOD__."($time) query='$rs->sql' --> ids=".array2string($ids));
2005-11-09 00:15:14 +01:00
return $ids ;
}
/**
* deletes an event incl . all recurrences , participants and alarms
*
* @ param int $cal_id
*/
function delete ( $cal_id )
{
//echo "<p>socal::delete($cal_id)</p>\n";
$this -> delete_alarms ( $cal_id );
foreach ( $this -> all_tables as $table )
{
2008-03-15 15:10:20 +01:00
$this -> db -> delete ( $table , array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
}
2010-06-14 13:45:00 +02:00
/**
* Delete all events that were before the given date .
*
* Recurring events that finished before the date will be deleted .
* Recurring events that span the date will be ignored . Non - recurring
* events before the date will be deleted .
*
* @ param int $date
*/
function purge ( $date )
{
2012-09-19 12:27:28 +02:00
// with new range_end we simple delete all with range_end < $date (range_end NULL is never returned)
foreach ( $this -> db -> select ( $this -> cal_table , 'cal_id' , 'range_end < ' . ( int ) $date , __LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
2010-06-14 13:45:00 +02:00
{
2010-07-06 10:20:53 +02:00
//echo __METHOD__." About to delete".$row['cal_id']."\r\n";
foreach ( $this -> all_tables as $table )
{
$this -> db -> delete ( $table , array ( 'cal_id' => $row [ 'cal_id' ]), __LINE__ , __FILE__ , 'calendar' );
}
// handle sync
2010-09-10 09:01:41 +02:00
$this -> db -> update ( 'egw_api_content_history' , array (
'sync_deleted' => time (),
), array (
'sync_appname' => 'calendar' ,
'sync_contentid' => $row [ 'cal_id' ], // sync_contentid is varchar(60)!
), __LINE__ , __FILE__ );
2010-07-06 10:20:53 +02:00
// handle links
egw_link :: unlink ( '' , 'calendar' , $row [ 'cal_id' ]);
2010-06-14 13:45:00 +02:00
}
}
2005-11-09 00:15:14 +01:00
/**
* read the alarms of a calendar - event specified by $cal_id
*
* alarm - id is a string of 'cal:' . $cal_id . ':' . $alarm_nr , it is used as the job - id too
*
* @ param int $cal_id
* @ return array of alarms with alarm - id as key
*/
function read_alarms ( $cal_id )
{
$alarms = array ();
if ( $jobs = $this -> async -> read ( 'cal:' . ( int ) $cal_id . ':%' ))
{
foreach ( $jobs as $id => $job )
{
$alarm = $job [ 'data' ]; // text, enabled
$alarm [ 'id' ] = $id ;
$alarm [ 'time' ] = $job [ 'next' ];
$alarms [ $id ] = $alarm ;
}
}
return $alarms ;
}
/**
* read a single alarm specified by it ' s $id
*
* @ param string $id alarm - id is a string of 'cal:' . $cal_id . ':' . $alarm_nr , it is used as the job - id too
* @ return array with data of the alarm
*/
function read_alarm ( $id )
{
if ( ! ( $jobs = $this -> async -> read ( $id )))
{
return False ;
}
list ( $id , $job ) = each ( $jobs );
$alarm = $job [ 'data' ]; // text, enabled
$alarm [ 'id' ] = $id ;
$alarm [ 'time' ] = $job [ 'next' ];
//echo "<p>read_alarm('$id')="; print_r($alarm); echo "</p>\n";
return $alarm ;
}
/**
* saves a new or updated alarm
*
* @ param int $cal_id Id of the calendar - entry
* @ param array $alarm array with fields : text , owner , enabled , ..
2010-06-07 19:54:29 +02:00
* @ param timestamp $now = 0 timestamp for modification of related event
2005-11-09 00:15:14 +01:00
* @ return string id of the alarm
*/
2010-06-07 19:54:29 +02:00
function save_alarm ( $cal_id , $alarm , $now = 0 )
2005-11-09 00:15:14 +01:00
{
//echo "<p>save_alarm(cal_id=$cal_id, alarm="; print_r($alarm); echo ")</p>\n";
2010-08-10 23:21:18 +02:00
//error_log(__METHOD__."(.$cal_id,$now,".array2string($alarm).')');
2005-11-09 00:15:14 +01:00
if ( ! ( $id = $alarm [ 'id' ]))
{
$alarms = $this -> read_alarms ( $cal_id ); // find a free alarm#
$n = count ( $alarms );
do
{
$id = 'cal:' . ( int ) $cal_id . ':' . $n ;
++ $n ;
}
while ( @ isset ( $alarms [ $id ]));
}
else
{
$this -> async -> cancel_timer ( $id );
}
$alarm [ 'cal_id' ] = $cal_id ; // we need the back-reference
2012-12-07 15:10:51 +01:00
//error_log(__METHOD__.__LINE__.' Save Alarm for CalID:'.$cal_id.'->'.array2string($alarm).'-->'.$id.'#'.function_backtrace());
2011-03-21 15:13:42 +01:00
// allways store job with the alarm owner as job-owner to get eg. the correct from address
if ( ! $this -> async -> set_timer ( $alarm [ 'time' ], $id , 'calendar.calendar_boupdate.send_alarm' , $alarm , $alarm [ 'owner' ]))
2005-11-09 00:15:14 +01:00
{
return False ;
}
2009-07-15 22:35:56 +02:00
// update the modification information of the related event
2010-06-07 19:54:29 +02:00
if ( ! $now ) $now = time ();
2009-07-15 22:35:56 +02:00
$modifier = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ];
2009-08-06 13:29:05 +02:00
$this -> db -> update ( $this -> cal_table , array ( 'cal_modified' => $now , 'cal_modifier' => $modifier ),
2009-07-15 22:35:56 +02:00
array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , 'calendar' );
2009-08-06 13:29:05 +02:00
2005-11-09 00:15:14 +01:00
return $id ;
}
/**
* delete all alarms of a calendar - entry
*
* @ param int $cal_id Id of the calendar - entry
* @ return int number of alarms deleted
*/
function delete_alarms ( $cal_id )
{
$alarms = $this -> read_alarms ( $cal_id );
foreach ( $alarms as $id => $alarm )
{
$this -> async -> cancel_timer ( $id );
}
return count ( $alarms );
}
/**
* 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
2010-06-07 19:54:29 +02:00
* @ param timestamp $now = 0 timestamp for modification of related event
2005-11-09 00:15:14 +01:00
* @ return int number of alarms deleted
*/
2010-06-07 19:54:29 +02:00
function delete_alarm ( $id , $now = 0 )
2005-11-09 00:15:14 +01:00
{
2009-07-15 22:35:56 +02:00
// update the modification information of the related event
list (, $cal_id ) = explode ( ':' , $id );
2009-08-06 13:29:05 +02:00
if ( $cal_id )
{
2010-06-07 19:54:29 +02:00
if ( ! $now ) $now = time ();
2009-07-15 22:35:56 +02:00
$modifier = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ];
2009-08-06 13:29:05 +02:00
$this -> db -> update ( $this -> cal_table , array ( 'cal_modified' => $now , 'cal_modifier' => $modifier ),
2009-07-15 22:35:56 +02:00
array ( 'cal_id' => $cal_id ), __LINE__ , __FILE__ , 'calendar' );
}
2005-11-09 00:15:14 +01:00
return $this -> async -> cancel_timer ( $id );
}
2006-03-29 09:01:18 +02:00
2008-06-07 19:45:33 +02:00
/**
* Delete account hook
*
* @ param array | int $old_user integer old user or array with keys 'account_id' and 'new_owner' as the deleteaccount hook uses it
* @ param int $new_user = null
*/
2009-07-15 22:35:56 +02:00
function deleteaccount ( $old_user , $newuser = null )
2005-11-09 00:15:14 +01:00
{
2008-06-07 19:45:33 +02:00
if ( is_array ( $old_user ))
{
$new_user = $old_user [ 'new_owner' ];
$old_user = $old_user [ 'account_id' ];
}
2005-11-09 00:15:14 +01:00
if ( ! ( int ) $new_user )
{
2009-07-15 22:35:56 +02:00
$user_type = '' ;
$user_id = null ;
2009-11-29 15:03:45 +01:00
self :: split_user ( $old_user , $user_type , $user_id );
2006-03-29 09:01:18 +02:00
2005-11-09 00:15:14 +01:00
if ( $user_type == 'u' ) // only accounts can be owners of events
{
2008-03-15 15:10:20 +01:00
foreach ( $this -> db -> select ( $this -> cal_table , 'cal_id' , array ( 'cal_owner' => $old_user ), __LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
2005-11-09 00:15:14 +01:00
{
2008-03-15 15:10:20 +01:00
$this -> delete ( $row [ 'cal_id' ]);
2005-11-09 00:15:14 +01:00
}
}
$this -> db -> delete ( $this -> user_table , array (
'cal_user_type' => $user_type ,
'cal_user_id' => $user_id ,
2008-03-17 10:06:08 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2006-03-29 09:01:18 +02:00
2006-03-21 11:05:45 +01:00
// delete calendar entries without participants (can happen if the deleted user is the only participants, but not the owner)
2008-03-15 15:10:20 +01:00
foreach ( $this -> db -> select ( $this -> cal_table , " DISTINCT $this->cal_table .cal_id " , 'cal_user_id IS NULL' , __LINE__ , __FILE__ ,
False , '' , 'calendar' , 0 , " LEFT JOIN $this->user_table ON $this->cal_table .cal_id= $this->user_table .cal_id " ) as $row )
2006-03-21 11:05:45 +01:00
{
2008-03-15 15:10:20 +01:00
$this -> delete ( $row [ 'cal_id' ]);
2006-03-21 11:05:45 +01:00
}
2005-11-09 00:15:14 +01:00
}
else
{
2008-03-15 15:10:20 +01:00
$this -> db -> update ( $this -> cal_table , array ( 'cal_owner' => $new_user ), array ( 'cal_owner' => $old_user ), __LINE__ , __FILE__ , 'calendar' );
2007-05-07 10:27:50 +02:00
// delete participation of old user, if new user is already a participant
2008-03-15 15:10:20 +01:00
$ids = array ();
foreach ( $this -> db -> select ( $this -> user_table , 'cal_id' , array ( // MySQL does NOT allow to run this as delete!
2007-05-07 10:27:50 +02:00
'cal_user_type' => 'u' ,
'cal_user_id' => $old_user ,
2011-03-11 08:37:34 +01:00
" cal_id IN (SELECT cal_id FROM $this->user_table other WHERE other.cal_id=cal_id AND other.cal_user_id= " . $this -> db -> quote ( $new_user ) . " AND cal_user_type='u') " ,
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
2007-05-07 10:27:50 +02:00
{
$ids [] = $row [ 'cal_id' ];
}
if ( $ids ) $this -> db -> delete ( $this -> user_table , array (
'cal_user_type' => 'u' ,
'cal_user_id' => $old_user ,
'cal_id' => $ids ,
2008-03-15 15:10:20 +01:00
), __LINE__ , __FILE__ , 'calendar' );
2007-05-07 10:27:50 +02:00
// now change participant in the rest to contain new user instead of old user
2008-03-15 15:10:20 +01:00
$this -> db -> update ( $this -> user_table , array (
'cal_user_id' => $new_user ,
), array (
'cal_user_type' => 'u' ,
'cal_user_id' => $old_user ,
), __LINE__ , __FILE__ , 'calendar' );
2005-11-09 00:15:14 +01:00
}
}
2009-07-15 22:35:56 +02:00
/**
* get stati of all recurrences of an event for a specific participant
*
* @ param int $cal_id
2010-02-23 19:19:12 +01:00
* @ param int $uid = null participant uid ; if == null return only the recur dates
2009-11-29 15:03:45 +01:00
* @ param int $start = 0 if != 0 : startdate of the search / list ( servertime )
* @ param int $end = 0 if != 0 : enddate of the search / list ( servertime )
*
2009-07-15 22:35:56 +02:00
* @ return array recur_date => status pairs ( index 0 => main status )
*/
2010-02-23 19:19:12 +01:00
function get_recurrences ( $cal_id , $uid = null , $start = 0 , $end = 0 )
2009-07-15 22:35:56 +02:00
{
$participant_status = array ();
$where = array ( 'cal_id' => $cal_id );
2009-11-29 15:03:45 +01:00
if ( $start != 0 && $end == 0 ) $where [] = '(cal_recur_date = 0 OR cal_recur_date >= ' . ( int ) $start . ')' ;
2009-11-29 22:02:15 +01:00
if ( $start == 0 && $end != 0 ) $where [] = '(cal_recur_date = 0 OR cal_recur_date <= ' . ( int ) $end . ')' ;
2009-11-29 15:03:45 +01:00
if ( $start != 0 && $end != 0 )
{
$where [] = '(cal_recur_date = 0 OR (cal_recur_date >= ' . ( int ) $start .
' AND cal_recur_date <= ' . ( int ) $end . '))' ;
}
2009-07-15 22:35:56 +02:00
foreach ( $this -> db -> select ( $this -> user_table , 'DISTINCT cal_recur_date' , $where , __LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
// inititalize the array
$participant_status [ $row [ 'cal_recur_date' ]] = null ;
}
2010-02-23 19:19:12 +01:00
if ( is_null ( $uid )) return $participant_status ;
$user_type = $user_id = null ;
self :: split_user ( $uid , $user_type , $user_id );
2009-07-15 22:35:56 +02:00
$where = array (
'cal_id' => $cal_id ,
'cal_user_type' => $user_type ? $user_type : 'u' ,
'cal_user_id' => $user_id ,
);
2009-11-29 15:03:45 +01:00
if ( $start != 0 && $end == 0 ) $where [] = '(cal_recur_date = 0 OR cal_recur_date >= ' . ( int ) $start . ')' ;
2009-11-29 22:02:15 +01:00
if ( $start == 0 && $end != 0 ) $where [] = '(cal_recur_date = 0 OR cal_recur_date <= ' . ( int ) $end . ')' ;
2009-11-29 15:03:45 +01:00
if ( $start != 0 && $end != 0 )
{
$where [] = '(cal_recur_date = 0 OR (cal_recur_date >= ' . ( int ) $start .
' AND cal_recur_date <= ' . ( int ) $end . '))' ;
}
2009-11-26 21:21:16 +01:00
foreach ( $this -> db -> select ( $this -> user_table , 'cal_recur_date,cal_status,cal_quantity,cal_role' , $where ,
2009-07-15 22:35:56 +02:00
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
2009-11-26 21:21:16 +01:00
$status = self :: combine_status ( $row [ 'cal_status' ], $row [ 'cal_quantity' ], $row [ 'cal_role' ]);
$participant_status [ $row [ 'cal_recur_date' ]] = $status ;
2009-07-15 22:35:56 +02:00
}
return $participant_status ;
}
/**
* get all participants of an event
*
* @ param int $cal_id
* @ param int $recur_date = 0 gives participants of this recurrence , default 0 = all
*
* @ return array participants
*/
function get_participants ( $cal_id , $recur_date = 0 )
{
$participants = array ();
$where = array ( 'cal_id' => $cal_id );
if ( $recur_date )
{
$where [ 'cal_recur_date' ] = $recur_date ;
}
foreach ( $this -> db -> select ( $this -> user_table , 'DISTINCT cal_user_type,cal_user_id' , $where ,
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
2009-11-29 15:03:45 +01:00
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
2009-07-15 22:35:56 +02:00
$id = $row [ 'cal_user_type' ] . $row [ 'cal_user_id' ];
$participants [ $id ][ 'type' ] = $row [ 'cal_user_type' ];
$participants [ $id ][ 'id' ] = $row [ 'cal_user_id' ];
$participants [ $id ][ 'uid' ] = $uid ;
}
return $participants ;
}
/**
* get all releated events
*
* @ param int $uid UID of the series
*
* @ return array of event exception ids for all events which share $uid
*/
function get_related ( $uid )
{
$where = array (
'cal_uid' => $uid ,
);
$related = array ();
foreach ( $this -> db -> select ( $this -> cal_table , 'cal_id,cal_reference' , $where ,
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
2010-01-22 20:47:32 +01:00
if ( $row [ 'cal_reference' ] != 0 )
2009-07-15 22:35:56 +02:00
{
2010-01-22 20:47:32 +01:00
// not the series master
$related [] = $row [ 'cal_id' ];
2009-07-15 22:35:56 +02:00
}
}
return $related ;
}
/**
* Gets the exception days of a given recurring event caused by
2010-01-14 18:01:30 +01:00
* irregular participant stati or timezone transitions
2009-07-15 22:35:56 +02:00
*
2009-11-19 11:13:17 +01:00
* @ param array $event Recurring Event .
* @ param string tz_id = null timezone for exports ( null for event ' s timezone )
2009-11-29 15:03:45 +01:00
* @ param int $start = 0 if != 0 : startdate of the search / list ( servertime )
2010-01-29 22:42:54 +01:00
* @ param int $end = 0 if != 0 : enddate of the search / list ( servertime )
* @ param string $filter = 'all' string filter - name : all ( not rejected ),
2010-02-17 14:29:28 +01:00
* accepted , unknown , tentative , rejected , delegated
2010-01-29 22:42:54 +01:00
* rrule return array of remote exceptions in servertime
* tz_rrule / tz_only , return ( only by ) timezone transition affected entries
* map return array of dates with no pseudo exception
* key remote occurrence date
* tz_map return array of all dates with no tz pseudo exception
2009-07-15 22:35:56 +02:00
*
* @ return array Array of exception days ( false for non - recurring events ) .
*/
2010-01-29 22:42:54 +01:00
function get_recurrence_exceptions ( $event , $tz_id = null , $start = 0 , $end = 0 , $filter = 'all' )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
if ( ! is_array ( $event )) return false ;
2009-07-15 22:35:56 +02:00
$cal_id = ( int ) $event [ 'id' ];
2010-01-29 22:42:54 +01:00
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// "($cal_id, $tz_id, $filter): " . $event['tzid']);
2009-07-15 22:35:56 +02:00
if ( ! $cal_id || $event [ 'recur_type' ] == MCAL_RECUR_NONE ) return false ;
$days = array ();
2009-11-19 11:13:17 +01:00
2010-01-29 22:42:54 +01:00
$expand_all = ( ! $this -> isWholeDay ( $event ) && $tz_id && $tz_id != $event [ 'tzid' ]);
if ( $filter == 'tz_only' && ! $expand_all ) return $days ;
$remote = in_array ( $filter , array ( 'tz_rrule' , 'rrule' ));
$egw_rrule = calendar_rrule :: event2rrule ( $event , false );
2010-02-17 14:29:28 +01:00
$egw_rrule -> current = clone $egw_rrule -> time ;
2010-01-29 22:42:54 +01:00
if ( $expand_all )
2009-11-19 11:13:17 +01:00
{
2011-05-03 19:28:54 +02:00
unset ( $event [ 'recur_exception' ]);
2010-01-29 22:42:54 +01:00
$remote_rrule = calendar_rrule :: event2rrule ( $event , false , $tz_id );
2010-02-17 14:29:28 +01:00
$remote_rrule -> current = clone $remote_rrule -> time ;
2010-01-29 22:42:54 +01:00
}
while ( $egw_rrule -> valid ())
{
2010-02-17 14:29:28 +01:00
while ( $egw_rrule -> exceptions &&
in_array ( $egw_rrule -> current -> format ( 'Ymd' ), $egw_rrule -> exceptions ))
{
if ( in_array ( $filter , array ( 'map' , 'tz_map' , 'rrule' , 'tz_rrule' )))
{
// real exception
$locts = ( int ) egw_time :: to ( $egw_rrule -> current (), 'server' );
if ( $expand_all )
{
$remts = ( int ) egw_time :: to ( $remote_rrule -> current (), 'server' );
if ( $remote )
{
$days [ $locts ] = $remts ;
}
else
{
$days [ $remts ] = $locts ;
}
}
else
{
$days [ $locts ] = $locts ;
}
}
if ( $expand_all )
{
$remote_rrule -> next_no_exception ();
}
$egw_rrule -> next_no_exception ();
if ( ! $egw_rrule -> valid ()) return $days ;
}
2010-01-29 22:42:54 +01:00
$day = $egw_rrule -> current ();
$locts = ( int ) egw_time :: to ( $day , 'server' );
$tz_exception = ( $filter == 'tz_rrule' );
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// '()[EVENT Server]: ' . $day->format('Ymd\THis') . " ($locts)");
if ( $expand_all )
2009-11-19 11:13:17 +01:00
{
2010-01-29 22:42:54 +01:00
$remote_day = $remote_rrule -> current ();
$remts = ( int ) egw_time :: to ( $remote_day , 'server' );
// error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// '()[EVENT Device]: ' . $remote_day->format('Ymd\THis') . " ($remts)");
2009-11-19 11:13:17 +01:00
}
2009-07-15 22:35:56 +02:00
2010-01-29 22:42:54 +01:00
if ( ! ( $end && $end < $locts ) && $start <= $locts )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
// we are within the relevant time period
if ( $expand_all && $day -> format ( 'U' ) != $remote_day -> format ( 'U' ))
{
$tz_exception = true ;
if ( $filter != 'map' && $filter != 'tz_map' )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
// timezone pseudo exception
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// '() tz exception: ' . $day->format('Ymd\THis'));
if ( $remote )
2009-07-15 22:35:56 +02:00
{
2010-01-29 22:42:54 +01:00
$days [ $locts ] = $remts ;
}
else
{
$days [ $remts ] = $locts ;
}
}
}
if ( $filter != 'tz_map' && ( ! $tz_exception || $filter == 'tz_only' ) &&
$this -> status_pseudo_exception ( $event [ 'id' ], $locts , $filter ))
{
// status pseudo exception
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// '() status exception: ' . $day->format('Ymd\THis'));
if ( $expand_all )
{
if ( $filter == 'tz_only' )
{
unset ( $days [ $remts ]);
}
else
{
if ( $filter != 'map' )
2010-01-14 18:01:30 +01:00
{
2010-01-29 22:42:54 +01:00
if ( $remote )
2010-01-14 18:01:30 +01:00
{
2010-01-29 22:42:54 +01:00
$days [ $locts ] = $remts ;
}
else
{
$days [ $remts ] = $locts ;
2010-01-14 18:01:30 +01:00
}
2009-11-19 11:13:17 +01:00
}
2009-07-15 22:35:56 +02:00
}
}
2010-01-29 22:42:54 +01:00
elseif ( $filter != 'map' )
{
$days [ $locts ] = $locts ;
}
}
elseif (( $filter == 'map' || filter == 'tz_map' ) &&
! $tz_exception )
{
// no pseudo exception date
if ( $expand_all )
{
$days [ $remts ] = $locts ;
}
else
{
$days [ $locts ] = $locts ;
}
}
}
2010-02-17 14:29:28 +01:00
if ( $expand_all )
2010-01-29 22:42:54 +01:00
{
2010-02-17 14:29:28 +01:00
$remote_rrule -> next_no_exception ();
2009-07-15 22:35:56 +02:00
}
2010-02-17 14:29:28 +01:00
$egw_rrule -> next_no_exception ();
2009-07-15 22:35:56 +02:00
}
return $days ;
}
2010-01-29 22:42:54 +01:00
/**
* Checks for status only pseudo exceptions
*
* @ param int $cal_id event id
* @ param int $recur_date occurrence to check
* @ param string $filter status filter criteria for user
*
* @ return boolean true , if stati don ' t match with defaults
*/
function status_pseudo_exception ( $cal_id , $recur_date , $filter )
{
static $recurrence_zero ;
static $cached_id ;
static $user ;
if ( ! isset ( $cached_id ) || $cached_id != $cal_id )
{
// get default stati
$recurrence_zero = array ();
$user = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ];
$where = array ( 'cal_id' => $cal_id ,
'cal_recur_date' => 0 );
foreach ( $this -> db -> select ( $this -> user_table , 'cal_user_id,cal_user_type,cal_status' , $where ,
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
switch ( $row [ 'cal_user_type' ])
{
case 'u' : // account
case 'c' : // contact
case 'e' : // email address
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
$recurrence_zero [ $uid ] = $row [ 'cal_status' ];
}
}
$cached_id = $cal_id ;
}
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// "($cal_id, $recur_date, $filter)[DEFAULTS]: " .
// array2string($recurrence_zero));
$participants = array ();
$where = array ( 'cal_id' => $cal_id ,
'cal_recur_date' => $recur_date );
foreach ( $this -> db -> select ( $this -> user_table , 'cal_user_id,cal_user_type,cal_status' , $where ,
__LINE__ , __FILE__ , false , '' , 'calendar' ) as $row )
{
switch ( $row [ 'cal_user_type' ])
{
case 'u' : // account
case 'c' : // contact
case 'e' : // email address
$uid = self :: combine_user ( $row [ 'cal_user_type' ], $row [ 'cal_user_id' ]);
$participants [ $uid ] = $row [ 'cal_status' ];
}
}
if ( empty ( $participants )) return false ; // occurrence does not exist at all yet
foreach ( $recurrence_zero as $uid => $status )
{
if ( $uid == $user )
{
// handle filter for current user
switch ( $filter )
{
case 'unknown' :
if ( $status != 'U' )
{
unset ( $participants [ $uid ]);
continue ;
}
break ;
case 'accepted' :
if ( $status != 'A' )
{
unset ( $participants [ $uid ]);
continue ;
}
break ;
case 'tentative' :
if ( $status != 'T' )
{
unset ( $participants [ $uid ]);
continue ;
}
break ;
case 'rejected' :
if ( $status != 'R' )
{
unset ( $participants [ $uid ]);
continue ;
}
break ;
2010-02-17 14:29:28 +01:00
case 'delegated' :
if ( $status != 'D' )
{
unset ( $participants [ $uid ]);
continue ;
}
break ;
2010-01-29 22:42:54 +01:00
case 'default' :
if ( $status == 'R' )
{
unset ( $participants [ $uid ]);
continue ;
}
break ;
default :
// All entries
}
}
if ( ! isset ( $participants [ $uid ])
|| $participants [ $uid ] != $status )
return true ;
unset ( $participants [ $uid ]);
}
return ( ! empty ( $participants ));
}
/**
* Check if the event is the whole day
*
* @ param array $event event ( all timestamps in servertime )
* @ return boolean true if whole day event within its timezone , false othwerwise
*/
function isWholeDay ( $event )
{
if ( ! isset ( $event [ 'start' ]) || ! isset ( $event [ 'end' ])) return false ;
if ( empty ( $event [ 'tzid' ]))
{
$timezone = egw_time :: $server_timezone ;
}
else
{
if ( ! isset ( self :: $tz_cache [ $event [ 'tzid' ]]))
{
self :: $tz_cache [ $event [ 'tzid' ]] = calendar_timezones :: DateTimeZone ( $event [ 'tzid' ]);
}
$timezone = self :: $tz_cache [ $event [ 'tzid' ]];
}
$start = new egw_time ( $event [ 'start' ], egw_time :: $server_timezone );
$start -> setTimezone ( $timezone );
$end = new egw_time ( $event [ 'end' ], egw_time :: $server_timezone );
$end -> setTimezone ( $timezone );
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// '(): ' . $start . '-' . $end);
$start = egw_time :: to ( $start , 'array' );
$end = egw_time :: to ( $end , 'array' );
return ! $start [ 'hour' ] && ! $start [ 'minute' ] && $end [ 'hour' ] == 23 && $end [ 'minute' ] == 59 ;
}
/**
2010-02-19 18:10:15 +01:00
* Moves a datetime to the beginning of the day within timezone
2010-01-29 22:42:54 +01:00
*
* @ param egw_time & time the datetime entry
* @ param string tz_id timezone
*
* @ return DateTime
*/
2010-05-03 18:43:42 +02:00
function & startOfDay ( egw_time $time , $tz_id = null )
2010-01-29 22:42:54 +01:00
{
if ( empty ( $tz_id ))
{
$timezone = egw_time :: $server_timezone ;
}
else
{
if ( ! isset ( self :: $tz_cache [ $tz_id ]))
{
self :: $tz_cache [ $tz_id ] = calendar_timezones :: DateTimeZone ( $tz_id );
}
$timezone = self :: $tz_cache [ $tz_id ];
}
return new egw_time ( $time -> format ( 'Y-m-d 00:00:00' ), $timezone );
}
2010-10-28 11:22:01 +02:00
2010-09-11 20:08:48 +02:00
/**
2010-10-28 11:22:01 +02:00
* Udates the modification timestamp
2010-09-11 20:08:48 +02:00
*
* @ param id event id
2012-09-28 09:53:14 +02:00
* @ param time new timestamp , default current ( server - ) time
* @ param modifier uid of the modifier , default current user
2010-09-11 20:08:48 +02:00
*/
2012-09-28 09:53:14 +02:00
function updateModified ( $id , $time = null , $modifier = null )
2010-09-11 20:08:48 +02:00
{
2012-09-28 09:53:14 +02:00
if ( is_null ( $time )) $time = time ();
if ( is_null ( $modifier )) $modifier = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ];
2010-09-11 20:08:48 +02:00
$this -> db -> update ( $this -> cal_table ,
array ( 'cal_modified' => $time , 'cal_modifier' => $modifier ),
array ( 'cal_id' => $id ), __LINE__ , __FILE__ , 'calendar' );
}
2004-11-07 15:38:00 +01:00
}