2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 /*********************************************************************************
40 ********************************************************************************/
43 require_once('modules/Calendar/DateTimeUtil.php');
45 require_once('include/utils/activity_utils.php');
47 function sort_func_by_act_date($act0,$act1)
49 if ($act0->start_time->ts == $act1->start_time->ts)
54 return ($act0->start_time->ts < $act1->start_time->ts) ? -1 : 1;
61 var $slices_arr = array();
62 // for monthly calendar view, if you want to see all the
63 // days in the grid, otherwise you only see that months
64 var $show_only_current_slice = false;
65 var $show_activities = true;
66 var $show_tasks = true;
68 var $show_week_on_month_view = true;
70 var $toggle_appt = true;
71 var $slice_hash = array();
72 var $shared_users_arr = array();
74 function Calendar($view,$time_arr=array())
78 if ( $current_user->getPreference('time'))
80 $time = $current_user->getPreference('time');
84 $time = $sugar_config['default_time_format'];
87 if( substr_count($time, 'h') > 0)
94 if ( isset($time_arr['activity_focus']))
96 $this->activity_focus = new CalendarActivity($time_arr['activity_focus']);
97 $this->date_time = $this->activity_focus->start_time;
101 $this->date_time = new DateTimeUtil($time_arr,true);
104 if (!( $view == 'day' || $view == 'month' || $view == 'year' || $view == 'week' || $view == 'shared') )
106 sugar_die ("view needs to be one of: day, week, month, shared, or year");
109 if ( empty($this->date_time->year))
111 sugar_die ("all views: year was not set");
113 else if ( $this->view == 'month' && empty($this->date_time->month))
115 sugar_die ("month view: month was not set");
117 else if ( $this->view == 'week' && empty($this->date_time->week))
119 sugar_die ("week view: week was not set");
121 else if ( $this->view == 'shared' && empty($this->date_time->week))
123 sugar_die ("shared view: shared was not set");
125 else if ( $this->view == 'day' && empty($this->date_time->day) && empty($this->date_time->month))
127 sugar_die ("day view: day and month was not set");
130 $this->create_slices();
133 function add_shared_users(&$shared_users_arr)
135 $this->shared_users_arr = $shared_users_arr;
138 function get_view_name($view)
140 if ($view == 'month')
144 else if ($view == 'week')
148 else if ($view == 'day')
152 else if ($view == 'year')
156 else if ($view == 'shared')
162 sugar_die ("get_view_name: view ".$this->view." not supported");
166 function isDayView() {
167 return $this->view == 'day';
170 function get_slices_arr()
172 return $this->slices_arr;
176 function create_slices()
179 global $current_user;
182 if ( $this->view == 'month')
184 $days_in_month = $this->date_time->days_in_month;
187 $first_day_of_month = $this->date_time->get_day_by_index_this_month(0);
188 $num_of_prev_days = $first_day_of_month->day_of_week;
189 // do 42 slices (6x7 grid)
191 for($i=0;$i < 42;$i++)
193 $slice = new Slice('day',$this->date_time->get_day_by_index_this_month($i-$num_of_prev_days));
194 $this->slice_hash[$slice->start_time->get_mysql_date()] = $slice;
195 array_push($this->slices_arr, $slice->start_time->get_mysql_date());
199 else if ( $this->view == 'week' || $this->view == 'shared')
203 for($i=0;$i<$days_in_week;$i++)
205 $slice = new Slice('day',$this->date_time->get_day_by_index_this_week($i));
206 $this->slice_hash[$slice->start_time->get_mysql_date()] = $slice;
207 array_push($this->slices_arr, $slice->start_time->get_mysql_date());
210 else if ( $this->view == 'day')
214 for($i=0;$i<$hours_in_day;$i++)
216 $slice = new Slice('hour',$this->date_time->get_datetime_by_index_today($i));
217 $this->slice_hash[$slice->start_time->get_mysql_date().":".$slice->start_time->hour ] = $slice;
218 $this->slices_arr[] = $slice->start_time->get_mysql_date().":".$slice->start_time->hour;
221 else if ( $this->view == 'year')
226 $slice = new Slice('month',$this->date_time->get_day_by_index_this_year($i));
227 $this->slice_hash[$slice->start_time->get_mysql_date()] = $slice;
228 array_push($this->slices_arr, $slice->start_time->get_mysql_date());
233 sugar_die("not a valid view:".$this->view);
238 function add_activities($user,$type='sugar') {
239 if ( $this->view == 'week' || $this->view == 'shared') {
240 $end_date_time = $this->date_time->get_first_day_of_next_week();
242 $end_date_time = $this->date_time;
247 $acts_arr = CalendarActivity::get_freebusy_activities($user, $this->date_time, $end_date_time);
249 $acts_arr = CalendarActivity::get_activities($user->id, $this->show_tasks, $this->date_time, $end_date_time, $this->view);
252 // loop thru each activity for this user
253 for ($i = 0;$i < count($acts_arr);$i++) {
254 $act = $acts_arr[$i];
255 // get "hashed" time slots for the current activity we are looping through
256 $hash_list =DateTimeUtil::getHashList($this->view,$act->start_time,$act->end_time);
258 for($j=0;$j < count($hash_list); $j++) {
259 if(!isset($this->slice_hash[$hash_list[$j]]) || !isset($this->slice_hash[$hash_list[$j]]->acts_arr[$user->id])) {
260 $this->slice_hash[$hash_list[$j]]->acts_arr[$user->id] = array();
262 $this->slice_hash[$hash_list[$j]]->acts_arr[$user->id][] = $act;
267 function occurs_within_slice(&$slice,&$act)
269 // if activity starts within this slice
270 // OR activity ends within this slice
271 // OR activity starts before and ends after this slice
272 if ( ( $act->start_time->ts >= $slice->start_time->ts &&
273 $act->start_time->ts <= $slice->end_time->ts )
275 ( $act->end_time->ts >= $slice->start_time->ts &&
276 $act->end_time->ts <= $slice->end_time->ts )
278 ( $act->start_time->ts <= $slice->start_time->ts &&
279 $act->end_time->ts >= $slice->end_time->ts )
282 //print "act_start:{$act->start_time->ts}<BR>";
283 //print "act_end:{$act->end_time->ts}<BR>";
284 //print "slice_start:{$slice->start_time->ts}<BR>";
285 //print "slice_end:{$slice->end_time->ts}<BR>";
293 function get_previous_date_str()
295 if ($this->view == 'month')
297 $day = $this->date_time->get_first_day_of_last_month();
299 else if ($this->view == 'week' || $this->view == 'shared')
301 $day = $this->date_time->get_first_day_of_last_week();
303 else if ($this->view == 'day')
305 $day = $this->date_time->get_yesterday();
307 else if ($this->view == 'year')
309 $day = $this->date_time->get_first_day_of_last_year();
313 return "get_previous_date_str: notdefined for this view";
315 return $day->get_date_str();
318 function get_next_date_str()
320 if ($this->view == 'month')
322 $day = $this->date_time->get_first_day_of_next_month();
325 if ($this->view == 'week' || $this->view == 'shared' )
327 $day = $this->date_time->get_first_day_of_next_week();
330 if ($this->view == 'day')
332 $day = $this->date_time->get_tomorrow();
335 if ($this->view == 'year')
337 $day = $this->date_time->get_first_day_of_next_year();
341 sugar_die("get_next_date_str: not defined for view");
343 return $day->get_date_str();
346 function get_start_slice_idx()
349 if ($this->isDayView())
353 for($i=0;$i < 8; $i++)
355 if (count($this->slice_hash[$this->slices_arr[$i]]->acts_arr) > 0)
368 function get_end_slice_idx()
370 if ( $this->view == 'month')
372 return $this->date_time->days_in_month - 1;
374 else if ( $this->view == 'week' || $this->view == 'shared')
378 else if ($this->isDayView())
382 for($i=$end_at;$i < 23; $i++)
384 if (count($this->slice_hash[$this->slices_arr[$i+1]]->acts_arr) > 0)
408 var $acts_arr = array();
410 function Slice($view,$time)
413 $this->start_time = $time;
417 $this->end_time = $this->start_time->get_day_end_time();
419 if ( $view == 'hour')
421 $this->end_time = $this->start_time->get_hour_end_time();
432 // global to switch on the offet
434 $DO_USER_TIME_OFFSET = false;
436 class CalendarActivity
442 function CalendarActivity($args)
444 // if we've passed in an array, then this is a free/busy slot
445 // and does not have a sugarbean associated to it
446 global $DO_USER_TIME_OFFSET;
448 if ( is_array ( $args ))
450 $this->start_time = $args[0];
451 $this->end_time = $args[1];
452 $this->sugar_bean = null;
456 // else do regular constructor..
460 $this->sugar_bean = $sugar_bean;
463 if ($sugar_bean->object_name == 'Task')
466 $newdate = $timedate->merge_date_time($this->sugar_bean->date_due, $this->sugar_bean->time_due);
467 $tempdate = $timedate->to_db_date($newdate,$DO_USER_TIME_OFFSET);
469 if($newdate != $tempdate){
470 $this->sugar_bean->date_due = $tempdate;
472 $temptime = $timedate->to_db_time($newdate, $DO_USER_TIME_OFFSET);
473 if($newdate != $temptime){
474 $this->sugar_bean->time_due = $temptime;
476 $this->start_time =DateTimeUtil::get_time_start(
477 $this->sugar_bean->date_due,
478 $this->sugar_bean->time_due
480 if ( empty($this->start_time))
485 $this->end_time = $this->start_time;
489 // Convert it back to database time so we can properly manage it for getting the proper start and end dates
490 $dbDate = $timedate->to_db($this->sugar_bean->date_start);
491 $this->start_time =DateTimeUtil::get_time_start($dbDate);
492 $this->end_time =DateTimeUtil::get_time_end(
494 $this->sugar_bean->duration_hours,
495 $this->sugar_bean->duration_minutes
501 function get_occurs_within_where_clause($table_name, $rel_table, $start_ts_obj, $end_ts_obj, $field_name='date_start', $view) {
503 $dtUtilArr = array();
506 $start_ts = $start_ts_obj->get_first_day_of_this_month();
507 $end_ts = $end_ts_obj->get_first_day_of_next_month();
510 // Date for the past 5 days as that is the maximum duration of a single activity
511 $dtUtilArr['ts'] = $start_ts_obj->ts - (86400*5);
512 $start_ts = new DateTimeUtil($dtUtilArr, false);
513 // Date for the next 5 days as that is the maximum duration of a single activity
514 $dtUtilArr['ts'] = $end_ts_obj->ts + (86400*5);
515 $end_ts = new DateTimeUtil($dtUtilArr, false);
519 $start_day = $timedate->getDayStartEndGMT(date($timedate->get_date_format(), $start_ts->ts));
520 $end_day = $timedate->getDayStartEndGMT(date($timedate->get_date_format(), $end_ts->ts));
522 $field_date = $GLOBALS['db']->convert($table_name.'.'.$field_name,'datetime');
524 $where = "($field_date >= '{$start_day['start']}' AND $field_date < '{$end_day['start']}'";
525 if($rel_table != '') {
526 $where .= " AND $rel_table.accept_status != 'decline'";
533 function get_freebusy_activities(&$user_focus,&$start_date_time,&$end_date_time)
538 $vcal_focus = new vCal();
539 $vcal_str = $vcal_focus->get_vcal_freebusy($user_focus);
541 $lines = explode("\n",$vcal_str);
543 foreach ($lines as $line)
545 $dates_arr = array();
546 if ( preg_match('/^FREEBUSY.*?:([^\/]+)\/([^\/]+)/i',$line,$matches))
548 $dates_arr[] =DateTimeUtil::parse_utc_date_time($matches[1]);
549 $dates_arr[] =DateTimeUtil::parse_utc_date_time($matches[2]);
550 $act_list[] = new CalendarActivity($dates_arr);
553 usort($act_list,'sort_func_by_act_date');
558 function get_activities($user_id, $show_tasks, &$view_start_time, &$view_end_time, $view) {
559 global $current_user;
564 // get all upcoming meetings, tasks due, and calls for a user
565 if(ACLController::checkAccess('Meetings', 'list', $current_user->id == $user_id)) {
566 $meeting = new Meeting();
568 if($current_user->id == $user_id) {
569 $meeting->disable_row_level_security = true;
572 $where = CalendarActivity::get_occurs_within_where_clause($meeting->table_name, $meeting->rel_users_table, $view_start_time, $view_end_time, 'date_start', $view);
573 $focus_meetings_list = build_related_list_by_user_id($meeting,$user_id,$where);
574 foreach($focus_meetings_list as $meeting) {
575 if(isset($seen_ids[$meeting->id])) {
579 $seen_ids[$meeting->id] = 1;
580 $act = new CalendarActivity($meeting);
588 if(ACLController::checkAccess('Calls', 'list',$current_user->id == $user_id)) {
591 if($current_user->id == $user_id) {
592 $call->disable_row_level_security = true;
595 $where = CalendarActivity::get_occurs_within_where_clause($call->table_name, $call->rel_users_table, $view_start_time, $view_end_time, 'date_start', $view);
596 $focus_calls_list = build_related_list_by_user_id($call,$user_id,$where);
598 foreach($focus_calls_list as $call) {
599 if(isset($seen_ids[$call->id])) {
602 $seen_ids[$call->id] = 1;
604 $act = new CalendarActivity($call);
613 if(ACLController::checkAccess('Tasks', 'list',$current_user->id == $user_id)) {
616 $where = CalendarActivity::get_occurs_within_where_clause('tasks', '', $view_start_time, $view_end_time, 'date_due', $view);
617 $where .= " AND tasks.assigned_user_id='$user_id' ";
619 $focus_tasks_list = $task->get_full_list("", $where,true);
621 if(!isset($focus_tasks_list)) {
622 $focus_tasks_list = array();
625 foreach($focus_tasks_list as $task) {
626 $act = new CalendarActivity($task);
634 usort($act_list,'sort_func_by_act_date');