[ Index ] |
|
Code source de eGroupWare 1.2.106-2 |
1 <?php 2 /**************************************************************************\ 3 * eGroupWare - Calendar - forms of the UserInterface * 4 * http://www.egroupware.org * 5 * Written and (c) 2004/5 by Ralf Becker <RalfBecker@outdoor-training.de> * 6 * -------------------------------------------- * 7 * This program is free software; you can redistribute it and/or modify it * 8 * under the terms of the GNU General Public License as published by the * 9 * Free Software Foundation; either version 2 of the License, or (at your * 10 * option) any later version. * 11 \**************************************************************************/ 12 13 /* $Id: class.uiforms.inc.php 23142 2006-12-27 15:04:37Z ralfbecker $ */ 14 15 include_once (EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php'); 16 17 /** 18 * calendar UserInterface forms: view and edit events, freetime search 19 * 20 * The new UI, BO and SO classes have a strikt definition, in which time-zone they operate: 21 * UI only operates in user-time, so there have to be no conversation at all !!! 22 * BO's functions take and return user-time only (!), they convert internaly everything to servertime, because 23 * SO operates only on server-time 24 * 25 * The state of the UI elements is managed in the uical class, which all UI classes extend. 26 * 27 * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!! 28 * 29 * @package calendar 30 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> 31 * @copyright (c) 2004/5 by RalfBecker-At-outdoor-training.de 32 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 33 */ 34 class uiforms extends uical 35 { 36 var $public_functions = array( 37 'freetimesearch' => True, 38 'edit' => true, 39 'view' => true, 40 'export' => true, 41 'import' => true, 42 ); 43 44 /** 45 * Standard durations used in edit and freetime search 46 * 47 * @var array 48 */ 49 var $durations = array(); 50 51 /** 52 * Constructor 53 */ 54 function uiforms() 55 { 56 $this->uical(true); // call the parent's constructor 57 58 $this->link =& $this->bo->link; 59 60 for ($n=15; $n <= 8*60; $n+=($n < 60 ? 15 : ($n < 240 ? 30 : 60))) 61 { 62 $this->durations[$n*60] = sprintf('%d:%02d',$n/60,$n%60); 63 } 64 } 65 66 /** 67 * View a calendar event 68 */ 69 function view() 70 { 71 return $this->edit(null,array('view' => true)); 72 } 73 74 /** 75 * Create a default event (adding a new event) by evaluating certain _GET vars 76 * 77 * @return array event-array 78 */ 79 function &default_add_event() 80 { 81 $extra_participants = $_GET['participants'] ? explode(',',$_GET['participants']) : array(); 82 83 if (isset($_GET['owner'])) 84 { 85 $owner = $_GET['owner']; 86 } 87 // dont set the planner start group as owner/participants if called from planner 88 elseif ($this->view != 'planner' || $this->owner != $this->cal_prefs['planner_start_with_group']) 89 { 90 $owner = $this->owner; 91 } 92 if (!$owner || !is_numeric($owner) || $GLOBALS['egw']->accounts->get_type($owner) != 'u' || 93 !$this->bo->check_perms(EGW_ACL_ADD,0,$owner)) 94 { 95 if ($owner) // make an owner who is no user or we have no add-rights a participant 96 { 97 $extra_participants += explode(',',$owner); 98 } 99 $owner = $this->user; 100 } 101 //echo "<p>this->owner=$this->owner, _GET[owner]=$_GET[owner], user=$this->user => owner=$owner, extra_participants=".implode(',',$extra_participants)."</p>\n"; 102 103 // by default include the owner as participant (the user can remove him) 104 $extra_participants[] = $owner; 105 106 $start = $this->bo->date2ts(array( 107 'full' => isset($_GET['date']) && (int) $_GET['date'] ? (int) $_GET['date'] : $this->date, 108 'hour' => (int) (isset($_GET['hour']) && (int) $_GET['hour'] ? $_GET['hour'] : $this->bo->cal_prefs['workdaystarts']), 109 'minute' => (int) $_GET['minute'], 110 )); 111 //echo "<p>_GET[date]=$_GET[date], _GET[hour]=$_GET[hour], _GET[minute]=$_GET[minute], this->date=$this->date ==> start=$start=".date('Y-m-d H:i',$start)."</p>\n"; 112 113 $participant_types['u'] = $participant_types = $participants = array(); 114 foreach($extra_participants as $uid) 115 { 116 if (isset($participants[$uid])) continue; // already included 117 118 if (is_numeric($uid)) 119 { 120 $participants[$uid] = $participant_types['u'][$uid] = $uid == $this->user ? 'A' : 'U'; 121 } 122 elseif (is_array($this->bo->resources[$uid{0}])) 123 { 124 $res_data = $this->bo->resources[$uid{0}]; 125 list($id,$quantity) = explode(':',substr($uid,1)); 126 $participants[$uid] = $participant_types[$uid{0}][$id] = ($res_data['new_status'] ? ExecMethod($res_data['new_status'],$id) : 'U'). 127 ((int) $quantity > 1 ? (int)$quantity : ''); 128 // if new_status == 'x', resource is not bookable 129 if(strstr($participant_types[$uid{0}][$id],'x')) 130 { 131 unset($participant_types[$uid{0}][$id]); 132 unset($participants[$uid]); 133 } 134 } 135 } 136 return array( 137 'participant_types' => $participant_types, 138 'participants' => $participants, 139 'owner' => $owner, 140 'start' => $start, 141 'end' => $start + (int) $this->bo->cal_prefs['defaultlength']*60, 142 'priority' => 2, // normal 143 'public'=> $this->cal_prefs['default_private'] ? 0 : 1, 144 'alarm' => array(), 145 ); 146 } 147 148 /** 149 * Process the edited event and evtl. call edit to redisplay it 150 * 151 * @param array $content posted eTemplate content 152 */ 153 function process_edit($content) 154 { 155 //echo "content submitted="; _debug_array($content); 156 list($button) = @each($content['button']); 157 unset($content['button']); 158 159 $view = $content['view'] && $button != 'edit' && $button != 'exception'; 160 if ($view && $button == 'vcal') 161 { 162 $msg = $this->export($content['id'],true); 163 } 164 // delete a recur-exception 165 if ($content['recur_exception']['delete_exception']) 166 { 167 list($date) = each($content['recur_exception']['delete_exception']); 168 unset($content['recur_exception']['delete_exception']); 169 if (($key = array_search($date,$content['recur_exception'])) !== false) 170 { 171 unset($content['recur_exception'][$key]); 172 $content['recur_exception'] = array_values($content['recur_exception']); 173 } 174 } 175 // delete an alarm 176 if ($content['alarm']['delete_alarm']) 177 { 178 list($id) = each($content['alarm']['delete_alarm']); 179 //echo "delete alarm $id"; _debug_array($content['alarm']['delete_alarm']); 180 181 if ($content['id']) 182 { 183 if ($this->bo->delete_alarm($id)) 184 { 185 $msg = lang('Alarm deleted'); 186 unset($content['alarm'][$id]); 187 } 188 else 189 { 190 $msg = lang('Permission denied'); 191 } 192 } 193 else 194 { 195 unset($content['alarm'][$id]); 196 } 197 } 198 if ($content['duration']) 199 { 200 $content['end'] = $content['start'] + $content['duration']; 201 } 202 $event = $content; 203 unset($event['new_alarm']); 204 unset($event['alarm']['delete_alarm']); 205 unset($event['duration']); 206 207 if (in_array($button,array('ignore','freetime','reedit'))) // called from conflict display 208 { 209 // no conversation necessary, event is already in the right format 210 } 211 elseif (isset($content['participants']) && !(isset($content['view']) && $content['view'])) // convert content => event 212 { 213 //echo "participants="; _debug_array($content['participants']); 214 $event['participants'] = $event['participant_types'] = array(); 215 foreach($content['participants'] as $app => $participants) 216 { 217 if (!$participants) continue; 218 219 $type = 'u'; 220 foreach($this->bo->resources as $t => $data) 221 { 222 if ($data['app'] == $app) 223 { 224 $type = $t; 225 break; 226 } 227 } 228 foreach(is_array($participants) ? $participants : explode(',',$participants) as $id) 229 { 230 if (is_array($id)) continue; // ignore the status 231 list($id,$quantity) = explode(':',$id); 232 $event['participants'][$type == 'u' ? (int) $id : $type.$id] = $event['participant_types'][$type][$id] = 233 // for existing participants use their old status (dont change it) 234 (isset($content['participant_types'][$type][$id]) ? $content['participant_types'][$type][$id]{0} : 235 // for new participants check if they have a 'new_status' resource-methode to determine the status 236 (isset($this->bo->resources[$type]['new_status']) ? ExecMethod($this->bo->resources[$type]['new_status'],$id) : 237 // if not use 'A'=accepted for the current user and 'U' otherwise 238 ($type == 'u' && $id == $this->bo->user ? 'A' : 'U'))).((int) $quantity > 1 ? (int)$quantity : ''); 239 // ToDo: move this logic into bocal 240 } 241 } 242 if ($content['whole_day']) 243 { 244 $event['start'] = $this->bo->date2array($event['start']); 245 $event['start']['hour'] = $event['start']['minute'] = 0; unset($event['start']['raw']); 246 $event['start'] = $this->bo->date2ts($event['start']); 247 $event['end'] = $this->bo->date2array($event['end']); 248 $event['end']['hour'] = 23; $event['end']['minute'] = $event['end']['second'] = 59; unset($event['end']['raw']); 249 $event['end'] = $this->bo->date2ts($event['end']); 250 } 251 // some checks for recurances, if you give a date, make it a weekly repeating event and visa versa 252 if ($event['recur_type'] == MCAL_RECUR_NONE && $event['recur_data']) $event['recur_type'] = MCAL_RECUR_WEEKLY; 253 if ($event['recur_type'] == MCAL_RECUR_WEEKLY && !$event['recur_data']) 254 { 255 $event['recur_data'] = 1 << (int)date('w',$event['start']); 256 } 257 } 258 else // status change view 259 { 260 foreach($event['participants'] as $name => $data) 261 { 262 if (!is_array($data)) continue; 263 264 $type = 'u'; 265 foreach($this->bo->resources as $t => $d) 266 { 267 if ($d['app'] == $name) 268 { 269 $type = $t; 270 break; 271 } 272 } 273 // checking for status changes 274 foreach($data[$name.'_status'] as $uid => $status) 275 { 276 list($uid,$quantity) = explode(':',$uid); 277 //echo "checking $type: $uid $status against old ".$event['participant_types'][$type][$uid]."<br />\n"; 278 if ($event['participant_types'][$type][$uid]{0} != $status{0}) // status changed by user 279 { 280 if ($this->bo->set_status($event['id'],$type,$uid,$status,$event['recur_type'] != MCAL_RECUR_NONE ? $event['start'] : 0)) 281 { 282 $event['participants'][$type == 'u' ? (int) $uid : $type.$uid] = 283 $event['participant_types'][$type][$uid] = $status.$quantity; 284 // refreshing the calendar-view with the changed participant-status 285 $msg = lang('Status changed'); 286 if (!$preserv['no_popup']) 287 { 288 $js = 'opener.location.href=\''.addslashes($GLOBALS['egw']->link('/index.php',array( 289 'menuaction' => $content['referer'], 290 'msg' => $msg, 291 ))).'\';'; 292 } 293 } 294 } 295 } 296 unset($event['participants'][$name]); // unset the status-changes from the event 297 } 298 } 299 $preserv = array( 300 'view' => $view, 301 'edit_single' => $content['edit_single'], 302 'referer' => $content['referer'], 303 'no_popup' => $content['no_popup'], 304 ); 305 switch($button) 306 { 307 case 'exception': // create an exception in a recuring event 308 $preserv['edit_single'] = $content['start']; 309 $event['recur_type'] = MCAL_RECUR_NONE; 310 foreach(array('recur_enddate','recur_interval','recur_exception','recur_data') as $name) 311 { 312 unset($event[$name]); 313 } 314 break; 315 316 case 'edit': 317 if ($content['recur_type'] != MCAL_RECUR_NONE) 318 { 319 // need to reload start and end of the serie 320 $event = $this->bo->read($event['id'],0); 321 } 322 break; 323 324 case 'copy': // create new event with copied content, some content need to be unset to make a "new" event 325 unset($event['id']); 326 unset($event['uid']); 327 unset($event['alarm']); 328 unset($event['reference']); 329 unset($event['recur_exception']); 330 unset($event['edit_single']); // in case it has been set 331 unset($event['modified']); 332 unset($event['modifier']); 333 $event['owner'] = !(int)$this->owner || !$this->bo->check_perms(EGW_ACL_ADD,0,$this->owner) ? $this->user : $this->owner; 334 $preserv['view'] = $preserv['edit_single'] = false; 335 $msg = lang('Event copied - the copy can now be edited'); 336 $event['title'] = lang('Copy of:').' '.$event['title']; 337 break; 338 339 case 'ignore': 340 $ignore_conflicts = true; 341 $button = $event['button_was']; // save or apply 342 unset($event['button_was']); 343 // fall through 344 case 'save': 345 case 'apply': 346 if ($event['id'] && !$this->bo->check_perms(EGW_ACL_EDIT,$event)) 347 { 348 $msg = lang('Permission denied'); 349 break; 350 } 351 if ($event['start'] > $event['end']) 352 { 353 $msg = lang('Error: Starttime has to be before the endtime !!!'); 354 $button = ''; 355 break; 356 } 357 if (!$event['participants']) 358 { 359 $msg = lang('Error: no participants selected !!!'); 360 $button = ''; 361 break; 362 } 363 if ($content['edit_single']) // we edited a single event from a series 364 { 365 $event['reference'] = $event['id']; 366 unset($event['id']); 367 unset($event['uid']); 368 $conflicts = $this->bo->update($event,$ignore_conflicts); 369 if (!is_array($conflicts) && $conflicts) 370 { 371 // now we need to add the original start as recur-execption to the series 372 $recur_event = $this->bo->read($event['reference']); 373 $recur_event['recur_exception'][] = $content['edit_single']; 374 unset($recur_event['start']); unset($recur_event['end']); // no update necessary 375 $this->bo->update($recur_event,true); // no conflict check here 376 unset($recur_event); 377 unset($event['edit_single']); // if we further edit it, it's just a single event 378 unset($preserv['edit_single']); 379 } 380 else // conflict or error, we need to reset everything to the state befor we tried to save it 381 { 382 $event['id'] = $event['reference']; 383 unset($event['reference']); 384 $event['uid'] = $content['uid']; 385 } 386 } 387 else // we edited a non-reccuring event or the whole series 388 { 389 $conflicts = $this->bo->update($event,$ignore_conflicts); 390 unset($event['ignore']); 391 } 392 if (is_array($conflicts)) 393 { 394 $event['button_was'] = $button; // remember for ignore 395 return $this->conflicts($event,$conflicts,$preserv); 396 } 397 elseif($conflicts) // true and non-array means Ok ;-) 398 { 399 $msg .= ($msg ? ', ' : '') . lang('Event saved'); 400 401 // writing links for new entry, existing ones are handled by the widget itself 402 if (!$content['id'] && is_array($content['link_to']['to_id'])) 403 { 404 $this->link->link('calendar',$event['id'],$content['link_to']['to_id']); 405 } 406 $js = 'opener.location.href=\''.addslashes($GLOBALS['egw']->link('/index.php',array( 407 'menuaction' => $content['referer'], 408 'msg' => $msg, 409 ))).'\';'; 410 411 if ($content['custom_mail']) 412 { 413 $js = $this->custom_mail($event,!$content['id'])."\n".$js; // first open the new window and then update the view 414 unset($event['custom_mail']); 415 } 416 } 417 else 418 { 419 $msg = lang('Error: saving the event !!!'); 420 } 421 break; 422 423 case 'delete': 424 if ($this->bo->delete($event['id'],(int)$content['edit_single'])) 425 { 426 $msg = lang('Event deleted'); 427 $js = 'opener.location.href=\''.addslashes($GLOBALS['egw']->link('/index.php',array( 428 'menuaction' => $content['referer'], 429 'msg' => $msg, 430 ))).'\';'; 431 } 432 break; 433 434 case 'freetime': 435 if (!is_object($GLOBALS['egw']->js)) 436 { 437 $GLOBALS['egw']->js =& CreateObject('phpgwapi.javascript'); 438 } 439 // the "click" has to be in onload, to make sure the button is already created 440 $GLOBALS['egw']->js->set_onload("document.getElementsByName('exec[freetime]')[0].click();"); 441 break; 442 443 case 'add_alarm': 444 if ($this->bo->check_perms(EGW_ACL_EDIT,!$content['new_alarm']['owner'] ? $event : 0,$content['new_alarm']['owner'])) 445 { 446 $offset = DAY_s * $content['new_alarm']['days'] + HOUR_s * $content['new_alarm']['hours'] + 60 * $content['new_alarm']['mins']; 447 $alarm = array( 448 'offset' => $offset, 449 'time' => $content['start'] - $offset, 450 'all' => !$content['new_alarm']['owner'], 451 'owner' => $content['new_alarm']['owner'] ? $content['new_alarm']['owner'] : $this->user, 452 ); 453 if ($alarm['time'] < $this->bo->now_su) 454 { 455 $msg = lang("Can't add alarms in the past !!!"); 456 } 457 elseif ($event['id']) // save the alarm immediatly 458 { 459 if(($alarm_id = $this->bo->save_alarm($event['id'],$alarm))) 460 { 461 $alarm['id'] = $alarm_id; 462 $event['alarm'][$alarm_id] = $alarm; 463 464 $msg = lang('Alarm added'); 465 } 466 else 467 { 468 $msg = lang('Error adding the alarm'); 469 } 470 } 471 else 472 { 473 for($alarm['id']=1; isset($event['alarm'][$alarm['id']]); $alarm['id']++); // get a temporary non-conflicting, numeric id 474 $event['alarm'][$alarm['id']] = $alarm; 475 } 476 } 477 else 478 { 479 $msg = lang('Permission denied'); 480 } 481 break; 482 } 483 if (in_array($button,array('cancel','save','delete','delete_series'))) 484 { 485 if ($content['no_popup']) 486 { 487 $GLOBALS['egw']->redirect_link('/index.php',array( 488 'menuaction' => $content['referer'], 489 'msg' => $msg, 490 )); 491 } 492 $js .= 'window.close();'; 493 echo "<html><body onload=\"$js\"></body></html>\n"; 494 $GLOBALS['egw']->common->egw_exit(); 495 } 496 return $this->edit($event,$preserv,$msg,$js,$event['id'] ? $event['id'] : $content['link_to']['to_id']); 497 } 498 499 /** 500 * return javascript to open felamimail compose window with preset content to mail all participants 501 * 502 * @param array $event 503 * @param boolean $added 504 * @return string javascript window.open command 505 */ 506 function custom_mail($event,$added) 507 { 508 $to = array(); 509 foreach($event['participants'] as $uid => $status) 510 { 511 if (is_numeric($uid) && $uid != $this->user && $status != 'R' && $GLOBALS['egw']->accounts->get_type($uid) == 'u') 512 { 513 $GLOBALS['egw']->accounts->get_account_name($uid,$lid,$firstname,$lastname); 514 515 $to[] = $firstname.' '.$lastname. 516 ' <'.$GLOBALS['egw']->accounts->id2name($uid,'account_email').'>'; 517 } 518 } 519 list($subject,$body) = $this->bo->get_update_message($event,$added ? MSG_ADDED : MSG_MODIFIED); // update-message is in TZ of the user 520 521 $boical =& CreateObject('calendar.boical'); 522 $ics = $boical->exportVCal(array($event),'2.0','request'); 523 524 $ics_file = tempnam($GLOBALS['egw_info']['server']['temp_dir'],'ics'); 525 if(($f = fopen($ics_file,'w'))) 526 { 527 fwrite($f,$ics); 528 fclose($f); 529 } 530 $vars = array( 531 'menuaction' => 'felamimail.uicompose.compose', 532 'preset[to]' => implode(', ',$to), 533 'preset[subject]' => $subject, 534 'preset[body]' => $body, 535 'preset[name]' => 'event.ics', 536 'preset[file]' => $ics_file, 537 'preset[type]' => 'text/calendar; method=request', 538 'preset[size]' => filesize($ics_file), 539 ); 540 return "window.open('".$GLOBALS['egw']->link('/index.php',$vars)."','_blank','width=700,height=700,scrollbars=yes,status=no');"; 541 } 542 543 /** 544 * Edit a calendar event 545 * 546 * @param array $event=null Event to edit, if not $_GET['cal_id'] contains the event-id 547 * @param array $perserv=null following keys: 548 * view boolean view-mode, if no edit-access we automatic fallback to view-mode 549 * referer string menuaction of the referer 550 * no_popup boolean use a popup or not 551 * edit_single int timestamp of single event edited, unset/null otherwise 552 * @param string $msg='' msg to display 553 * @param string $js='window.focus();' javascript to include in the page 554 * @param mixed $link_to_id='' $content from or for the link-widget 555 */ 556 function edit($event=null,$preserv=null,$msg='',$js = 'window.focus();',$link_to_id='') 557 { 558 $etpl =& CreateObject('etemplate.etemplate','calendar.edit'); 559 560 $sel_options = array( 561 'recur_type' => &$this->bo->recur_types, 562 'accounts_status' => $this->bo->verbose_status, 563 'owner' => array(), 564 'duration' => $this->durations, 565 ); 566 if (!is_array($event)) 567 { 568 $preserv = array( 569 'no_popup' => isset($_GET['no_popup']), 570 'referer' => preg_match('/menuaction=([^&]+)/',$_SERVER['HTTP_REFERER'],$matches) ? $matches[1] : $this->view_menuaction, 571 'view' => $preserv['view'], 572 ); 573 $cal_id = (int) $_GET['cal_id']; 574 575 if (!$cal_id || $cal_id && !($event = $this->bo->read($cal_id,$_GET['date'])) || !$this->bo->check_perms(EGW_ACL_READ,$event)) 576 { 577 if ($cal_id) 578 { 579 if (!$preserv['no_popup']) 580 { 581 $js = "alert('".lang('Permission denied')."'); window.close();"; 582 } 583 else 584 { 585 $GLOBALS['egw']->common->egw_header(); 586 parse_navbar(); 587 echo '<p class="redItalic" align="center">'.lang('Permission denied')."</p>\n"; 588 $GLOBALS['egw']->common->egw_exit(); 589 } 590 } 591 $event =& $this->default_add_event(); 592 } 593 // check if the event is the whole day 594 $start = $this->bo->date2array($event['start']); 595 $end = $this->bo->date2array($event['end']); 596 $event['whole_day'] = !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59; 597 598 $link_to_id = $event['id']; 599 if (!$add_link && !$event['id'] && isset($_GET['link_app']) && isset($_GET['link_id']) && 600 preg_match('/^[a-z_0-9-]+:[:a-z_0-9-]+$/i',$_GET['link_app'].':'.$_GET['link_id'])) // gard against XSS 601 { 602 $this->link->link('calendar',$link_to_id,$_GET['link_app'],$_GET['link_id']); 603 } 604 } 605 $view = $preserv['view'] = $preserv['view'] || $event['id'] && !$this->bo->check_perms(EGW_ACL_EDIT,$event); 606 //echo "view=$view, event="; _debug_array($event); 607 $content = array_merge($event,array( 608 'link_to' => array( 609 'to_id' => $link_to_id, 610 'to_app' => 'calendar', 611 ), 612 'edit_single' => $preserv['edit_single'], // need to be in content too, as it is used in the template 613 'view' => $view, 614 )); 615 $content['participants'] = array(); 616 617 $content['duration'] = $content['end'] - $content['start']; 618 if (isset($this->durations[$content['duration']])) $content['end'] = ''; 619 620 foreach($event['participant_types'] as $type => $participants) 621 { 622 $name = 'accounts'; 623 if (isset($this->bo->resources[$type])) 624 { 625 $name = $this->bo->resources[$type]['app']; 626 } 627 foreach($participants as $id => $status) 628 { 629 $content['participants'][$name][] = $id . (substr($status,1) > 1 ? (':'.substr($status,1)) : ''); 630 } 631 632 if ($view) 633 { 634 $stati =& $content['participants'][$name][$name.'_status']; 635 $stati = $participants; 636 // enumerate group-invitations, so people can accept/reject them 637 if ($name == 'accounts') 638 { 639 foreach($participants as $id => $status) 640 { 641 if ($GLOBALS['egw']->accounts->get_type($id) == 'g' && 642 ($members = $GLOBALS['egw']->accounts->member($id))) 643 { 644 $sel_options['accounts_status']['G'] = lang('Select one'); 645 foreach($members as $member) 646 { 647 if (!isset($stati[$member['account_id']]) && $this->bo->check_perms(EGW_ACL_EDIT,0,$member['account_id'])) 648 { 649 $stati[$member['account_id']] = 'G'; // status for invitation via membership in group 650 $content['participants'][$name][] = $member['account_id']; 651 } 652 } 653 } 654 } 655 } 656 foreach($stati as $id => $status) 657 { 658 $readonlys[$name.'_status['.$id.']'] = !$this->bo->check_perms(EGW_ACL_EDIT,0,($type != 'u' ? $type : '').$id); 659 } 660 } 661 } 662 // echo '$content[participants]'; _debug_array($content['participants']); 663 // echo '$content[participant_types]'; _debug_array($content['participant_types']); 664 // _debug_array($sel_options); 665 $preserv = array_merge($preserv,$view ? $event : $content); 666 667 if ($event['alarm']) 668 { 669 // makes keys of the alarm-array starting with 1 670 $content['alarm'] = array(false); 671 foreach(array_values($event['alarm']) as $id => $alarm) 672 { 673 if (!$alarm['all'] && !$this->bo->check_perms(EGW_ACL_READALARM,0,$alarm['owner'])) 674 { 675 continue; // no read rights to the calendar of the alarm-owner, dont show the alarm 676 } 677 $alarm['all'] = (int) $alarm['all']; 678 $days = (int) ($alarm['offset'] / DAY_s); 679 $hours = (int) (($alarm['offset'] % DAY_s) / HOUR_s); 680 $minutes = (int) (($alarm['offset'] % HOUR_s) / 60); 681 $label = array(); 682 if ($days) $label[] = $days.' '.lang('days'); 683 if ($hours) $label[] = $hours.' '.lang('hours'); 684 if ($minutes) $label[] = $minutes.' '.lang('Minutes'); 685 $alarm['offset'] = implode(', ',$label); 686 $content['alarm'][] = $alarm; 687 688 $readonlys['delete_alarm['.$id.']'] = !$this->bo->check_perms(EGW_ACL_EDIT,$alarm['all'] ? $event : 0,$alarm['owner']); 689 } 690 } 691 else 692 { 693 $content['alarm'] = false; 694 } 695 $content['msg'] = $msg; 696 697 if ($view) 698 { 699 foreach($event as $key => $val) 700 { 701 if ($key != 'alarm') $readonlys[$key] = true; 702 } 703 // we need to unset the tab itself, as this would make all content (incl. the change-status selects) readonly 704 unset($readonlys['general|description|participants|recurrence|custom|links|alarms']); 705 706 $readonlys['button[save]'] = $readonlys['button[apply]'] = $readonlys['freetime'] = true; 707 $readonlys['link_to'] = $readonlys['customfields'] = true; 708 $readonlys['duration'] = true; 709 710 if ($event['recur_type'] != MCAL_RECUR_NONE) 711 { 712 $etpl->set_cell_attribute('button[edit]','help','Edit this series of recuring events'); 713 $etpl->set_cell_attribute('button[delete]','help','Delete this series of recuring events'); 714 $onclick =& $etpl->get_cell_attribute('button[delete]','onclick'); 715 $onclick = str_replace('Delete this event','Delete this series of recuring events',$onclick); 716 } 717 } 718 else 719 { 720 if (!is_object($GLOBALS['egw']->js)) 721 { 722 $GLOBALS['egw']->js = CreateObject('phpgwapi.javascript'); 723 } 724 // We hide the enddate if one of our predefined durations fits 725 // the call to set_style_by_class has to be in onload, to make sure the function and the element is already created 726 $GLOBALS['egw']->js->set_onload("set_style_by_class('table','end_hide','visibility','".($content['duration'] && isset($sel_options['duration'][$content['duration']]) ? 'hidden' : 'visible')."');"); 727 728 $readonlys['button[copy]'] = $readonlys['button[vcal]'] = true; 729 unset($preserv['participants']); // otherwise deleted participants are still reported 730 $readonlys['recur_exception'] = !count($content['recur_exception']); // otherwise we get a delete button 731 } 732 // disabling the custom fields tab, if there are none 733 $readonlys['general|description|participants|recurrence|custom|links|alarms'] = array( 734 'custom' => !count($this->bo->config['customfields']) 735 ); 736 if ($view || !isset($GLOBALS['egw_info']['user']['apps']['felamimail'])) 737 { 738 $etpl->disable_cells('custom_mail'); 739 } 740 if (!($readonlys['button[exception]'] = $readonlys['button[edit]'] = !$view || !$this->bo->check_perms(EGW_ACL_EDIT,$event))) 741 { 742 $readonlys['button[exception]'] = $event['recur_type'] == MCAL_RECUR_NONE; 743 } 744 $readonlys['button[delete]'] = !$event['id'] || !$this->bo->check_perms(EGW_ACL_DELETE,$event); 745 746 if ($event['id'] || $this->bo->check_perms(EGW_ACL_EDIT,$event)) // new event or edit rights to the event ==> allow to add alarm for all users 747 { 748 $sel_options['owner'][0] = lang('All participants'); 749 } 750 foreach((array) $event['participant_types']['u'] as $uid => $status) 751 { 752 if ($status != 'R' && $this->bo->check_perms(EGW_ACL_EDIT,0,$uid)) 753 { 754 $sel_options['owner'][$uid] = $this->bo->participant_name($uid); 755 } 756 } 757 $content['no_add_alarm'] = !count($sel_options['owner']); // no rights to set any alarm 758 if (!$event['id']) 759 { 760 $etpl->set_cell_attribute('button[new_alarm]','type','checkbox'); 761 } 762 foreach($this->bo->resources as $res_data) 763 { 764 $sel_options[$res_data['app'].'_status'] =& $this->bo->verbose_status; 765 } 766 if ($preserv['no_popup']) 767 { 768 $etpl->set_cell_attribute('button[cancel]','onclick',''); 769 } 770 //echo "content="; _debug_array($content); 771 //echo "preserv="; _debug_array($preserv); 772 //echo "readonlys="; _debug_array($readonlys); 773 //echo "sel_options="; _debug_array($sel_options); 774 $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . ($event['id'] ? ($view ? lang('View') : 775 ($content['edit_single'] ? lang('Edit exception') : lang('Edit'))) : lang('Add')); 776 $GLOBALS['egw_info']['flags']['java_script'] .= "<script>\n$js\n</script>\n"; 777 $etpl->exec('calendar.uiforms.process_edit',$content,$sel_options,$readonlys,$preserv,$preserv['no_popup'] ? 0 : 2); 778 } 779 780 /** 781 * displays a sheduling conflict 782 * 783 * @param array $event 784 * @param array $conflicts array with conflicting events, the events are not garantied to be readable by the user! 785 * @param array $preserv data to preserv 786 */ 787 function conflicts($event,$conflicts,$preserv) 788 { 789 $etpl =& CreateObject('etemplate.etemplate','calendar.conflicts'); 790 791 foreach($conflicts as $k => $conflict) 792 { 793 $is_readable = $this->bo->check_perms(EGW_ACL_READ,$conflict); 794 795 $conflicts[$k] += array( 796 'icon_participants' => $is_readable ? (count($conflict['participants']) > 1 ? 'users' : 'single') : 'private', 797 'tooltip_participants' => $is_readable ? implode(', ',$this->bo->participants($conflict)) : '', 798 'time' => $this->bo->long_date($conflict['start'],$conflict['end'],true), 799 'conflicting_participants' => implode(",\n",$this->bo->participants(array( 800 'participants' => array_intersect_key($conflict['participants'],$event['participants']), 801 ),true,true)), // show group invitations too 802 'icon_recur' => $conflict['recur_type'] != MCAL_RECUR_NONE ? 'recur' : '', 803 'text_recur' => $conflict['recur_type'] != MCAL_RECUR_NONE ? lang('Recurring event') : ' ', 804 ); 805 } 806 $content = $event + array( 807 'conflicts' => array_values($conflicts), // conflicts have id-start as key 808 ); 809 $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('Scheduling conflict'); 810 811 $etpl->exec('calendar.uiforms.process_edit',$content,false,false,array_merge($event,$preserv),$preserv['no_popup'] ? 0 : 2); 812 } 813 814 /** 815 * Freetime search 816 * 817 * As the function is called in a popup via javascript, parametes get initialy transfered via the url 818 * @param array $content=null array with parameters or false (default) to use the get-params 819 * @param string start[str] start-date 820 * @param string start[hour] start-hour 821 * @param string start[min] start-minutes 822 * @param string end[str] end-date 823 * @param string end[hour] end-hour 824 * @param string end[min] end-minutes 825 * @param string participants ':' delimited string of user-id's 826 */ 827 function freetimesearch($content = null) 828 { 829 $etpl =& CreateObject('etemplate.etemplate','calendar.freetimesearch'); 830 831 $sel_options['search_window'] = array( 832 7*DAY_s => lang('one week'), 833 14*DAY_s => lang('two weeks'), 834 31*DAY_s => lang('one month'), 835 92*DAY_s => lang('three month'), 836 365*DAY_s => lang('one year'), 837 ); 838 if (!is_array($content)) 839 { 840 $edit_content = $etpl->process_values2url(); 841 842 if ($edit_content['duration']) 843 { 844 $edit_content['end'] = $edit_content['start'] + $edit_content['duration']; 845 } 846 if ($edit_content['whole_day']) 847 { 848 $arr = $this->bo->date2array($edit_content['start']); 849 $arr['hour'] = $arr['minute'] = $arr['second'] = 0; unset($arr['raw']); 850 $edit_content['start'] = $this->bo->date2ts($arr); 851 $arr = $this->bo->date2array($edit_content['end']); 852 $arr['hour'] = 23; $arr['minute'] = $arr['second'] = 59; unset($arr['raw']); 853 $edit_content['end'] = $this->bo->date2ts($arr); 854 } 855 $content = array( 856 'start' => $edit_content['start'], 857 'duration' => $edit_content['end'] - $edit_content['start'], 858 'end' => $edit_content['end'], 859 'cal_id' => $edit_content['id'], 860 'recur_type' => $edit_content['recur_type'], 861 'participants' => array(), 862 ); 863 foreach($edit_content['participants'] as $app => $ids) 864 { 865 if ($app == 'accounts') 866 { 867 $content['participants'] += (array)$ids; 868 } 869 elseif ($ids) 870 { 871 foreach($this->bo->resources as $type => $data) 872 { 873 if ($data['app'] == $app) break; 874 } 875 foreach((array)$ids as $id) 876 { 877 $content['participants'][] = $type . $id; 878 } 879 } 880 } 881 // default search parameters 882 $content['start_time'] = $edit_content['whole_day'] ? 0 : $this->cal_prefs['workdaystarts']; 883 $content['end_time'] = $this->cal_prefs['workdayends']; 884 if ($this->cal_prefs['workdayends']*HOUR_s < $this->cal_prefs['workdaystarts']*HOUR_s+$content['duration']) 885 { 886 $content['end_time'] = 0; // no end-time limit, as duration would never fit 887 } 888 $content['weekdays'] = MCAL_M_WEEKDAYS; 889 890 $content['search_window'] = 7 * DAY_s; 891 // pick a searchwindow fitting the duration (search for a 10 day slot in a one week window never succeeds) 892 foreach($sel_options['search_window'] as $window => $label) 893 { 894 if ($window > $content['duration']) 895 { 896 $content['search_window'] = $window; 897 break; 898 } 899 } 900 } 901 else 902 { 903 if (!$content['duration']) $content['duration'] = $content['end'] - $content['start']; 904 905 if (is_array($content['freetime']['select'])) 906 { 907 list($selected) = each($content['freetime']['select']); 908 //echo "$selected = ".date('D d.m.Y H:i',$content['freetime'][$selected]['start']); 909 $start = (int) $content['freetime'][$selected]['start']; 910 $end = $start + $content['duration']; 911 /** 912 * ToDo: make this an eTemplate function to transmit content back to the opener 913 */ 914 $fields_to_set = array( 915 'exec[start][str]' => date($this->common_prefs['dateformat'],$start), 916 'exec[start][i]' => (int) date('i',$start), 917 'exec[end][str]' => date($this->common_prefs['dateformat'],$end), 918 'exec[end][i]' => (int) date('i',$end), 919 'exec[duration]' => $content['duration'], 920 ); 921 if ($this->common_prefs['timeformat'] == 12) 922 { 923 $fields_to_set += array( 924 'exec[start][H]' => date('h',$start), 925 'exec[start][a]' => date('a',$start), 926 'exec[end][H]' => date('h',$end), 927 'exec[end][a]' => date('a',$end), 928 ); 929 } 930 else 931 { 932 $fields_to_set += array( 933 'exec[start][H]' => (int) date('H',$start), 934 'exec[end][H]' => (int) date('H',$end), 935 ); 936 } 937 echo "<html> 938 <script> 939 var fields = Array('".implode("','",array_keys($fields_to_set))."'); 940 var values = Array('".implode("','",$fields_to_set)."'); 941 for (i=0; i < fields.length; ++i) { 942 elements = opener.document.getElementsByName(fields[i]); 943 if (elements) { 944 if (elements.length == 1) 945 elements[0].value = values[i]; 946 else 947 for (n=0; n < elements.length; ++n) { 948 if (elements[n].value == values[i]) elements[n].checked = true; 949 } 950 } 951 } 952 window.close(); 953 </script> 954 </html>\n"; 955 exit; 956 } 957 } 958 if ($content['recur_type']) 959 { 960 $content['msg'] .= lang('Only the initial date of that recuring event is checked!'); 961 } 962 $content['freetime'] = $this->freetime($content['participants'],$content['start'],$content['start']+$content['search_window'],$content['duration'],$content['cal_id']); 963 $content['freetime'] = $this->split_freetime_daywise($content['freetime'],$content['duration'],$content['weekdays'],$content['start_time'],$content['end_time'],$sel_options); 964 965 //echo "<pre>".print_r($content,true)."</pre>\n"; 966 $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('freetime search'); 967 // let the window popup, if its already there 968 $GLOBALS['egw_info']['flags']['java_script'] .= "<script>\nwindow.focus();\n</script>\n"; 969 970 if (!is_object($GLOBALS['egw']->js)) 971 { 972 $GLOBALS['egw']->js = CreateObject('phpgwapi.javascript'); 973 } 974 $sel_options['duration'] = $this->durations; 975 if ($content['duration'] && isset($sel_options['duration'][$content['duration']])) $content['end'] = ''; 976 // We hide the enddate if one of our predefined durations fits 977 // the call to set_style_by_class has to be in onload, to make sure the function and the element is already created 978 $GLOBALS['egw']->js->set_onload("set_style_by_class('table','end_hide','visibility','".($content['duration'] && isset($sel_options['duration'][$content['duration']]) ? 'hidden' : 'visible')."');"); 979 980 $etpl->exec('calendar.uiforms.freetimesearch',$content,$sel_options,'',array( 981 'participants' => $content['participants'], 982 'cal_id' => $content['cal_id'], 983 'recur_type' => $content['recur_type'], 984 ),2); 985 } 986 987 /** 988 * calculate the freetime of given $participants in a certain time-span 989 * 990 * @param array $participants user-id's 991 * @param int $start start-time timestamp in user-time 992 * @param int $end end-time timestamp in user-time 993 * @param int $duration min. duration in sec, default 1 994 * @param int $cal_id own id for existing events, to exclude them from being busy-time, default 0 995 * @return array of free time-slots: array with start and end values 996 */ 997 function freetime($participants,$start,$end,$duration=1,$cal_id=0) 998 { 999 if ($this->debug > 2) $this->bo->debug_message('uiforms::freetime(participants=%1, start=%2, end=%3, duration=%4, cal_id=%5)',true,$participants,$start,$end,$duration,$cal_id); 1000 1001 $busy = $this->bo->search(array( 1002 'start' => $start, 1003 'end' => $end, 1004 'users' => $participants, 1005 'ignore_acl' => true, // otherwise we get only events readable by the user 1006 )); 1007 $busy[] = array( // add end-of-search-date as event, to cope with empty search and get freetime til that date 1008 'start' => $end, 1009 'end' => $end, 1010 ); 1011 $ft_start = $start; 1012 $freetime = array(); 1013 $n = 0; 1014 foreach($busy as $event) 1015 { 1016 if ((int)$cal_id && $event['id'] == (int)$cal_id) continue; // ignore our own event 1017 1018 if ($event['non_blocking']) continue; // ignore non_blocking events 1019 1020 if ($this->debug) 1021 { 1022 echo "<p>ft_start=".date('D d.m.Y H:i',$ft_start)."<br>\n"; 1023 echo "event[title]=$event[title]<br>\n"; 1024 echo "event[start]=".date('D d.m.Y H:i',$event['start']['raw'])."<br>\n"; 1025 echo "event[end]=".date('D d.m.Y H:i',$event['end']['raw'])."<br>\n"; 1026 } 1027 // $events ends before our actual position ==> ignore it 1028 if ($event['end'] < $ft_start) 1029 { 1030 //echo "==> event ends before ft_start ==> continue<br>\n"; 1031 continue; 1032 } 1033 // $events starts before our actual position ==> set start to it's end and go to next event 1034 if ($event['start'] < $ft_start) 1035 { 1036 //echo "==> event starts before ft_start ==> set ft_start to it's end & continue<br>\n"; 1037 $ft_start = $event['end']; 1038 continue; 1039 } 1040 $ft_end = $event['start']; 1041 1042 // only show slots equal or bigger to min_length 1043 if ($ft_end - $ft_start >= $duration) 1044 { 1045 $freetime[++$n] = array( 1046 'start' => $ft_start, 1047 'end' => $ft_end, 1048 ); 1049 if ($this->debug > 1) echo "<p>freetime: ".date('D d.m.Y H:i',$ft_start)." - ".date('D d.m.Y H:i',$ft_end)."</p>\n"; 1050 } 1051 $ft_start = $event['end']; 1052 } 1053 if ($this->debug > 0) $this->bo->debug_message('uiforms::freetime(participants=%1, start=%2, end=%3, duration=%4, cal_id=%5) freetime=%6',true,$participants,$start,$end,$duration,$cal_id,$freetime); 1054 1055 return $freetime; 1056 } 1057 1058 /** 1059 * split the freetime in daywise slot, taking into account weekdays, start- and stop-times 1060 * 1061 * If the duration is bigger then the difference of start- and end_time, the end_time is ignored 1062 * 1063 * @param array $freetime free time-slots: array with start and end values 1064 * @param int $duration min. duration in sec 1065 * @param int $weekdays allowed weekdays, bitfield of MCAL_M_... 1066 * @param int $start_time minimum start-hour 0-23 1067 * @param int $end_time maximum end-hour 0-23, or 0 for none 1068 * @param array $sel_options on return options for start-time selectbox 1069 * @return array of free time-slots: array with start and end values 1070 */ 1071 function split_freetime_daywise($freetime,$duration,$weekdays,$start_time,$end_time,&$sel_options) 1072 { 1073 if ($this->debug > 1) $this->bo->debug_message('uiforms::split_freetime_daywise(freetime=%1, duration=%2, start_time=%3, end_time=%4)',true,$freetime,$duration,$start_time,$end_time); 1074 1075 $freetime_daywise = array(); 1076 if (!is_array($sel_options)) $sel_options = array(); 1077 $time_format = $this->common_prefs['timeformat'] == 12 ? 'h:i a' : 'H:i'; 1078 1079 $start_time = (int) $start_time; // ignore leading zeros 1080 $end_time = (int) $end_time; 1081 1082 // ignore the end_time, if duration would never fit 1083 if (($end_time - $start_time)*HOUR_s < $duration) 1084 { 1085 $end_time = 0; 1086 if ($this->debug > 1) $this->bo->debug_message('uiforms::split_freetime_daywise(, duration=%2, start_time=%3,..) end_time set to 0, it never fits durationn otherwise',true,$duration,$start_time); 1087 } 1088 $n = 0; 1089 foreach($freetime as $ft) 1090 { 1091 $daybegin = $this->bo->date2array($ft['start']); 1092 $daybegin['hour'] = $daybegin['minute'] = $daybegin['second'] = 0; 1093 unset($daybegin['raw']); 1094 $daybegin = $this->bo->date2ts($daybegin); 1095 1096 for($t = $daybegin; $t < $ft['end']; $t += DAY_s,$daybegin += DAY_s) 1097 { 1098 $dow = date('w',$daybegin+DAY_s/2); // 0=Sun, .., 6=Sat 1099 $mcal_dow = pow(2,$dow); 1100 if (!($weekdays & $mcal_dow)) 1101 { 1102 //echo "wrong day of week $dow<br>\n"; 1103 continue; // wrong day of week 1104 } 1105 $start = $t < $ft['start'] ? $ft['start'] : $t; 1106 1107 if ($start-$daybegin < $start_time*HOUR_s) // start earlier then start_time 1108 { 1109 $start = $daybegin + $start_time*HOUR_s; 1110 } 1111 // if end_time given use it, else the original slot's end 1112 $end = $end_time ? $daybegin + $end_time*HOUR_s : $ft['end']; 1113 if ($end > $ft['end']) $end = $ft['end']; 1114 1115 // slot to small for duration 1116 if ($end - $start < $duration) 1117 { 1118 //echo "slot to small for duration=$duration<br>\n"; 1119 continue; 1120 } 1121 $freetime_daywise[++$n] = array( 1122 'start' => $start, 1123 'end' => $end, 1124 ); 1125 $times = array(); 1126 for ($s = $start; $s+$duration <= $end && $s < $daybegin+DAY_s; $s += 60*$this->cal_prefs['interval']) 1127 { 1128 $e = $s + $duration; 1129 $end_date = $e-$daybegin > DAY_s ? lang(date('l',$e)).' '.date($this->common_prefs['dateformat'],$e).' ' : ''; 1130 $times[$s] = date($time_format,$s).' - '.$end_date.date($time_format,$e); 1131 } 1132 $sel_options[$n.'[start]'] = $times; 1133 } 1134 } 1135 return $freetime_daywise; 1136 } 1137 1138 /** 1139 * Export events as vCalendar version 2.0 files (iCal) 1140 * 1141 * @param int/array $content=0 numeric cal_id or submitted content from etempalte::exec 1142 * @param boolean $return_error=false should an error-msg be returned or a regular page with it generated (default) 1143 * @return string error-msg if $return_error 1144 */ 1145 function export($content=0,$return_error=false) 1146 { 1147 if (is_numeric($cal_id = $content ? $content : $_REQUEST['cal_id'])) 1148 { 1149 if (!($ical =& ExecMethod2('calendar.boical.exportVCal',$cal_id,'2.0'))) 1150 { 1151 $msg = lang('Permission denied'); 1152 1153 if ($return_error) return $msg; 1154 } 1155 else 1156 { 1157 $GLOBALS['egw']->browser =& CreateObject('phpgwapi.browser'); 1158 $GLOBALS['egw']->browser->content_header('event.ics','text/calendar',strlen($ical)); 1159 echo $ical; 1160 $GLOBALS['egw']->common->egw_exit(); 1161 } 1162 } 1163 if (is_array($content)) 1164 { 1165 $events =& $this->bo->search(array( 1166 'start' => $content['start'], 1167 'end' => $content['end'], 1168 'enum_recuring' => false, 1169 'daywise' => false, 1170 'owner' => $this->owner, 1171 'date_format' => 'server', // timestamp in server time for boical class 1172 )); 1173 if (!$events) 1174 { 1175 $msg = lang('No events found'); 1176 } 1177 else 1178 { 1179 $ical =& ExecMethod2('calendar.boical.exportVCal',$events,'2.0'/*$content['version']*/); 1180 $GLOBALS['egw']->browser =& CreateObject('phpgwapi.browser'); 1181 $GLOBALS['egw']->browser->content_header($content['file'] ? $content['file'] : 'event.ics','text/calendar',strlen($ical)); 1182 echo $ical; 1183 $GLOBALS['egw']->common->egw_exit(); 1184 } 1185 } 1186 if (!is_array($content)) 1187 { 1188 $view = $GLOBALS['egw']->session->appsession('view','calendar'); 1189 1190 $content = array( 1191 'start' => $this->bo->date2ts($_REQUEST['start'] ? $_REQUEST['start'] : $this->date), 1192 'end' => $this->bo->date2ts($_REQUEST['end'] ? $_REQUEST['end'] : $this->date), 1193 'file' => 'event.ics', 1194 'version' => '2.0', 1195 ); 1196 } 1197 $content['msg'] = $msg; 1198 1199 $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('iCal Export'); 1200 $etpl =& CreateObject('etemplate.etemplate','calendar.export'); 1201 1202 $etpl->exec('calendar.uiforms.export',$content); 1203 } 1204 1205 /** 1206 * Import events as vCalendar version 2.0 files (iCal) 1207 * 1208 * @param array $content=null submitted content from etempalte::exec 1209 */ 1210 function import($content=null) 1211 { 1212 if (is_array($content)) 1213 { 1214 if (is_array($content['ical_file']) && is_uploaded_file($content['ical_file']['tmp_name'])) 1215 { 1216 if (!ExecMethod('calendar.boical.importVCal',file_get_contents($content['ical_file']['tmp_name']))) 1217 { 1218 $msg = lang('Error: importing the iCal'); 1219 } 1220 else 1221 { 1222 $msg = lang('iCal successful imported'); 1223 } 1224 } 1225 else 1226 { 1227 $msg = lang('You need to select an iCal file first'); 1228 } 1229 } 1230 $content = array( 1231 'msg' => $msg, 1232 ); 1233 $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('iCal Import'); 1234 $etpl =& CreateObject('etemplate.etemplate','calendar.import'); 1235 1236 $etpl->exec('calendar.uiforms.import',$content); 1237 } 1238 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 17:20:01 2007 | par Balluche grâce à PHPXref 0.7 |