2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition 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('include/utils/activity_utils.php');
45 function sort_func_by_act_date($act0,$act1)
47 if ($act0->start_time->ts == $act1->start_time->ts)
52 return ($act0->start_time->ts < $act1->start_time->ts) ? -1 : 1;
63 var $slices_arr = array();
64 // for monthly calendar view, if you want to see all the
65 // days in the grid, otherwise you only see that months
66 var $show_only_current_slice = false;
67 var $show_activities = true;
68 var $show_tasks = true;
70 var $show_week_on_month_view = true;
72 var $toggle_appt = true;
73 var $slice_hash = array();
74 var $shared_users_arr = array();
76 function __construct($view,$time_arr=array())
78 global $current_user, $timedate;
80 if ( $current_user->getPreference('time'))
82 $time = $current_user->getPreference('time');
86 $time = $sugar_config['default_time_format'];
89 if( substr_count($time, 'h') > 0)
94 if (!( $view == 'day' || $view == 'month' || $view == 'year' || $view == 'week' || $view == 'shared') )
96 sugar_die ("view needs to be one of: day, week, month, shared, or year");
101 if ( isset($time_arr['activity_focus']))
103 $this->activity_focus = new CalendarActivity($time_arr['activity_focus']);
104 $this->date_time = $this->activity_focus->start_time;
108 if(!empty($time_arr)) {
109 // FIXME: what format?
110 $this->date_time = $timedate->fromTimeArray($time_arr);
112 $this->date_time = $timedate->getNow();
116 $timedate->tzUser($this->date_time, $current_user);
117 $GLOBALS['log']->debug("CALENDATE: ".$this->date_time->format('r'));
118 $this->create_slices();
121 function add_shared_users($shared_users_arr)
123 $this->shared_users_arr = $shared_users_arr;
126 function get_view_name($view)
128 if ($view == 'month')
132 else if ($view == 'week')
136 else if ($view == 'day')
140 else if ($view == 'year')
144 else if ($view == 'shared')
150 sugar_die ("get_view_name: view ".$this->view." not supported");
154 function isDayView() {
155 return $this->view == 'day';
158 function get_slices_arr()
160 return $this->slices_arr;
164 function create_slices()
166 global $current_user;
168 if ( $this->view == 'month')
170 $days_in_month = $this->date_time->days_in_month;
172 $first_day_of_month = $this->date_time->get_day_by_index_this_month(0);
173 $num_of_prev_days = $first_day_of_month->day_of_week;
174 // do 42 slices (6x7 grid)
176 for($i=0;$i < 42;$i++)
178 $slice = new Slice('day',$this->date_time->get_day_by_index_this_month($i-$num_of_prev_days));
179 $this->slice_hash[$slice->start_time->get_mysql_date()] = $slice;
180 array_push($this->slices_arr, $slice->start_time->get_mysql_date());
184 else if ( $this->view == 'week' || $this->view == 'shared')
188 for($i=0;$i<$days_in_week;$i++)
190 $slice = new Slice('day',$this->date_time->get_day_by_index_this_week($i));
191 $this->slice_hash[$slice->start_time->get_mysql_date()] = $slice;
192 array_push($this->slices_arr, $slice->start_time->get_mysql_date());
195 else if ( $this->view == 'day')
199 for($i=0;$i<$hours_in_day;$i++)
201 $slice = new Slice('hour',$this->date_time->get_datetime_by_index_today($i));
202 $this->slice_hash[$slice->start_time->get_mysql_date().":".$slice->start_time->hour ] = $slice;
203 $this->slices_arr[] = $slice->start_time->get_mysql_date().":".$slice->start_time->hour;
206 else if ( $this->view == 'year')
211 $slice = new Slice('month',$this->date_time->get_day_by_index_this_year($i));
212 $this->slice_hash[$slice->start_time->get_mysql_date()] = $slice;
213 array_push($this->slices_arr, $slice->start_time->get_mysql_date());
218 sugar_die("not a valid view:".$this->view);
223 function add_activities($user,$type='sugar') {
225 if ( $this->view == 'week' || $this->view == 'shared') {
226 $end_date_time = $this->date_time->get("+7 days");
228 $end_date_time = $this->date_time;
233 $acts_arr = CalendarActivity::get_freebusy_activities($user, $this->date_time, $end_date_time);
235 $acts_arr = CalendarActivity::get_activities($user->id, $this->show_tasks, $this->date_time, $end_date_time, $this->view);
238 // loop thru each activity for this user
239 foreach ($acts_arr as $act) {
240 // get "hashed" time slots for the current activity we are looping through
241 $start = $timedate->tzUser($act->start_time);
242 $end = $timedate->tzUser($act->end_time);
243 $hash_list = SugarDateTime::getHashList($this->view, $start, $end);
245 for($j=0;$j < count($hash_list); $j++) {
246 if(!isset($this->slice_hash[$hash_list[$j]]) || !isset($this->slice_hash[$hash_list[$j]]->acts_arr[$user->id])) {
247 $this->slice_hash[$hash_list[$j]]->acts_arr[$user->id] = array();
249 $this->slice_hash[$hash_list[$j]]->acts_arr[$user->id][] = $act;
254 function occurs_within_slice($slice, $act)
256 // if activity starts within this slice
257 // OR activity ends within this slice
258 // OR activity starts before and ends after this slice
259 if ( ( $act->start_time->ts >= $slice->start_time->ts &&
260 $act->start_time->ts <= $slice->end_time->ts )
262 ( $act->end_time->ts >= $slice->start_time->ts &&
263 $act->end_time->ts <= $slice->end_time->ts )
265 ( $act->start_time->ts <= $slice->start_time->ts &&
266 $act->end_time->ts >= $slice->end_time->ts )
276 function get_previous_date_str()
278 if ($this->view == 'month')
280 $day = $this->date_time->get("-1 month")->get_day_begin(1);
282 else if ($this->view == 'week' || $this->view == 'shared')
284 // first day last week
285 $day = $this->date_time->get("-7 days")->get_day_by_index_this_week(0)->get_day_begin();
287 else if ($this->view == 'day')
289 $day = $this->date_time->get("yesterday")->get_day_begin();
291 else if ($this->view == 'year')
293 $day = $this->date_time->get("-1 year")->get_day_begin();
297 return "get_previous_date_str: notdefined for this view";
299 return $day->get_date_str();
302 function get_next_date_str()
304 if ($this->view == 'month')
306 $day = $this->date_time->get("+1 month")->get_day_begin(1);
309 if ($this->view == 'week' || $this->view == 'shared' )
311 $day = $this->date_time->get("+7 days")->get_day_by_index_this_week(0)->get_day_begin();
314 if ($this->view == 'day')
316 $day = $this->date_time->get("tomorrow")->get_day_begin();
319 if ($this->view == 'year')
321 $day = $this->date_time->get("+1 year")->get_day_begin();
325 sugar_die("get_next_date_str: not defined for view");
327 return $day->get_date_str();
330 function get_start_slice_idx()
333 if ($this->isDayView())
337 for($i=0;$i < 8; $i++)
339 if (count($this->slice_hash[$this->slices_arr[$i]]->acts_arr) > 0)
352 function get_end_slice_idx()
354 if ( $this->view == 'month')
356 return $this->date_time->days_in_month - 1;
358 else if ( $this->view == 'week' || $this->view == 'shared')
362 else if ($this->isDayView())
366 for($i=$end_at;$i < 23; $i++)
368 if (count($this->slice_hash[$this->slices_arr[$i+1]]->acts_arr) > 0)
392 var $acts_arr = array();
394 function Slice($view,$time)
397 $this->start_time = $time;
401 $this->end_time = $this->start_time->get_day_end_time();
403 if ( $view == 'hour')
405 $this->end_time = $this->start_time->get_hour_end_time();
416 // global to switch on the offet
418 $DO_USER_TIME_OFFSET = false;
420 class CalendarActivity
426 function CalendarActivity($args)
428 // if we've passed in an array, then this is a free/busy slot
429 // and does not have a sugarbean associated to it
430 global $DO_USER_TIME_OFFSET;
433 if ( is_array ( $args ))
435 $this->start_time = clone $args[0];
436 $this->end_time = clone $args[1];
437 $this->sugar_bean = null;
438 $timedate->tzGMT($this->start_time);
439 $timedate->tzGMT($this->end_time);
443 // else do regular constructor..
446 $this->sugar_bean = $sugar_bean;
449 if ($sugar_bean->object_name == 'Task')
451 $this->start_time = $timedate->fromUser($this->sugar_bean->date_due);
452 if ( empty($this->start_time))
457 $this->end_time = $timedate->fromUser($this->sugar_bean->date_due);
461 $this->start_time = $timedate->fromUser($this->sugar_bean->date_start);
462 if ( empty($this->start_time))
466 $hours = $this->sugar_bean->duration_hours;
470 $mins = $this->sugar_bean->duration_minutes;
474 $this->end_time = $this->start_time->get("+$hours hours $mins minutes");
476 // Convert it back to database time so we can properly manage it for getting the proper start and end dates
477 $timedate->tzGMT($this->start_time);
478 $timedate->tzGMT($this->end_time);
481 function get_occurs_within_where_clause($table_name, $rel_table, $start_ts_obj, $end_ts_obj, $field_name='date_start', $view)
484 // ensure we're working with user TZ
485 $start_ts_obj = $timedate->tzUser($start_ts_obj);
486 $end_ts_obj = $timedate->tzUser($end_ts_obj);
489 //C.L. For the start date, go back 6 days since 99 hours is the max duration (6 days)
490 $start = $start_ts_obj->get("-6 days")->get_day_begin();
491 $end = $end_ts_obj->get("first day of next month")->get_day_begin();
493 case 'freebusy': //bug: 44586, for freebusy, don't modify the start/end dates
494 $start = $start_ts_obj;
498 // Date for the past 5 days as that is the maximum duration of a single activity
499 $start = $start_ts_obj->get("-5 days")->get_day_begin();
500 $end = $start_ts_obj->get("+5 days")->get_day_end();
504 $field_date = $table_name.'.'.$field_name;
505 $start_day = $GLOBALS['db']->convert("'{$start->asDb()}'",'datetime');
506 $end_day = $GLOBALS['db']->convert("'{$end->asDb()}'",'datetime');
508 $where = "($field_date >= $start_day AND $field_date < $end_day";
509 if($rel_table != '') {
510 $where .= " AND $rel_table.accept_status != 'decline'";
517 function get_freebusy_activities($user_focus, $start_date_time, $end_date_time)
520 $vcal_focus = new vCal();
521 $vcal_str = $vcal_focus->get_vcal_freebusy($user_focus);
523 $lines = explode("\n",$vcal_str);
524 $utc = new DateTimeZone("UTC");
525 foreach ($lines as $line)
527 if ( preg_match('/^FREEBUSY.*?:([^\/]+)\/([^\/]+)/i',$line,$matches))
529 $dates_arr = array(SugarDateTime::createFromFormat(vCal::UTC_FORMAT, $matches[1], $utc),
530 SugarDateTime::createFromFormat(vCal::UTC_FORMAT, $matches[2], $utc));
531 $act_list[] = new CalendarActivity($dates_arr);
534 usort($act_list,'sort_func_by_act_date');
539 function get_activities($user_id, $show_tasks, $view_start_time, $view_end_time, $view) {
540 global $current_user;
545 // get all upcoming meetings, tasks due, and calls for a user
546 if(ACLController::checkAccess('Meetings', 'list', $current_user->id == $user_id)) {
547 $meeting = new Meeting();
549 if($current_user->id == $user_id) {
550 $meeting->disable_row_level_security = true;
553 $where = CalendarActivity::get_occurs_within_where_clause($meeting->table_name, $meeting->rel_users_table, $view_start_time, $view_end_time, 'date_start', $view);
554 $focus_meetings_list = build_related_list_by_user_id($meeting,$user_id,$where);
555 foreach($focus_meetings_list as $meeting) {
556 if(isset($seen_ids[$meeting->id])) {
560 $seen_ids[$meeting->id] = 1;
561 $act = new CalendarActivity($meeting);
569 if(ACLController::checkAccess('Calls', 'list',$current_user->id == $user_id)) {
572 if($current_user->id == $user_id) {
573 $call->disable_row_level_security = true;
576 $where = CalendarActivity::get_occurs_within_where_clause($call->table_name, $call->rel_users_table, $view_start_time, $view_end_time, 'date_start', $view);
577 $focus_calls_list = build_related_list_by_user_id($call,$user_id,$where);
579 foreach($focus_calls_list as $call) {
580 if(isset($seen_ids[$call->id])) {
583 $seen_ids[$call->id] = 1;
585 $act = new CalendarActivity($call);
594 if(ACLController::checkAccess('Tasks', 'list',$current_user->id == $user_id)) {
597 $where = CalendarActivity::get_occurs_within_where_clause('tasks', '', $view_start_time, $view_end_time, 'date_due', $view);
598 $where .= " AND tasks.assigned_user_id='$user_id' ";
600 $focus_tasks_list = $task->get_full_list("", $where,true);
602 if(!isset($focus_tasks_list)) {
603 $focus_tasks_list = array();
606 foreach($focus_tasks_list as $task) {
607 $act = new CalendarActivity($task);
615 usort($act_list,'sort_func_by_act_date');