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.
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 ********************************************************************************/
42 * Find first day of week according to user's settings
43 * @param SugarDateTime $date
44 * @return SugarDateTime $date
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);
55 * Get list of needed fields for modules
58 static function get_fields(){
83 * Get array of needed time data
84 * @param SugarBean $bean
87 static function get_time_data(SugarBean $bean){
90 $start_field = "date_start";
91 $end_field = "date_end";
93 if($bean->object_name == 'Task')
94 $start_field = $end_field = "date_due";
95 if(empty($bean->$start_field))
97 if(empty($bean->$end_field))
98 $bean->$end_field = $bean->$start_field;
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);
117 * Get array that will be sent back to ajax frontend
118 * @param SugarBean $bean
121 static function get_sendback_array(SugarBean $bean){
123 if(isset($bean->parent_name) && isset($_REQUEST['parent_name']))
124 $bean->parent_name = $_REQUEST['parent_name'];
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();
132 foreach($users as $u)
133 $user_ids[] = $u->id;
135 $field_list = CalendarUtils::get_fields();
136 $field_arr = array();
137 foreach($field_list[$bean->module_dir] as $field){
138 if ($field == 'related_to')
140 $focus = BeanFactory::getBean($bean->parent_type, $bean->parent_id);
141 $field_arr[$field] = $focus->name;
145 $field_arr[$field] = $bean->$field;
149 $date_field = "date_start";
150 if($bean->object_name == 'Task')
151 $date_field = "date_due";
155 'type' => strtolower($bean->object_name),
156 'module_name' => $bean->module_dir,
157 'user_id' => $GLOBALS['current_user']->id,
160 'name' => $bean->name,
161 'record' => $bean->id,
162 'users' => $user_ids,
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));
173 * Get array of repeat data
174 * @param SugarBean $bean
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;
183 $repeat_parent_id = $bean->id;
184 return array("repeat_parent_id" => $repeat_parent_id);
188 if(!empty($bean->repeat_type)){
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,
198 // TODO CHECK DATETIME VARIABLE
199 if(!empty($_REQUEST['date_start'])){
200 $date_start = $_REQUEST['date_start'];
202 $date_start = $bean->date_start;
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()),
216 * Build array of datetimes for recurring meetings
217 * @param string $date_start
218 * @param array $params
221 static function build_repeat_sequence($date_start,$params){
225 $type = $params['type'];
226 $interval = intval($params['interval']);
230 if(!empty($params['count'])){
231 $count = $params['count'];
237 if(!empty($params['until'])){
238 $until = $params['until'];
240 $until = $date_start;
242 if($type == "Weekly"){
243 $dow = $params['dow'];
250 * @var SugarDateTime $start Recurrence start date.
252 $start = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_time_format(),$date_start);
254 * @var SugarDateTime $end Recurrence end date. Used if recurrence ends by date.
257 if (!empty($params['until'])) {
258 $end = SugarDateTime::createFromFormat($GLOBALS['timedate']->get_date_format(), $until);
259 $end->modify("+1 Day");
263 $current = clone $start;
265 $i = 1; // skip the first iteration
266 $w = $interval; // for week iteration
267 $last_dow = $start->format("w");
269 $limit = SugarConfig::getInstance()->get('calendar.max_repeat_count',1000);
271 while($i < $count || ($count == 0 && $current->format("U") < $end->format("U"))){
275 $current->modify("+{$interval} Days");
278 $day_index = $last_dow;
279 for($d = $last_dow + 1; $d <= $last_dow + 7; $d++){
281 if(strpos($dow,(string)($day_index)) !== false){
285 $step = $day_index - $last_dow;
286 $last_dow = $day_index;
291 if($w % $interval != 0)
294 $current->modify("+{$step} Days");
297 $current->modify("+{$interval} Months");
300 $current->modify("+{$interval} Years");
309 if(($i < $count || $count == 0 && $current->format("U") < $end->format("U")) ){
310 $arr[] = $current->format($GLOBALS['timedate']->get_date_time_format());
314 if($i > $limit + 100)
321 * Save repeat activities
322 * @param SugarBean $bean
323 * @param array $time_arr array of datetimes
326 static function save_repeat_activities(SugarBean $bean,$time_arr){
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
334 $date_modified = $GLOBALS['timedate']->nowDb();
335 $lower_name = strtolower($bean->object_name);
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'];
343 INSERT INTO {$bean->rel_users_table}
344 (id,user_id,{$lower_name}_id,date_modified)
347 $users_filled = false;
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'];
355 INSERT INTO {$bean->rel_contacts_table}
356 (id,contact_id,{$lower_name}_id,date_modified)
359 $contacts_filled = false;
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'];
367 INSERT INTO {$bean->rel_leads_table}
368 (id,lead_id,{$lower_name}_id,date_modified)
371 $leads_filled = false;
375 foreach($time_arr as $date_start){
376 $clone = $bean; // we don't use clone keyword cause not necessary
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;
390 foreach($users_rel_arr as $user_id){
392 $qu_users .= ",".PHP_EOL;
393 $qu_users .= "('".create_guid()."','{$user_id}','{$clone->id}','{$date_modified}')";
394 $users_filled = true;
396 foreach($contacts_rel_arr as $contact_id){
398 $qu_contacts .= ",".PHP_EOL;
399 $qu_contacts .= "('".create_guid()."','{$contact_id}','{$clone->id}','{$date_modified}')";
400 $contacts_filled = true;
402 foreach($leads_rel_arr as $lead_id){
404 $qu_leads .= ",".PHP_EOL;
405 $qu_leads .= "('".create_guid()."','{$lead_id}','{$clone->id}','{$date_modified}')";
406 $leads_filled = true;
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));
418 $db->query($qu_users);
420 if ($contacts_filled) {
421 $db->query($qu_contacts);
424 $db->query($qu_leads);
427 vCal::cache_sugar_vcal($GLOBALS['current_user']);
432 * Delete recurring activities and their invitee relationships
433 * @param SugarBean $bean
435 static function markRepeatDeleted(SugarBean $bean)
437 // we don't use mark_deleted method here because it runs very slowly
439 $date_modified = $GLOBALS['timedate']->nowDb();
440 if(!empty($GLOBALS['current_user']))
441 $modified_user_id = $GLOBALS['current_user']->id;
443 $modified_user_id = 1;
444 $lower_name = strtolower($bean->object_name);
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)) {
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}'");
456 vCal::cache_sugar_vcal($GLOBALS['current_user']);
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
464 static function correctRecurrences(SugarBean $bean, $beanId)
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);
472 while ($ro = $db->fetchByAssoc($re)) {
475 $new_parent_id = $id;
476 $qu = "UPDATE {$bean->table_name} SET repeat_parent_id = '' AND recurring_source = '' WHERE id = '{$id}'";
478 $qu = "UPDATE {$bean->table_name} SET repeat_parent_id = '{$new_parent_id}' WHERE id = '{$id}'";