]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Calendar/Calendar.php
Release 6.2.0
[Github/sugarcrm.git] / modules / Calendar / Calendar.php
1 <?php
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.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38 /*********************************************************************************
39
40  ********************************************************************************/
41
42
43 require_once('include/utils/activity_utils.php');
44
45 function sort_func_by_act_date($act0,$act1)
46 {
47         if ($act0->start_time->ts == $act1->start_time->ts)
48         {
49                 return 0;
50         }
51
52         return ($act0->start_time->ts < $act1->start_time->ts) ? -1 : 1;
53 }
54
55 class Calendar
56 {
57     var $view = 'month';
58         /**
59          * Current date
60          * @var SugarDateTime
61          */
62         var $date_time;
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;
69         var $activity_focus;
70         var $show_week_on_month_view = true;
71         var $use_24 = 1;
72         var $toggle_appt = true;
73         var $slice_hash = array();
74         var $shared_users_arr = array();
75
76         function __construct($view,$time_arr=array())
77         {
78                 global $current_user, $timedate;
79                 global $sugar_config;
80                 if ( $current_user->getPreference('time'))
81                 {
82                         $time = $current_user->getPreference('time');
83                 }
84                 else
85                 {
86                         $time = $sugar_config['default_time_format'];
87                 }
88
89                 if( substr_count($time, 'h') > 0)
90                 {
91                         $this->use_24 = 0;
92                 }
93
94                 if (!( $view == 'day' || $view == 'month' || $view == 'year' || $view == 'week' || $view == 'shared') )
95                 {
96                         sugar_die ("view needs to be one of: day, week, month, shared, or year");
97                 }
98
99                 $this->view = $view;
100
101                 if ( isset($time_arr['activity_focus']))
102                 {
103                         $this->activity_focus =  new CalendarActivity($time_arr['activity_focus']);
104                         $this->date_time =  $this->activity_focus->start_time;
105                 }
106                 else
107                 {
108                     if(!empty($time_arr)) {
109                         // FIXME: what format?
110                             $this->date_time = $timedate->fromTimeArray($time_arr);
111                     } else {
112                         $this->date_time = $timedate->getNow();
113                     }
114                 }
115
116                 $timedate->tzUser($this->date_time, $current_user);
117         $GLOBALS['log']->debug("CALENDATE: ".$this->date_time->format('r'));
118                 $this->create_slices();
119
120         }
121         function add_shared_users($shared_users_arr)
122         {
123                 $this->shared_users_arr = $shared_users_arr;
124         }
125
126         function get_view_name($view)
127         {
128                 if ($view == 'month')
129                 {
130                         return "MONTH";
131                 }
132                 else if ($view == 'week')
133                 {
134                         return "WEEK";
135                 }
136                 else if ($view == 'day')
137                 {
138                         return "DAY";
139                 }
140                 else if ($view == 'year')
141                 {
142                         return "YEAR";
143                 }
144                 else if ($view == 'shared')
145                 {
146                         return "SHARED";
147                 }
148                 else
149                 {
150                         sugar_die ("get_view_name: view ".$this->view." not supported");
151                 }
152         }
153
154     function isDayView() {
155         return $this->view == 'day';
156     }
157
158         function get_slices_arr()
159         {
160                 return $this->slices_arr;
161         }
162
163
164         function create_slices()
165         {
166                 global $current_user;
167
168                 if ( $this->view == 'month')
169                 {
170                         $days_in_month = $this->date_time->days_in_month;
171
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)
175
176                         for($i=0;$i < 42;$i++)
177                         {
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());
181                         }
182
183                 }
184                 else if ( $this->view == 'week' || $this->view == 'shared')
185                 {
186                         $days_in_week = 7;
187
188                         for($i=0;$i<$days_in_week;$i++)
189                         {
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());
193                         }
194                 }
195                 else if ( $this->view == 'day')
196                 {
197                         $hours_in_day = 24;
198
199                         for($i=0;$i<$hours_in_day;$i++)
200                         {
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;
204                         }
205                 }
206                 else if ( $this->view == 'year')
207                 {
208
209                         for($i=0;$i<12;$i++)
210                         {
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());
214                         }
215                 }
216                 else
217                 {
218                         sugar_die("not a valid view:".$this->view);
219                 }
220
221         }
222
223         function add_activities($user,$type='sugar') {
224             global $timedate;
225                 if ( $this->view == 'week' || $this->view == 'shared') {
226                         $end_date_time = $this->date_time->get("+7 days");
227                 } else {
228                         $end_date_time = $this->date_time;
229                 }
230
231                 $acts_arr = array();
232         if($type == 'vfb') {
233                         $acts_arr = CalendarActivity::get_freebusy_activities($user, $this->date_time, $end_date_time);
234         } else {
235                         $acts_arr = CalendarActivity::get_activities($user->id, $this->show_tasks, $this->date_time, $end_date_time, $this->view);
236         }
237
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);
244
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();
248                                 }
249                                 $this->slice_hash[$hash_list[$j]]->acts_arr[$user->id][] = $act;
250                         }
251                 }
252         }
253
254         function occurs_within_slice($slice, $act)
255         {
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 )
261                         ||
262                         ( $act->end_time->ts >= $slice->start_time->ts &&
263                         $act->end_time->ts <= $slice->end_time->ts )
264                         ||
265                         ( $act->start_time->ts <= $slice->start_time->ts &&
266                         $act->end_time->ts >= $slice->end_time->ts )
267                 )
268                 {
269                         return true;
270                 }
271
272                 return false;
273
274         }
275
276         function get_previous_date_str()
277         {
278                 if ($this->view == 'month')
279                 {
280                     $day = $this->date_time->get("-1 month")->get_day_begin(1);
281                 }
282                 else if ($this->view == 'week' || $this->view == 'shared')
283                 {
284                     // first day last week
285                         $day = $this->date_time->get("-7 days")->get_day_by_index_this_week(0)->get_day_begin();
286                 }
287                 else if ($this->view == 'day')
288                 {
289                         $day = $this->date_time->get("yesterday")->get_day_begin();
290                 }
291                 else if ($this->view == 'year')
292                 {
293             $day = $this->date_time->get("-1 year")->get_day_begin();
294                 }
295                 else
296                 {
297                         return "get_previous_date_str: notdefined for this view";
298                 }
299                 return $day->get_date_str();
300         }
301
302         function get_next_date_str()
303         {
304                 if ($this->view == 'month')
305                 {
306                         $day = $this->date_time->get("+1 month")->get_day_begin(1);
307                 }
308                 else
309                 if ($this->view == 'week' || $this->view == 'shared' )
310                 {
311                         $day = $this->date_time->get("+7 days")->get_day_by_index_this_week(0)->get_day_begin();
312                 }
313                 else
314                 if ($this->view == 'day')
315                 {
316                         $day = $this->date_time->get("tomorrow")->get_day_begin();
317                 }
318                 else
319                 if ($this->view == 'year')
320                 {
321                         $day = $this->date_time->get("+1 year")->get_day_begin();
322                 }
323                 else
324                 {
325                         sugar_die("get_next_date_str: not defined for view");
326                 }
327                 return $day->get_date_str();
328         }
329
330         function get_start_slice_idx()
331         {
332
333                 if ($this->isDayView())
334                 {
335                         $start_at = 8;
336
337                         for($i=0;$i < 8; $i++)
338                         {
339                                 if (count($this->slice_hash[$this->slices_arr[$i]]->acts_arr) > 0)
340                                 {
341                                         $start_at = $i;
342                                         break;
343                                 }
344                         }
345                         return $start_at;
346                 }
347                 else
348                 {
349                         return 0;
350                 }
351         }
352         function get_end_slice_idx()
353         {
354                 if ( $this->view == 'month')
355                 {
356                         return $this->date_time->days_in_month - 1;
357                 }
358                 else if ( $this->view == 'week' || $this->view == 'shared')
359                 {
360                         return 6;
361                 }
362                 else if ($this->isDayView())
363                 {
364                         $end_at = 18;
365
366                         for($i=$end_at;$i < 23; $i++)
367                         {
368                                 if (count($this->slice_hash[$this->slices_arr[$i+1]]->acts_arr) > 0)
369                                 {
370                                         $end_at = $i + 1;
371                                 }
372                         }
373
374
375                         return $end_at;
376
377                 }
378                 else
379                 {
380                         return 1;
381                 }
382         }
383
384
385 }
386
387 class Slice
388 {
389         var $view = 'day';
390         var $start_time;
391         var $end_time;
392         var $acts_arr = array();
393
394         function Slice($view,$time)
395         {
396                 $this->view = $view;
397                 $this->start_time = $time;
398
399                 if ( $view == 'day')
400                 {
401                         $this->end_time = $this->start_time->get_day_end_time();
402                 }
403                 if ( $view == 'hour')
404                 {
405                         $this->end_time = $this->start_time->get_hour_end_time();
406                 }
407
408         }
409         function get_view()
410         {
411                 return $this->view;
412         }
413
414 }
415
416 // global to switch on the offet
417
418 $DO_USER_TIME_OFFSET = false;
419
420 class CalendarActivity
421 {
422         var $sugar_bean;
423         var $start_time;
424         var $end_time;
425
426         function CalendarActivity($args)
427         {
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;
431                 global $timedate;
432
433         if ( is_array ( $args ))
434         {
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);
440            return;
441         }
442
443     // else do regular constructor..
444
445         $sugar_bean = $args;
446                 $this->sugar_bean = $sugar_bean;
447
448
449                 if ($sugar_bean->object_name == 'Task')
450                 {
451                     $this->start_time = $timedate->fromUser($this->sugar_bean->date_due);
452                         if ( empty($this->start_time))
453                         {
454                                 return null;
455                         }
456
457                         $this->end_time = $timedate->fromUser($this->sugar_bean->date_due);
458                 }
459                 else
460                 {
461             $this->start_time = $timedate->fromUser($this->sugar_bean->date_start);
462                         if ( empty($this->start_time))
463                         {
464                             return null;
465                         }
466                         $hours = $this->sugar_bean->duration_hours;
467                         if(empty($hours)) {
468                             $hours = 0;
469                         }
470                         $mins = $this->sugar_bean->duration_minutes;
471                         if(empty($mins)) {
472                             $mins = 0;
473                         }
474                         $this->end_time = $this->start_time->get("+$hours hours $mins minutes");
475                 }
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);
479         }
480
481         function get_occurs_within_where_clause($table_name, $rel_table, $start_ts_obj, $end_ts_obj, $field_name='date_start', $view)
482         {
483                 global $timedate;
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);
487                 switch ($view) {
488                         case 'month':
489                                 $start = $start_ts_obj->get_day_begin(1);
490                                 $end = $end_ts_obj->get("first day of next month")->get_day_begin();
491                                 break;
492                         default:
493                                 // Date for the past 5 days as that is the maximum duration of a single activity
494                                 $start = $start_ts_obj->get("-5 days")->get_day_begin();
495                                 $end =  $start_ts_obj->get("+5 days")->get_day_end();
496                                 break;
497                 }
498
499                 $field_date = $GLOBALS['db']->convert($table_name.'.'.$field_name,'datetime');
500         $start_day = $start->asDb();
501         $end_day = $end->asDb();
502
503                 $where = "($field_date >= '{$start_day}' AND $field_date < '{$end_day}'";
504         if($rel_table != '') {
505             $where .= " AND $rel_table.accept_status != 'decline'";
506         }
507                 
508                 $where .= ")";
509                 return $where;
510         }
511
512   function get_freebusy_activities($user_focus, $start_date_time, $end_date_time)
513   {
514           $act_list = array();
515       $vcal_focus = new vCal();
516       $vcal_str = $vcal_focus->get_vcal_freebusy($user_focus);
517
518       $lines = explode("\n",$vcal_str);
519       $utc = new DateTimeZone("UTC");
520       foreach ($lines as $line)
521       {
522         if ( preg_match('/^FREEBUSY.*?:([^\/]+)\/([^\/]+)/i',$line,$matches))
523         {
524           $dates_arr = array(SugarDateTime::createFromFormat(vCal::UTC_FORMAT, $matches[1], $utc),
525                               SugarDateTime::createFromFormat(vCal::UTC_FORMAT, $matches[2], $utc));
526           $act_list[] = new CalendarActivity($dates_arr);
527         }
528       }
529                   usort($act_list,'sort_func_by_act_date');
530       return $act_list;
531   }
532
533
534         function get_activities($user_id, $show_tasks, $view_start_time, $view_end_time, $view) {
535                 global $current_user;
536                 $act_list = array();
537                 $seen_ids = array();
538
539
540                 // get all upcoming meetings, tasks due, and calls for a user
541                 if(ACLController::checkAccess('Meetings', 'list', $current_user->id == $user_id)) {
542                         $meeting = new Meeting();
543
544                         if($current_user->id  == $user_id) {
545                                 $meeting->disable_row_level_security = true;
546                         }
547
548                         $where = CalendarActivity::get_occurs_within_where_clause($meeting->table_name, $meeting->rel_users_table, $view_start_time, $view_end_time, 'date_start', $view);
549                         $focus_meetings_list = build_related_list_by_user_id($meeting,$user_id,$where);
550                         foreach($focus_meetings_list as $meeting) {
551                                 if(isset($seen_ids[$meeting->id])) {
552                                         continue;
553                                 }
554
555                                 $seen_ids[$meeting->id] = 1;
556                                 $act = new CalendarActivity($meeting);
557
558                                 if(!empty($act)) {
559                                         $act_list[] = $act;
560                                 }
561                         }
562                 }
563
564                 if(ACLController::checkAccess('Calls', 'list',$current_user->id  == $user_id)) {
565                         $call = new Call();
566
567                         if($current_user->id  == $user_id) {
568                                 $call->disable_row_level_security = true;
569                         }
570
571                         $where = CalendarActivity::get_occurs_within_where_clause($call->table_name, $call->rel_users_table, $view_start_time, $view_end_time, 'date_start', $view);
572                         $focus_calls_list = build_related_list_by_user_id($call,$user_id,$where);
573
574                         foreach($focus_calls_list as $call) {
575                                 if(isset($seen_ids[$call->id])) {
576                                         continue;
577                                 }
578                                 $seen_ids[$call->id] = 1;
579
580                                 $act = new CalendarActivity($call);
581                                 if(!empty($act)) {
582                                         $act_list[] = $act;
583                                 }
584                         }
585                 }
586
587
588                 if($show_tasks) {
589                         if(ACLController::checkAccess('Tasks', 'list',$current_user->id == $user_id)) {
590                                 $task = new Task();
591
592                                 $where = CalendarActivity::get_occurs_within_where_clause('tasks', '', $view_start_time, $view_end_time, 'date_due', $view);
593                                 $where .= " AND tasks.assigned_user_id='$user_id' ";
594
595                                 $focus_tasks_list = $task->get_full_list("", $where,true);
596
597                                 if(!isset($focus_tasks_list)) {
598                                         $focus_tasks_list = array();
599                                 }
600
601                                 foreach($focus_tasks_list as $task) {
602                                         $act = new CalendarActivity($task);
603                                         if(!empty($act)) {
604                                                 $act_list[] = $act;
605                                         }
606                                 }
607                         }
608                 }
609
610                 usort($act_list,'sort_func_by_act_date');
611                 return $act_list;
612         }
613 }