]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Calendar/CalendarUtils.php
Release 6.5.10
[Github/sugarcrm.git] / modules / Calendar / CalendarUtils.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-2013 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 class CalendarUtils {
40
41         /**
42          * Find first day of week according to user's settings
43          * @param SugarDateTime $date
44          * @return SugarDateTime $date
45          */
46         static function get_first_day_of_week(SugarDateTime $date){
47                 $fdow = $GLOBALS['current_user']->get_first_day_of_week();
48                 if($date->day_of_week < $fdow)
49                                 $date = $date->get('-7 days');
50                 return $date->get_day_by_index_this_week($fdow);
51         }
52
53
54         /**
55          * Get list of needed fields for modules
56          * @return array
57          */
58         static function get_fields(){
59                 return array(
60                         'Meetings' => array(
61                                 'name',
62                                 'duration_hours',
63                                 'duration_minutes',
64                                 'status',
65                                 'related_to',
66                         ),
67                         'Calls' => array(
68                                 'name',
69                                 'duration_hours',
70                                 'duration_minutes',
71                                 'status',
72                                 'related_to',
73                         ),
74                         'Tasks' => array(
75                                 'name',
76                                 'status',
77                                 'related_to',
78                         ),
79                 );
80         }
81
82         /**
83          * Get array of needed time data
84          * @param SugarBean $bean
85          * @return array
86          */
87         static function get_time_data(SugarBean $bean){
88                                         $arr = array();
89
90                                         $start_field = "date_start";
91                                         $end_field = "date_end";
92
93                                         if($bean->object_name == 'Task')
94                                                 $start_field = $end_field = "date_due";
95                                         if(empty($bean->$start_field))
96                                                 return array();
97                                         if(empty($bean->$end_field))
98                                                 $bean->$end_field = $bean->$start_field;
99
100                                         $timestamp = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$bean->$start_field,new DateTimeZone('UTC'))->format('U');
101                                         $arr['timestamp'] = $timestamp;
102                                         $arr['time_start'] = $GLOBALS['timedate']->fromTimestamp($arr['timestamp'])->format($GLOBALS['timedate']->get_time_format());
103                                         $date_start = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$bean->$start_field,new DateTimeZone('UTC'));
104                                         $arr['ts_start'] = $date_start->get("-".$date_start->format("H")." hours -".$date_start->format("i")." minutes -".$date_start->format("s")." seconds")->format('U');
105                                         $arr['offset'] = $date_start->format('H') * 3600 + $date_start->format('i') * 60;
106                                         $date_end = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$bean->$end_field,new DateTimeZone('UTC'));
107                                         if($bean->object_name != 'Task')
108                                                 $date_end->modify("-1 minute");
109                                         $arr['ts_end'] = $date_end->get("+1 day")->get("-".$date_end->format("H")." hours -".$date_end->format("i")." minutes -".$date_end->format("s")." seconds")->format('U');
110                                         $arr['days'] = ($arr['ts_end'] - $arr['ts_start']) / (3600*24);
111
112                                         return $arr;
113         }
114
115
116         /**
117          * Get array that will be sent back to ajax frontend
118          * @param SugarBean $bean
119          * @return array
120          */
121         static function get_sendback_array(SugarBean $bean){
122
123                         if(isset($bean->parent_name) && isset($_REQUEST['parent_name']))
124                                 $bean->parent_name = $_REQUEST['parent_name'];
125
126                         $users = array();
127                         if($bean->object_name == 'Call')
128                                 $users = $bean->get_call_users();
129                         else if($bean->object_name == 'Meeting')
130                                 $users = $bean->get_meeting_users();
131                         $user_ids = array();
132                         foreach($users as $u)
133                                 $user_ids[] = $u->id;
134
135                         $field_list = CalendarUtils::get_fields();
136                         $field_arr = array();
137                         foreach($field_list[$bean->module_dir] as $field){
138                             if ($field == 'related_to')
139                             {
140                                 $focus = BeanFactory::getBean($bean->parent_type, $bean->parent_id);
141                                 $field_arr[$field] = $focus->name;
142                             }
143                             else
144                             {
145                                 $field_arr[$field] = $bean->$field;
146                             }
147                         }
148
149                         $date_field = "date_start";
150                         if($bean->object_name == 'Task')
151                                 $date_field = "date_due";
152
153                         $arr = array(
154                                 'access' => 'yes',
155                                 'type' => strtolower($bean->object_name),
156                                 'module_name' => $bean->module_dir,
157                                 'user_id' => $GLOBALS['current_user']->id,
158                                 'detail' => 1,
159                                 'edit' => 1,
160                                 'name' => $bean->name,
161                                 'record' => $bean->id,
162                                 'users' => $user_ids,
163                         );
164                         if(!empty($bean->repeat_parent_id))
165                                 $arr['repeat_parent_id'] = $bean->repeat_parent_id;
166                         $arr = array_merge($arr,$field_arr);
167                         $arr = array_merge($arr,CalendarUtils::get_time_data($bean));
168
169                         return $arr;
170         }
171
172         /**
173          * Get array of repeat data
174          * @param SugarBean $bean
175          * @return array
176          */
177          static function get_sendback_repeat_data(SugarBean $bean){
178                 if ($bean->module_dir == "Meetings" || $bean->module_dir == "Calls") {
179                         if(!empty($bean->repeat_parent_id) || (!empty($bean->repeat_type) && empty($_REQUEST['edit_all_recurrences']))){
180                                 if(!empty($bean->repeat_parent_id))
181                                         $repeat_parent_id = $bean->repeat_parent_id;
182                                 else
183                                         $repeat_parent_id = $bean->id;
184                                 return array("repeat_parent_id" => $repeat_parent_id);
185                         }
186
187                         $arr = array();
188                         if(!empty($bean->repeat_type)){
189                                 $arr = array(
190                                         'repeat_type' => $bean->repeat_type,
191                                         'repeat_interval' => $bean->repeat_interval,
192                                         'repeat_dow' => $bean->repeat_dow,
193                                         'repeat_until' => $bean->repeat_until,
194                                         'repeat_count' => $bean->repeat_count,
195                                 );
196                         }
197
198                         // TODO CHECK DATETIME VARIABLE
199                         if(!empty($_REQUEST['date_start'])){
200                                 $date_start = $_REQUEST['date_start'];
201                         }else
202                                 $date_start = $bean->date_start;
203
204                         $date = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$date_start);
205                         $arr = array_merge($arr,array(
206                                 'current_dow' => $date->format("w"),
207                                 'default_repeat_until' => $date->get("+1 Month")->format($GLOBALS['timedate']->get_date_format()),
208                         ));
209
210                         return $arr;
211                 }
212                 return false;
213          }
214
215         /**
216          * Build array of datetimes for recurring meetings
217          * @param string $date_start
218          * @param array $params
219          * @return array
220          */
221         static function build_repeat_sequence($date_start,$params){
222
223                 $arr = array();
224
225                 $type = $params['type'];
226                 $interval = intval($params['interval']);
227                 if($interval < 1)
228                         $interval = 1;
229
230                 if(!empty($params['count'])){
231                         $count = $params['count'];
232                         if($count < 1)
233                                 $count = 1;
234                 }else
235                         $count = 0;
236
237                 if(!empty($params['until'])){
238                         $until = $params['until'];
239                 }else
240                         $until = $date_start;
241
242                 if($type == "Weekly"){
243                         $dow = $params['dow'];
244                         if($dow == ""){
245                                 return array();
246                         }
247                 }
248
249                 /** 
250                  * @var SugarDateTime $start Recurrence start date.
251                  */
252                 $start = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$date_start);
253                 /** 
254                  * @var SugarDateTime $end Recurrence end date. Used if recurrence ends by date.
255                  */
256                  
257                 if (!empty($params['until'])) {
258                         $end = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_format(), $until);
259                         $end->modify("+1 Day");
260                 } else {
261                         $end = $start;
262                 }
263                 $current = clone $start;
264
265                 $i = 1; // skip the first iteration
266                 $w = $interval; // for week iteration
267                 $last_dow = $start->format("w");
268
269                 $limit = SugarConfig::getInstance()->get('calendar.max_repeat_count',1000);
270
271                 while($i < $count || ($count == 0 && $current->format("U") < $end->format("U"))){
272                         $skip = false;
273                         switch($type){
274                                 case "Daily":
275                                         $current->modify("+{$interval} Days");
276                                         break;
277                                 case "Weekly":
278                                         $day_index = $last_dow;
279                                         for($d = $last_dow + 1; $d <= $last_dow + 7; $d++){
280                                                 $day_index = $d % 7;
281                                                 if(strpos($dow,(string)($day_index)) !== false){
282                                                         break;
283                                                 }
284                                         }
285                                         $step = $day_index - $last_dow;
286                                         $last_dow = $day_index;
287                                         if($step <= 0){
288                                                 $step += 7;
289                                                 $w++;
290                                         }
291                                         if($w % $interval != 0)
292                                                 $skip = true;
293
294                                         $current->modify("+{$step} Days");
295                                         break;
296                                 case "Monthly":
297                                         $current->modify("+{$interval} Months");
298                                         break;
299                                 case "Yearly":
300                                         $current->modify("+{$interval} Years");
301                                         break;
302                                 default:
303                                         return array();
304                         }
305
306                         if($skip)
307                                 continue;
308
309                         if(($i < $count || $count == 0 && $current->format("U") < $end->format("U"))  ){
310                                 $arr[] = $current->format($GLOBALS['timedate']->get_date_time_format());
311                         }
312                         $i++;
313
314                         if($i > $limit + 100)
315                                 break;
316                 }
317                 return $arr;
318         }
319
320         /**
321          * Save repeat activities
322          * @param SugarBean $bean
323          * @param array $time_arr array of datetimes
324          * @return array
325          */
326         static function save_repeat_activities(SugarBean $bean,$time_arr){
327
328                 // Here we will create single big inserting query for each invitee relationship
329                 // rather than using relationships framework due to performance issues.
330                 // Relationship framework runs very slowly
331
332                 global $db;
333                 $id = $bean->id;
334                 $date_modified = $GLOBALS['timedate']->nowDb();
335                 $lower_name = strtolower($bean->object_name);
336
337                 $qu = "SELECT * FROM {$bean->rel_users_table} WHERE deleted = 0 AND {$lower_name}_id = '{$id}'";
338                 $re = $db->query($qu);
339                 $users_rel_arr = array();
340                 while($ro = $db->fetchByAssoc($re))
341                         $users_rel_arr[] = $ro['user_id'];
342                 $qu_users = "
343                                 INSERT INTO {$bean->rel_users_table}
344                                 (id,user_id,{$lower_name}_id,date_modified)
345                                 VALUES
346                 ";
347                 $users_filled = false;
348
349                 $qu = "SELECT * FROM {$bean->rel_contacts_table} WHERE deleted = 0 AND {$lower_name}_id = '{$id}'";
350                 $re = $db->query($qu);
351                 $contacts_rel_arr = array();
352                 while($ro = $db->fetchByAssoc($re))
353                         $contacts_rel_arr[] = $ro['contact_id'];
354                 $qu_contacts = "
355                                 INSERT INTO {$bean->rel_contacts_table}
356                                 (id,contact_id,{$lower_name}_id,date_modified)
357                                 VALUES
358                 ";
359                 $contacts_filled = false;
360
361                 $qu = "SELECT * FROM {$bean->rel_leads_table} WHERE deleted = 0 AND {$lower_name}_id = '{$id}'";
362                 $re = $db->query($qu);
363                 $leads_rel_arr = array();
364                 while($ro = $db->fetchByAssoc($re))
365                         $leads_rel_arr[] = $ro['lead_id'];
366                 $qu_leads = "
367                                 INSERT INTO {$bean->rel_leads_table}
368                                 (id,lead_id,{$lower_name}_id,date_modified)
369                                 VALUES
370                 ";
371                 $leads_filled = false;
372
373                 $arr = array();
374                 $i = 0;
375                 foreach($time_arr as $date_start){
376                         $clone = $bean; // we don't use clone keyword cause not necessary
377                         $clone->id = "";
378                         $clone->date_start = $date_start;
379                         // TODO CHECK DATETIME VARIABLE
380                         $date = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$date_start);
381                         $date = $date->get("+{$bean->duration_hours} Hours")->get("+{$bean->duration_minutes} Minutes");
382                         $date_end = $date->format($GLOBALS['timedate']->get_date_time_format());
383                         $clone->date_end = $date_end;
384                         $clone->recurring_source = "Sugar";
385                         $clone->repeat_parent_id = $id;
386                         $clone->update_vcal = false;
387                         $clone->save(false);
388
389                         if($clone->id){
390                                 foreach($users_rel_arr as $user_id){
391                                         if($users_filled)
392                                                 $qu_users .= ",".PHP_EOL;
393                                         $qu_users .= "('".create_guid()."','{$user_id}','{$clone->id}','{$date_modified}')";
394                                         $users_filled = true;
395                                 }
396                                 foreach($contacts_rel_arr as $contact_id){
397                                         if($contacts_filled)
398                                                 $qu_contacts .= ",".PHP_EOL;
399                                         $qu_contacts .= "('".create_guid()."','{$contact_id}','{$clone->id}','{$date_modified}')";
400                                         $contacts_filled = true;
401                                 }
402                                 foreach($leads_rel_arr as $lead_id){
403                                         if($leads_filled)
404                                                 $qu_leads .= ",".PHP_EOL;
405                                         $qu_leads .= "('".create_guid()."','{$lead_id}','{$clone->id}','{$date_modified}')";
406                                         $leads_filled = true;
407                                 }
408                                 if($i < 44){
409                                         $clone->date_start = $date_start;
410                                         $clone->date_end = $date_end;
411                                         $arr[] = array_merge(array('id' => $clone->id),CalendarUtils::get_time_data($clone));
412                                 }
413                                 $i++;
414                         }
415                 }
416                 
417                 if ($users_filled) {
418                         $db->query($qu_users);
419                 }
420                 if ($contacts_filled) {
421                         $db->query($qu_contacts);
422                 }               
423                 if ($leads_filled) {
424                         $db->query($qu_leads);
425                 }
426                 
427                 vCal::cache_sugar_vcal($GLOBALS['current_user']);
428                 return $arr;
429         }
430
431         /**
432          * Delete recurring activities and their invitee relationships
433          * @param SugarBean $bean
434          */
435         static function markRepeatDeleted(SugarBean $bean)
436         {
437                 // we don't use mark_deleted method here because it runs very slowly
438                 global $db;
439                 $date_modified = $GLOBALS['timedate']->nowDb();
440                 if(!empty($GLOBALS['current_user']))
441                         $modified_user_id = $GLOBALS['current_user']->id;
442                 else
443                         $modified_user_id = 1;
444                 $lower_name = strtolower($bean->object_name);
445
446                 $qu = "SELECT id FROM {$bean->table_name} WHERE repeat_parent_id = '{$bean->id}' AND deleted = 0";
447                 $re = $db->query($qu);
448                 while( $ro = $db->fetchByAssoc($re)) {
449                         $id = $ro['id'];
450                         $date_modified = $GLOBALS['timedate']->nowDb();
451                         $db->query("UPDATE {$bean->table_name} SET deleted = 1, date_modified = '{$date_modified}', modified_user_id = '{$modified_user_id}' WHERE id = '{$id}'");
452                         $db->query("UPDATE {$bean->rel_users_table} SET deleted = 1, date_modified = '{$date_modified}' WHERE {$lower_name}_id = '{$id}'");
453                         $db->query("UPDATE {$bean->rel_contacts_table} SET deleted = 1, date_modified = '{$date_modified}' WHERE {$lower_name}_id = '{$id}'");
454                         $db->query("UPDATE {$bean->rel_leads_table} SET deleted = 1, date_modified = '{$date_modified}' WHERE {$lower_name}_id = '{$id}'");
455                 }
456                 vCal::cache_sugar_vcal($GLOBALS['current_user']);
457         }
458
459         /**
460          * check if meeting has repeat children and pass repeat_parent over to the 2nd meeting in sequence
461          * @param SugarBean $bean
462          * @param string $beanId
463          */
464         static function correctRecurrences(SugarBean $bean, $beanId)
465         {
466                 global $db;
467                 
468                 $qu = "SELECT id FROM {$bean->table_name} WHERE repeat_parent_id = '{$beanId}' AND deleted = 0 ORDER BY date_start";
469                 $re = $db->query($qu);
470
471                 $i = 0;
472                 while ($ro = $db->fetchByAssoc($re)) {
473                         $id = $ro['id'];
474                         if($i == 0){
475                                 $new_parent_id = $id;
476                                 $qu = "UPDATE {$bean->table_name} SET repeat_parent_id = '' AND recurring_source = '' WHERE id = '{$id}'";
477                         }else{
478                                 $qu = "UPDATE {$bean->table_name} SET repeat_parent_id = '{$new_parent_id}' WHERE id = '{$id}'";
479                         }
480                         $db->query($qu);
481                 $i++;
482                 }
483         }
484
485 }