Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like calendar_uiforms often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use calendar_uiforms, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 33 | class calendar_uiforms extends calendar_ui |
||
| 34 | { |
||
| 35 | var $public_functions = array( |
||
| 36 | 'freetimesearch' => True, |
||
| 37 | 'edit' => true, |
||
| 38 | 'process_edit' => true, |
||
| 39 | 'export' => true, |
||
| 40 | 'import' => true, |
||
| 41 | 'cat_acl' => true, |
||
| 42 | 'meeting' => true, |
||
| 43 | 'mail_import' => true, |
||
| 44 | ); |
||
| 45 | |||
| 46 | /** |
||
| 47 | * Standard durations used in edit and freetime search |
||
| 48 | * |
||
| 49 | * @var array |
||
| 50 | */ |
||
| 51 | var $durations = array(); |
||
| 52 | |||
| 53 | /** |
||
| 54 | * default locking time for entries, that are opened by another user |
||
| 55 | * |
||
| 56 | * @var locktime in seconds |
||
| 57 | */ |
||
| 58 | var $locktime_default=1; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Constructor |
||
| 62 | */ |
||
| 63 | function __construct() |
||
| 72 | |||
| 73 | /** |
||
| 74 | * Create a default event (adding a new event) by evaluating certain _GET vars |
||
| 75 | * |
||
| 76 | * @return array event-array |
||
| 77 | */ |
||
| 78 | function &default_add_event() |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Process the edited event and evtl. call edit to redisplay it |
||
| 248 | * |
||
| 249 | * @param array $content posted eTemplate content |
||
| 250 | * @ToDo add conflict check / available quantity of resources when adding participants |
||
| 251 | */ |
||
| 252 | function process_edit($content) |
||
| 253 | { |
||
| 254 | if (!is_array($content)) // redirect from etemplate, if POST empty |
||
| 255 | { |
||
| 256 | return $this->edit(null,null,strip_tags($_GET['msg'])); |
||
| 257 | } |
||
| 258 | // clear notification errors |
||
| 259 | notifications::errors(true); |
||
| 260 | $messages = null; |
||
| 261 | $msg_permission_denied_added = false; |
||
| 262 | |||
| 263 | // We'd like to just refresh the data as that's the fastest, but some changes |
||
| 264 | // affect more than just one event widget, so require a full refresh. |
||
| 265 | // $update_type is one of the update types |
||
| 266 | // (add, edit, update, delete) |
||
| 267 | $update_type = $content['id'] ? ($content['recur_type'] == MCAL_RECUR_NONE ? 'update' : 'edit') : 'add'; |
||
| 268 | |||
| 269 | list($button) = @each($content['button']); |
||
| 270 | if (!$button && $content['action']) $button = $content['action']; // action selectbox |
||
| 271 | unset($content['button']); unset($content['action']); |
||
| 272 | |||
| 273 | $view = $content['view']; |
||
| 274 | if ($button == 'ical') |
||
| 275 | { |
||
| 276 | $msg = $this->export($content['id'],true); |
||
| 277 | } |
||
| 278 | // delete a recur-exception |
||
| 279 | if ($content['recur_exception']['delete_exception']) |
||
| 280 | { |
||
| 281 | list($date) = each($content['recur_exception']['delete_exception']); |
||
| 282 | // eT2 converts time to |
||
| 283 | if (!is_numeric($date)) $date = Api\DateTime::to (str_replace('Z','', $date), 'ts'); |
||
| 284 | unset($content['recur_exception']['delete_exception']); |
||
| 285 | if (($key = array_search($date,$content['recur_exception'])) !== false) |
||
| 286 | { |
||
| 287 | // propagate the exception to a single event |
||
| 288 | $recur_exceptions = $this->bo->so->get_related($content['uid']); |
||
| 289 | foreach ($recur_exceptions as $id) |
||
| 290 | { |
||
| 291 | if (!($exception = $this->bo->read($id)) || |
||
| 292 | $exception['recurrence'] != $content['recur_exception'][$key]) continue; |
||
| 293 | $exception['uid'] = Api\CalDAV::generate_uid('calendar', $id); |
||
| 294 | $exception['reference'] = $exception['recurrence'] = 0; |
||
| 295 | $this->bo->update($exception, true, true,false,true,$messages,$content['no_notifications']); |
||
| 296 | break; |
||
| 297 | } |
||
| 298 | unset($content['recur_exception'][$key]); |
||
| 299 | $content['recur_exception'] = array_values($content['recur_exception']); |
||
| 300 | } |
||
| 301 | $update_type = 'edit'; |
||
| 302 | } |
||
| 303 | // delete an alarm |
||
| 304 | if ($content['alarm']['delete_alarm']) |
||
| 305 | { |
||
| 306 | list($id) = each($content['alarm']['delete_alarm']); |
||
| 307 | //echo "delete alarm $id"; _debug_array($content['alarm']['delete_alarm']); |
||
| 308 | |||
| 309 | if ($content['id']) |
||
| 310 | { |
||
| 311 | if ($this->bo->delete_alarm($id)) |
||
| 312 | { |
||
| 313 | $msg = lang('Alarm deleted'); |
||
| 314 | unset($content['alarm'][$id]); |
||
| 315 | } |
||
| 316 | else |
||
| 317 | { |
||
| 318 | $msg = lang('Permission denied'); |
||
| 319 | } |
||
| 320 | } |
||
| 321 | else |
||
| 322 | { |
||
| 323 | unset($content['alarm'][$id]); |
||
| 324 | } |
||
| 325 | } |
||
| 326 | if ($content['duration']) |
||
| 327 | { |
||
| 328 | $content['end'] = $content['start'] + $content['duration']; |
||
| 329 | } |
||
| 330 | // fix default alarm for a new (whole day) event, to be according to default-alarm(-wholeday) pref |
||
| 331 | if ($content['alarm'][1]['default']) |
||
| 332 | { |
||
| 333 | $def_alarm = $this->cal_prefs['default-alarm'.($content['whole_day'] ? '-wholeday' : '')]; |
||
| 334 | if ((string)$def_alarm === '') |
||
| 335 | { |
||
| 336 | unset($content['alarm'][1]); // '' = no alarm on whole day --> delete it |
||
| 337 | } |
||
| 338 | else |
||
| 339 | { |
||
| 340 | $content['alarm'][1]['offset'] = $offset = 60 * $def_alarm; |
||
| 341 | $content['start'][1]['offset'] = $this->bo->date2ts($content['start']) - $offset; |
||
| 342 | } |
||
| 343 | } |
||
| 344 | |||
| 345 | $event = $content; |
||
| 346 | unset($event['new_alarm']); |
||
| 347 | unset($event['alarm']['delete_alarm']); |
||
| 348 | unset($event['duration']); |
||
| 349 | |||
| 350 | if (in_array($button,array('ignore','freetime','reedit','confirm_edit_series'))) |
||
| 351 | { |
||
| 352 | // no conversation necessary, event is already in the right format |
||
| 353 | } |
||
| 354 | else |
||
| 355 | { |
||
| 356 | // convert content => event |
||
| 357 | if ($content['whole_day']) |
||
| 358 | { |
||
| 359 | $event['start'] = $this->bo->date2array($event['start']); |
||
| 360 | $event['start']['hour'] = $event['start']['minute'] = 0; unset($event['start']['raw']); |
||
| 361 | $event['start'] = $this->bo->date2ts($event['start']); |
||
| 362 | $event['end'] = $this->bo->date2array($event['end']); |
||
| 363 | $event['end']['hour'] = 23; $event['end']['minute'] = $event['end']['second'] = 59; unset($event['end']['raw']); |
||
| 364 | $event['end'] = $this->bo->date2ts($event['end']); |
||
| 365 | } |
||
| 366 | // some checks for recurrences, if you give a date, make it a weekly repeating event and visa versa |
||
| 367 | if ($event['recur_type'] == MCAL_RECUR_NONE && $event['recur_data']) $event['recur_type'] = MCAL_RECUR_WEEKLY; |
||
| 368 | View Code Duplication | if ($event['recur_type'] == MCAL_RECUR_WEEKLY && !$event['recur_data']) |
|
| 369 | { |
||
| 370 | $event['recur_data'] = 1 << (int)date('w',$event['start']); |
||
| 371 | } |
||
| 372 | if ($event['recur_type'] != MCAL_RECUR_NONE && !isset($event['recur_enddate'])) |
||
| 373 | { |
||
| 374 | // No recur end date, make sure it's set to something or it won't be changed |
||
| 375 | $event['recur_enddate'] = 0; |
||
| 376 | } |
||
| 377 | if (isset($content['participants'])) |
||
| 378 | { |
||
| 379 | |||
| 380 | $event['participants'] = $event['participant_types'] = array(); |
||
| 381 | |||
| 382 | foreach($content['participants'] as $key => $data) |
||
| 383 | { |
||
| 384 | switch($key) |
||
| 385 | { |
||
| 386 | case 'delete': // handled in default |
||
| 387 | case 'quantity': // handled in new_resource |
||
| 388 | case 'role': // handled in add, account or resource |
||
| 389 | case 'cal_resources': |
||
| 390 | case 'status_date': |
||
| 391 | break; |
||
| 392 | case 'participant': |
||
| 393 | foreach($data as $participant) |
||
| 394 | { |
||
| 395 | if (is_null($participant)) |
||
| 396 | { |
||
| 397 | continue; |
||
| 398 | } |
||
| 399 | |||
| 400 | // email or rfc822 addresse (eg. "Ralf Becker <[email protected]>") |
||
| 401 | $email = array(); |
||
| 402 | if(preg_match('/^(.*<)?([a-z0-9_.-]+@[a-z0-9_.-]{5,})>?$/i',$participant,$email)) |
||
| 403 | { |
||
| 404 | $status = calendar_so::combine_status('U',$content['participants']['quantity'],$content['participants']['role']); |
||
| 405 | if (($data = $GLOBALS['egw']->accounts->name2id($email[2],'account_email')) && $this->bo->check_acl_invite($data)) |
||
| 406 | { |
||
| 407 | $event['participants'][$data] = $event['participant_types']['u'][$data] = $status; |
||
| 408 | } |
||
| 409 | elseif ((list($data) = ExecMethod2('addressbook.addressbook_bo.search',array( |
||
| 410 | 'email' => $email[2], |
||
| 411 | 'email_home' => $email[2], |
||
| 412 | ),true,'','','',false,'OR'))) |
||
| 413 | { |
||
| 414 | $event['participants']['c'.$data['id']] = $event['participant_types']['c'][$data['id']] = $status; |
||
| 415 | } |
||
| 416 | else |
||
| 417 | { |
||
| 418 | $event['participants']['e'.$participant] = $event['participant_types']['e'][$participant] = $status; |
||
| 419 | } |
||
| 420 | } |
||
| 421 | else |
||
| 422 | { |
||
| 423 | if(is_numeric($participant)) |
||
| 424 | { |
||
| 425 | $uid = $participant; |
||
| 426 | $id = $participant; |
||
| 427 | $resource = $this->bo->resources['']; |
||
| 428 | } |
||
| 429 | else |
||
| 430 | { |
||
| 431 | $uid = $participant; |
||
| 432 | $id = substr($participant,1); |
||
| 433 | $resource = $this->bo->resources[$participant[0]]; |
||
| 434 | } |
||
| 435 | if(!$this->bo->check_acl_invite($uid)) |
||
| 436 | { |
||
| 437 | if(!$msg_permission_denied_added) |
||
| 438 | { |
||
| 439 | $msg .= lang('Permission denied!'); |
||
| 440 | $msg_permission_denied_added = true; |
||
| 441 | } |
||
| 442 | continue; |
||
| 443 | } |
||
| 444 | |||
| 445 | $type = $resource['type']; |
||
| 446 | $status = isset($this->bo->resources[$type]['new_status']) ? |
||
| 447 | ExecMethod($this->bo->resources[$type]['new_status'],$id) : |
||
| 448 | ($uid == $this->bo->user ? 'A' : 'U'); |
||
| 449 | |||
| 450 | // Expand mailing lists |
||
| 451 | if($type == 'l') |
||
| 452 | { |
||
| 453 | foreach($this->bo->enum_mailing_list($participant) as $contact) |
||
| 454 | { |
||
| 455 | // Mailing lists can contain users, so allow for that possibility |
||
| 456 | $_type = is_numeric($contact) ? '' : $contact[0]; |
||
| 457 | $_uid = is_numeric($contact) ? $contact : substr($contact,1); |
||
| 458 | $event['participants'][$contact] = $event['participant_types'][$_type][$_uid] = |
||
| 459 | calendar_so::combine_status($status,$content['participants']['quantity'],$content['participants']['role']); |
||
| 460 | } |
||
| 461 | continue; |
||
| 462 | } |
||
| 463 | if ($status) |
||
| 464 | { |
||
| 465 | $res_info = $this->bo->resource_info($uid); |
||
| 466 | // todo check real availability = maximum - already booked quantity |
||
| 467 | if (isset($res_info['useable']) && $content['participants']['quantity'] > $res_info['useable']) |
||
| 468 | { |
||
| 469 | $msg .= lang('Maximum available quantity of %1 exceeded!',$res_info['useable']); |
||
| 470 | foreach(array('quantity','resource','role') as $n) |
||
| 471 | { |
||
| 472 | $event['participants'][$n] = $content['participants'][$n]; |
||
| 473 | } |
||
| 474 | continue; |
||
| 475 | } |
||
| 476 | else |
||
| 477 | { |
||
| 478 | $event['participants'][$uid] = $event['participant_types'][$type][$id] = |
||
| 479 | calendar_so::combine_status($status,$content['participants']['quantity'],$content['participants']['role']); |
||
| 480 | } |
||
| 481 | } |
||
| 482 | } |
||
| 483 | } |
||
| 484 | break; |
||
| 485 | case 'add': |
||
| 486 | if (!$content['participants']['participant']) |
||
| 487 | { |
||
| 488 | $msg = lang('You need to select an account, contact or resource first!'); |
||
| 489 | } |
||
| 490 | break; |
||
| 491 | |||
| 492 | default: // existing participant row |
||
| 493 | if (!is_array($data)) continue; // widgets in participant tab, above participant list |
||
| 494 | foreach(array('uid','status','quantity','role') as $name) |
||
| 495 | { |
||
| 496 | $$name = $data[$name]; |
||
| 497 | } |
||
| 498 | if ($content['participants']['delete'][$uid] || $content['participants']['delete'][md5($uid)]) |
||
| 499 | { |
||
| 500 | $uid = false; // entry has been deleted |
||
| 501 | } |
||
| 502 | elseif ($uid) |
||
| 503 | { |
||
| 504 | if (is_numeric($uid)) |
||
| 505 | { |
||
| 506 | $id = $uid; |
||
| 507 | $type = 'u'; |
||
| 508 | } |
||
| 509 | else |
||
| 510 | { |
||
| 511 | $id = substr($uid,1); |
||
| 512 | $type = $uid[0]; |
||
| 513 | } |
||
| 514 | if ($data['old_status'] != $status && !(!$data['old_status'] && $status == 'G')) |
||
| 515 | { |
||
| 516 | //echo "<p>$uid: status changed '$data[old_status]' --> '$status<'/p>\n"; |
||
| 517 | $new_status = calendar_so::combine_status($status, $quantity, $role); |
||
| 518 | if ($this->bo->set_status($event['id'],$uid,$new_status,isset($content['edit_single']) ? $content['participants']['status_date'] : 0, false, true, $content['no_notifications'])) |
||
| 519 | { |
||
| 520 | // Update main window |
||
| 521 | $d = new Api\DateTime($content['edit_single'], Api\DateTime::$user_timezone); |
||
| 522 | $client_updated = $this->update_client($event['id'], $d); |
||
| 523 | |||
| 524 | // refreshing the calendar-view with the changed participant-status |
||
| 525 | if($event['recur_type'] != MCAL_RECUR_NONE) |
||
| 526 | { |
||
| 527 | $msg = lang('Status for all future scheduled days changed'); |
||
| 528 | } |
||
| 529 | else |
||
| 530 | { |
||
| 531 | if(isset($content['edit_single'])) |
||
| 532 | { |
||
| 533 | $msg = lang('Status for this particular day changed'); |
||
| 534 | // prevent accidentally creating a real exception afterwards |
||
| 535 | $view = true; |
||
| 536 | $hide_delete = true; |
||
| 537 | } |
||
| 538 | else |
||
| 539 | { |
||
| 540 | $msg = lang('Status changed'); |
||
| 541 | //Refresh the event in the main window after changing status |
||
| 542 | Framework::refresh_opener($msg, 'calendar', $event['id'], $client_updated ? 'update' : 'delete'); |
||
| 543 | } |
||
| 544 | } |
||
| 545 | if (!$content['no_popup']) |
||
| 546 | { |
||
| 547 | //we are handling refreshing for status changes on client side |
||
| 548 | } |
||
| 549 | if ($status == 'R' && $event['alarm']) |
||
| 550 | { |
||
| 551 | // remove from bo->set_status deleted alarms of rejected users from UI too |
||
| 552 | foreach($event['alarm'] as $alarm_id => $alarm) |
||
| 553 | { |
||
| 554 | if ((string)$alarm['owner'] === (string)$uid) |
||
| 555 | { |
||
| 556 | unset($event['alarm'][$alarm_id]); |
||
| 557 | } |
||
| 558 | } |
||
| 559 | } |
||
| 560 | } |
||
| 561 | } |
||
| 562 | if ($uid && $status != 'G') |
||
| 563 | { |
||
| 564 | $event['participants'][$uid] = $event['participant_types'][$type][$id] = |
||
| 565 | calendar_so::combine_status($status,$quantity,$role); |
||
| 566 | } |
||
| 567 | } |
||
| 568 | break; |
||
| 569 | } |
||
| 570 | } |
||
| 571 | } |
||
| 572 | } |
||
| 573 | $preserv = array( |
||
| 574 | 'view' => $view, |
||
| 575 | 'hide_delete' => $hide_delete, |
||
| 576 | 'edit_single' => $content['edit_single'], |
||
| 577 | 'reference' => $content['reference'], |
||
| 578 | 'recurrence' => $content['recurrence'], |
||
| 579 | 'actual_date' => $content['actual_date'], |
||
| 580 | 'no_popup' => $content['no_popup'], |
||
| 581 | 'tabs' => $content['tabs'], |
||
| 582 | 'template' => $content['template'], |
||
| 583 | ); |
||
| 584 | $noerror=true; |
||
| 585 | |||
| 586 | //error_log(__METHOD__.$button.'#'.array2string($content['edit_single']).'#'); |
||
| 587 | |||
| 588 | $ignore_conflicts = $status_reset_to_unknown = false; |
||
| 589 | |||
| 590 | switch((string)$button) |
||
| 591 | { |
||
| 592 | case 'ignore': |
||
| 593 | $ignore_conflicts = true; |
||
| 594 | $button = $event['button_was']; // save or apply |
||
| 595 | unset($event['button_was']); |
||
| 596 | break; |
||
| 597 | |||
| 598 | } |
||
| 599 | |||
| 600 | switch((string)$button) |
||
| 601 | { |
||
| 602 | case 'exception': // create an exception in a recuring event |
||
| 603 | $msg = $this->_create_exception($event,$preserv); |
||
| 604 | break; |
||
| 605 | |||
| 606 | case 'copy': // create new event with copied content, some content need to be unset to make a "new" event |
||
| 607 | unset($event['id']); |
||
| 608 | unset($event['uid']); |
||
| 609 | unset($event['reference']); |
||
| 610 | unset($preserv['reference']); |
||
| 611 | unset($event['recurrence']); |
||
| 612 | unset($preserv['recurrence']); |
||
| 613 | unset($event['recur_exception']); |
||
| 614 | unset($event['edit_single']); // in case it has been set |
||
| 615 | unset($event['modified']); |
||
| 616 | unset($event['modifier']); |
||
| 617 | unset($event['caldav_name']); |
||
| 618 | $event['owner'] = !(int)$event['owner'] || !$this->bo->check_perms(Acl::ADD,0,$event['owner']) ? $this->user : $event['owner']; |
||
| 619 | |||
| 620 | // Clear participant stati |
||
| 621 | foreach($event['participant_types'] as $type => &$participants) |
||
| 622 | { |
||
| 623 | foreach($participants as $id => &$p_response) |
||
| 624 | { |
||
| 625 | if($type == 'u' && $id == $event['owner']) continue; |
||
| 626 | calendar_so::split_status($p_response, $quantity, $role); |
||
| 627 | // if resource defines callback for status of new status (eg. Resources app acknowledges direct booking acl), call it |
||
| 628 | $status = isset($this->bo->resources[$type]['new_status']) ? ExecMethod($this->bo->resources[$type]['new_status'],$id) : 'U'; |
||
| 629 | $p_response = calendar_so::combine_status($status,$quantity,$role); |
||
| 630 | } |
||
| 631 | } |
||
| 632 | |||
| 633 | // Copy alarms |
||
| 634 | if (is_array($event['alarm'])) |
||
| 635 | { |
||
| 636 | foreach($event['alarm'] as $n => &$alarm) |
||
| 637 | { |
||
| 638 | unset($alarm['id']); |
||
| 639 | unset($alarm['cal_id']); |
||
| 640 | } |
||
| 641 | } |
||
| 642 | |||
| 643 | // Get links to be copied |
||
| 644 | // With no ID, $content['link_to']['to_id'] is used |
||
| 645 | $content['link_to']['to_id'] = array('to_app' => 'calendar', 'to_id' => 0); |
||
| 646 | foreach(Link::get_links('calendar', $content['id']) as $link) |
||
| 647 | { |
||
| 648 | View Code Duplication | if ($link['app'] != Link::VFS_APPNAME) |
|
| 649 | { |
||
| 650 | Link::link('calendar', $content['link_to']['to_id'], $link['app'], $link['id'], $link['remark']); |
||
| 651 | } |
||
| 652 | elseif ($link['app'] == Link::VFS_APPNAME) |
||
| 653 | { |
||
| 654 | Link::link('calendar', $content['link_to']['to_id'], Link::VFS_APPNAME, array( |
||
| 655 | 'tmp_name' => Link::vfs_path($link['app2'], $link['id2']).'/'.$link['id'], |
||
| 656 | 'name' => $link['id'], |
||
| 657 | ), $link['remark']); |
||
| 658 | } |
||
| 659 | } |
||
| 660 | unset($link); |
||
| 661 | $preserv['view'] = $preserv['edit_single'] = false; |
||
| 662 | $msg = lang('%1 copied - the copy can now be edited', lang(Link::get_registry('calendar','entry'))); |
||
| 663 | $event['title'] = lang('Copy of:').' '.$event['title']; |
||
| 664 | break; |
||
| 665 | |||
| 666 | case 'mail': |
||
| 667 | case 'sendrequest': |
||
| 668 | case 'save': |
||
| 669 | case 'print': |
||
| 670 | case 'apply': |
||
| 671 | case 'infolog': |
||
| 672 | if ($event['id'] && !$this->bo->check_perms(Acl::EDIT,$event)) |
||
| 673 | { |
||
| 674 | $msg = lang('Permission denied'); |
||
| 675 | $button = ''; |
||
| 676 | break; |
||
| 677 | } |
||
| 678 | if ($event['start'] > $event['end']) |
||
| 679 | { |
||
| 680 | $msg = lang('Error: Starttime has to be before the endtime !!!'); |
||
| 681 | $button = ''; |
||
| 682 | break; |
||
| 683 | } |
||
| 684 | if ($event['recur_type'] != MCAL_RECUR_NONE && $event['recur_enddate'] && $event['start'] > $event['recur_enddate']) |
||
| 685 | { |
||
| 686 | $msg = lang('repetition').': '.lang('Error: Starttime has to be before the endtime !!!'); |
||
| 687 | $button = ''; |
||
| 688 | break; |
||
| 689 | } |
||
| 690 | if ($event['recur_type'] != MCAL_RECUR_NONE && $event['end']-$event['start'] > calendar_rrule::recurrence_interval($event['recur_type'], $event['recur_interval'])) |
||
| 691 | { |
||
| 692 | $msg = lang('Error: Duration of event longer then recurrence interval!'); |
||
| 693 | $button = ''; |
||
| 694 | break; |
||
| 695 | } |
||
| 696 | if (!$event['participants']) |
||
| 697 | { |
||
| 698 | $msg = lang('Error: no participants selected !!!'); |
||
| 699 | $button = ''; |
||
| 700 | break; |
||
| 701 | } |
||
| 702 | // if private event with ressource reservation is forbidden |
||
| 703 | if (!$event['public'] && $GLOBALS['egw_info']['server']['no_ressources_private']) |
||
| 704 | { |
||
| 705 | foreach (array_keys($event['participants']) as $uid) |
||
| 706 | { |
||
| 707 | if ($uid[0] == 'r') //ressource detection |
||
| 708 | { |
||
| 709 | $msg = lang('Error: ressources reservation in private events is not allowed!!!'); |
||
| 710 | $button = ''; |
||
| 711 | break 2; //break foreach and case |
||
| 712 | } |
||
| 713 | } |
||
| 714 | } |
||
| 715 | if ($content['edit_single']) // we edited a single event from a series |
||
| 716 | { |
||
| 717 | $event['reference'] = $event['id']; |
||
| 718 | $event['recurrence'] = $content['edit_single']; |
||
| 719 | unset($event['id']); |
||
| 720 | $conflicts = $this->bo->update($event,$ignore_conflicts,true,false,true,$messages,$content['no_notifications']); |
||
| 721 | if (!is_array($conflicts) && $conflicts) |
||
| 722 | { |
||
| 723 | // now we need to add the original start as recur-execption to the series |
||
| 724 | $recur_event = $this->bo->read($event['reference']); |
||
| 725 | $recur_event['recur_exception'][] = $content['edit_single']; |
||
| 726 | // check if we need to move the alarms, because they are next on that exception |
||
| 727 | foreach($recur_event['alarm'] as $id => $alarm) |
||
| 728 | { |
||
| 729 | if ($alarm['time'] == $content['edit_single'] - $alarm['offset']) |
||
| 730 | { |
||
| 731 | $rrule = calendar_rrule::event2rrule($recur_event, true); |
||
| 732 | foreach ($rrule as $time) |
||
| 733 | { |
||
| 734 | View Code Duplication | if ($content['edit_single'] < $time->format('ts')) |
|
| 735 | { |
||
| 736 | $alarm['time'] = $time->format('ts') - $alarm['offset']; |
||
| 737 | $this->bo->save_alarm($event['reference'], $alarm); |
||
| 738 | break; |
||
| 739 | } |
||
| 740 | } |
||
| 741 | } |
||
| 742 | } |
||
| 743 | unset($recur_event['start']); unset($recur_event['end']); // no update necessary |
||
| 744 | unset($recur_event['alarm']); // unsetting alarms too, as they cant be updated without start! |
||
| 745 | $this->bo->update($recur_event,true); // no conflict check here |
||
| 746 | |||
| 747 | // Save links |
||
| 748 | if($content['links']) |
||
| 749 | { |
||
| 750 | Link::link('calendar', $event['id'], $content['links']['to_id']); |
||
| 751 | } |
||
| 752 | |||
| 753 | if(Api\Json\Response::isJSONResponse()) |
||
| 754 | { |
||
| 755 | // Sending null will trigger a removal of the original |
||
| 756 | // for that date |
||
| 757 | Api\Json\Response::get()->generic('data', array('uid' => 'calendar::'.$content['reference'].':'.$content['actual_date'], 'data' => null)); |
||
| 758 | } |
||
| 759 | |||
| 760 | unset($recur_event); |
||
| 761 | unset($event['edit_single']); // if we further edit it, it's just a single event |
||
| 762 | unset($preserv['edit_single']); |
||
| 763 | } |
||
| 764 | else // conflict or error, we need to reset everything to the state befor we tried to save it |
||
| 765 | { |
||
| 766 | $event['id'] = $event['reference']; |
||
| 767 | $event['reference'] = $event['recurrence'] = 0; |
||
| 768 | $event['uid'] = $content['uid']; |
||
| 769 | } |
||
| 770 | $update_type = 'edit'; |
||
| 771 | } |
||
| 772 | else // we edited a non-reccuring event or the whole series |
||
| 773 | { |
||
| 774 | if (($old_event = $this->bo->read($event['id']))) |
||
| 775 | { |
||
| 776 | if ($event['recur_type'] != MCAL_RECUR_NONE) |
||
| 777 | { |
||
| 778 | $update_type = 'edit'; |
||
| 779 | |||
| 780 | // we edit a existing series event |
||
| 781 | if ($event['start'] != $old_event['start'] || |
||
| 782 | $event['whole_day'] != $old_event['whole_day'] || |
||
| 783 | $event['end'] != $old_event['end']) |
||
| 784 | { |
||
| 785 | // calculate offset against old series start or clicked recurrance, |
||
| 786 | // depending on which is smaller |
||
| 787 | $offset = $event['start'] - $old_event['start']; |
||
| 788 | if (abs($offset) > abs($off2 = $event['start'] - $event['actual_date'])) |
||
| 789 | { |
||
| 790 | $offset = $off2; |
||
| 791 | } |
||
| 792 | $msg = $this->_break_recurring($event, $old_event, $event['actual_date'] + $offset,$content['no_notifications']); |
||
| 793 | if($msg) |
||
| 794 | { |
||
| 795 | $noerror = false; |
||
| 796 | } |
||
| 797 | } |
||
| 798 | } |
||
| 799 | else |
||
| 800 | { |
||
| 801 | if ($old_event['start'] != $event['start'] || |
||
| 802 | $old_event['end'] != $event['end'] || |
||
| 803 | $event['whole_day'] != $old_event['whole_day']) |
||
| 804 | { |
||
| 805 | $sameday = (date('Ymd', $old_event['start']) == date('Ymd', $event['start'])); |
||
| 806 | foreach((array)$event['participants'] as $uid => $status) |
||
| 807 | { |
||
| 808 | $q = $r = null; |
||
| 809 | calendar_so::split_status($status,$q,$r); |
||
| 810 | if ($uid[0] != 'c' && $uid[0] != 'e' && $uid != $this->bo->user && $status != 'U') |
||
| 811 | { |
||
| 812 | $preferences = new Api\Preferences($uid); |
||
| 813 | $part_prefs = $preferences->read_repository(); |
||
| 814 | switch ($part_prefs['calendar']['reset_stati']) |
||
| 815 | { |
||
| 816 | case 'no': |
||
| 817 | break; |
||
| 818 | case 'startday': |
||
| 819 | if ($sameday) break; |
||
| 820 | default: |
||
| 821 | $status_reset_to_unknown = true; |
||
| 822 | $event['participants'][$uid] = calendar_so::combine_status('U',$q,$r); |
||
| 823 | // todo: report reset status to user |
||
| 824 | } |
||
| 825 | } |
||
| 826 | } |
||
| 827 | // check if we need to move the alarms, because they are relative |
||
| 828 | $this->bo->check_move_alarms($event, $old_event); |
||
| 829 | } |
||
| 830 | } |
||
| 831 | } |
||
| 832 | // Adding participants needs to be done as an edit, in case we |
||
| 833 | // have participants visible in seperate calendars |
||
| 834 | if(is_array($old_event['participants']) && count(array_diff_key($event['participants'], $old_event['participants']))) |
||
| 835 | { |
||
| 836 | $update_type = 'edit'; |
||
| 837 | } |
||
| 838 | // Changing category may affect event filtering |
||
| 839 | if($this->cal_prefs['saved_states']['cat_id'] && $old_event['category'] != $event['category']) |
||
| 840 | { |
||
| 841 | $update_type = 'edit'; |
||
| 842 | } |
||
| 843 | $conflicts = $this->bo->update($event,$ignore_conflicts,true,false,true,$messages,$content['no_notifications']); |
||
| 844 | unset($event['ignore']); |
||
| 845 | } |
||
| 846 | if (is_array($conflicts)) |
||
| 847 | { |
||
| 848 | $event['button_was'] = $button; // remember for ignore |
||
| 849 | return $this->conflicts($event,$conflicts,$preserv); |
||
| 850 | } |
||
| 851 | |||
| 852 | // Event spans multiple days, need an edit to make sure they all get updated |
||
| 853 | // We could check old date, as removing from days could still be an update |
||
| 854 | if(date('Ymd', $event['start']) != date('Ymd', $event['end'])) |
||
| 855 | { |
||
| 856 | $update_type = 'edit'; |
||
| 857 | } |
||
| 858 | // check if there are messages from update, eg. removed participants or Api\Categories because of missing rights |
||
| 859 | if ($messages) |
||
| 860 | { |
||
| 861 | $msg .= ($msg ? ', ' : '').implode(', ',$messages); |
||
| 862 | } |
||
| 863 | if ($conflicts === 0) |
||
| 864 | { |
||
| 865 | $msg .= ($msg ? ', ' : '') .lang('Error: the entry has been updated since you opened it for editing!').'<br />'. |
||
| 866 | lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','<a href="'. |
||
| 867 | htmlspecialchars(Egw::link('/index.php',array( |
||
| 868 | 'menuaction' => 'calendar.calendar_uiforms.edit', |
||
| 869 | 'cal_id' => $content['id'], |
||
| 870 | ))).'">','</a>'); |
||
| 871 | $noerror = false; |
||
| 872 | } |
||
| 873 | elseif ($conflicts > 0) |
||
| 874 | { |
||
| 875 | // series moved by splitting in two --> move alarms and exceptions |
||
| 876 | if ($old_event && $old_event['id'] != $event['id']) |
||
| 877 | { |
||
| 878 | $update_type = 'edit'; |
||
| 879 | foreach ((array)$old_event['alarms'] as $alarm) |
||
| 880 | { |
||
| 881 | // check if alarms still needed in old event, if not delete it |
||
| 882 | $event_time = $alarm['time'] + $alarm['offset']; |
||
| 883 | if ($event_time >= $this->bo->now_su) |
||
| 884 | { |
||
| 885 | $this->bo->delete_alarm($alarm['id']); |
||
| 886 | } |
||
| 887 | $alarm['time'] += $offset; |
||
| 888 | unset($alarm['id']); |
||
| 889 | // if alarm would be in the past (eg. event moved back) --> move to next possible recurrence |
||
| 890 | if ($alarm['time'] < $this->bo->now_su) |
||
| 891 | { |
||
| 892 | if (($next_occurrence = $this->bo->read($event['id'], $this->bo->now_su+$alarm['offset'], true))) |
||
| 893 | { |
||
| 894 | $alarm['time'] = $next_occurrence['start'] - $alarm['offset']; |
||
| 895 | } |
||
| 896 | else |
||
| 897 | { |
||
| 898 | $alarm = false; // no (further) recurence found --> ignore alarm |
||
| 899 | } |
||
| 900 | } |
||
| 901 | // alarm is currently on a previous recurrence --> set for first recurrence of new series |
||
| 902 | elseif ($event_time < $event['start']) |
||
| 903 | { |
||
| 904 | $alarm['time'] = $event['start'] - $alarm['offset']; |
||
| 905 | } |
||
| 906 | if ($alarm) |
||
| 907 | { |
||
| 908 | $alarm['id'] = $this->bo->save_alarm($event['id'], $alarm); |
||
| 909 | $event['alarm'][$alarm['id']] = $alarm; |
||
| 910 | } |
||
| 911 | } |
||
| 912 | // attach all future exceptions to the new series |
||
| 913 | $events =& $this->bo->search(array( |
||
| 914 | 'query' => array('cal_uid' => $old_event['uid']), |
||
| 915 | 'filter' => 'owner', // return all possible entries |
||
| 916 | 'daywise' => false, |
||
| 917 | 'date_format' => 'ts', |
||
| 918 | )); |
||
| 919 | foreach ((array)$events as $exception) |
||
| 920 | { |
||
| 921 | if ($exception['recurrence'] > $this->bo->now_su) |
||
| 922 | { |
||
| 923 | $exception['recurrence'] += $offset; |
||
| 924 | $exception['reference'] = $event['id']; |
||
| 925 | $exception['uid'] = $event['uid']; |
||
| 926 | $this->bo->update($exception, true, true, true, true, $msg=null, $content['no_notifications']); |
||
| 927 | } |
||
| 928 | } |
||
| 929 | } |
||
| 930 | |||
| 931 | $message = lang('Event saved'); |
||
| 932 | View Code Duplication | if ($status_reset_to_unknown) |
|
| 933 | { |
||
| 934 | foreach((array)$event['participants'] as $uid => $status) |
||
| 935 | { |
||
| 936 | if ($uid[0] != 'c' && $uid[0] != 'e' && $uid != $this->bo->user) |
||
| 937 | { |
||
| 938 | calendar_so::split_status($status,$q,$r); |
||
| 939 | $status = calendar_so::combine_status('U',$q,$r); |
||
| 940 | $this->bo->set_status($event['id'], $uid, $status, 0, true); |
||
| 941 | } |
||
| 942 | } |
||
| 943 | $message .= lang(', stati of participants reset'); |
||
| 944 | } |
||
| 945 | |||
| 946 | $response = Api\Json\Response::get(); |
||
| 947 | if($response && $update_type != 'delete') |
||
| 948 | { |
||
| 949 | $client_updated = $this->update_client($event['id']); |
||
| 950 | } |
||
| 951 | |||
| 952 | $msg = $message . ($msg ? ', ' . $msg : ''); |
||
| 953 | Framework::refresh_opener($msg, 'calendar', $event['id'], $client_updated ? ($event['recur_type'] ? 'edit' : $update_type) : 'delete'); |
||
| 954 | // writing links for new entry, existing ones are handled by the widget itself |
||
| 955 | View Code Duplication | if (!$content['id'] && is_array($content['link_to']['to_id'])) |
|
| 956 | { |
||
| 957 | Link::link('calendar',$event['id'],$content['link_to']['to_id']); |
||
| 958 | } |
||
| 959 | } |
||
| 960 | else |
||
| 961 | { |
||
| 962 | $msg = lang('Error: saving the event !!!'); |
||
| 963 | } |
||
| 964 | break; |
||
| 965 | |||
| 966 | case 'cancel': |
||
| 967 | if($content['cancel_needs_refresh']) |
||
| 968 | { |
||
| 969 | Framework::refresh_opener($msg, 'calendar'); |
||
| 970 | } |
||
| 971 | break; |
||
| 972 | |||
| 973 | case 'delete': // delete of event (regular or series) |
||
| 974 | $exceptions_kept = null; |
||
| 975 | if ($this->bo->delete($event['id'], (int)$content['edit_single'], false, $event['no_notifications'], |
||
| 976 | $content['delete_exceptions'] == 'true', $exceptions_kept)) |
||
| 977 | { |
||
| 978 | if ($event['recur_type'] != MCAL_RECUR_NONE && $content['reference'] == 0 && !$content['edit_single']) |
||
| 979 | { |
||
| 980 | $msg = lang('Series deleted'); |
||
| 981 | if ($exceptions_kept) $msg .= lang(', exceptions preserved'); |
||
| 982 | } |
||
| 983 | else |
||
| 984 | { |
||
| 985 | $msg = lang('Event deleted'); |
||
| 986 | } |
||
| 987 | |||
| 988 | } |
||
| 989 | break; |
||
| 990 | |||
| 991 | case 'freetime': |
||
| 992 | // the "click" has to be in onload, to make sure the button is already created |
||
| 993 | $event['button_was'] = $button; |
||
| 994 | break; |
||
| 995 | |||
| 996 | case 'add_alarm': |
||
| 997 | $time = $content['start']; |
||
| 998 | $offset = $time - $content['new_alarm']['date']; |
||
| 999 | if ($event['recur_type'] != MCAL_RECUR_NONE && |
||
| 1000 | ($next_occurrence = $this->bo->read($event['id'], $this->bo->now_su + $offset, true)) && |
||
| 1001 | $time < $next_occurrence['start']) |
||
| 1002 | { |
||
| 1003 | $content['new_alarm']['date'] = $next_occurrence['start'] - $offset; |
||
| 1004 | } |
||
| 1005 | if ($this->bo->check_perms(Acl::EDIT,!$content['new_alarm']['owner'] ? $event : 0,$content['new_alarm']['owner'])) |
||
| 1006 | { |
||
| 1007 | $alarm = array( |
||
| 1008 | 'offset' => $offset, |
||
| 1009 | 'time' => $content['new_alarm']['date'], |
||
| 1010 | 'all' => !$content['new_alarm']['owner'], |
||
| 1011 | 'owner' => $content['new_alarm']['owner'] ? $content['new_alarm']['owner'] : $this->user, |
||
| 1012 | ); |
||
| 1013 | if ($alarm['time'] < $this->bo->now_su) |
||
| 1014 | { |
||
| 1015 | $msg = lang("Can't add alarms in the past !!!"); |
||
| 1016 | } |
||
| 1017 | elseif ($event['id']) // save the alarm immediatly |
||
| 1018 | { |
||
| 1019 | if (($alarm_id = $this->bo->save_alarm($event['id'],$alarm))) |
||
| 1020 | { |
||
| 1021 | $alarm['id'] = $alarm_id; |
||
| 1022 | $event['alarm'][$alarm_id] = $alarm; |
||
| 1023 | |||
| 1024 | $msg = lang('Alarm added'); |
||
| 1025 | Framework::refresh_opener($msg,'calendar', $event['id'], 'update'); |
||
| 1026 | } |
||
| 1027 | else |
||
| 1028 | { |
||
| 1029 | $msg = lang('Error adding the alarm'); |
||
| 1030 | } |
||
| 1031 | } |
||
| 1032 | else |
||
| 1033 | { |
||
| 1034 | for($alarm['id']=1; isset($event['alarm'][$alarm['id']]); $alarm['id']++) {} // get a temporary non-conflicting, numeric id |
||
| 1035 | $event['alarm'][$alarm['id']] = $alarm; |
||
| 1036 | } |
||
| 1037 | } |
||
| 1038 | else |
||
| 1039 | { |
||
| 1040 | $msg = lang('Permission denied'); |
||
| 1041 | } |
||
| 1042 | break; |
||
| 1043 | } |
||
| 1044 | // add notification-errors, if we have some |
||
| 1045 | if (($notification_errors = notifications::errors(true))) |
||
| 1046 | { |
||
| 1047 | $msg .= ($msg ? "\n" : '').implode("\n", $notification_errors); |
||
| 1048 | } |
||
| 1049 | // New event, send data before updating so it's there |
||
| 1050 | $response = Api\Json\Response::get(); |
||
| 1051 | if($response && !$content['id'] && $event['id']) |
||
| 1052 | { |
||
| 1053 | $client_updated = $this->update_client($event['id']); |
||
| 1054 | } |
||
| 1055 | if (in_array($button,array('cancel','save','delete','delete_exceptions','delete_keep_exceptions')) && $noerror) |
||
| 1056 | { |
||
| 1057 | if ($content['lock_token']) // remove an existing lock |
||
| 1058 | { |
||
| 1059 | Vfs::unlock(Vfs::app_entry_lock_path('calendar',$content['id']),$content['lock_token'],false); |
||
| 1060 | } |
||
| 1061 | if ($content['no_popup']) |
||
| 1062 | { |
||
| 1063 | Egw::redirect_link('/index.php',array( |
||
| 1064 | 'menuaction' => 'calendar.calendar_uiviews.index', |
||
| 1065 | 'msg' => $msg, |
||
| 1066 | )); |
||
| 1067 | } |
||
| 1068 | if (in_array($button,array('delete_exceptions','delete_keep_exceptions')) || $content['recur_type'] && $button == 'delete') |
||
| 1069 | { |
||
| 1070 | Framework::refresh_opener($msg,'calendar'); |
||
| 1071 | } |
||
| 1072 | else |
||
| 1073 | { |
||
| 1074 | Framework::refresh_opener($msg, 'calendar', |
||
| 1075 | $event['id'] . ($content['edit_single'] ? ':' . (int)$content['edit_single'] : '' ), |
||
| 1076 | $button == 'save' && $client_updated ? ($content['id'] ? $update_type : 'add') : 'delete' |
||
| 1077 | ); |
||
| 1078 | } |
||
| 1079 | Framework::window_close(); |
||
| 1080 | exit(); |
||
| 1081 | } |
||
| 1082 | unset($event['no_notifications']); |
||
| 1083 | return $this->edit($event,$preserv,$msg,$event['id'] ? $event['id'] : $content['link_to']['to_id']); |
||
| 1084 | } |
||
| 1085 | |||
| 1086 | /** |
||
| 1087 | * Create an exception from the clicked event |
||
| 1088 | * |
||
| 1089 | * It's not stored to the DB unless the user saves it! |
||
| 1090 | * |
||
| 1091 | * @param array &$event |
||
| 1092 | * @param array &$preserv |
||
| 1093 | * @return string message that exception was created |
||
| 1094 | */ |
||
| 1095 | function _create_exception(&$event,&$preserv) |
||
| 1096 | { |
||
| 1097 | // In some cases where the user makes the first day an exception, actual_date may be missing |
||
| 1098 | $preserv['actual_date'] = $preserv['actual_date'] ? $preserv['actual_date'] : $event['start']; |
||
| 1099 | |||
| 1100 | $event['end'] += $preserv['actual_date'] - $event['start']; |
||
| 1101 | $event['reference'] = $preserv['reference'] = $event['id']; |
||
| 1102 | $event['recurrence'] = $preserv['recurrence'] = $preserv['actual_date']; |
||
| 1103 | $event['start'] = $preserv['edit_single'] = $preserv['actual_date']; |
||
| 1104 | $event['recur_type'] = MCAL_RECUR_NONE; |
||
| 1105 | foreach(array('recur_enddate','recur_interval','recur_exception','recur_data') as $name) |
||
| 1106 | { |
||
| 1107 | unset($event[$name]); |
||
| 1108 | } |
||
| 1109 | // add all alarms as new alarms to execption |
||
| 1110 | $event['alarm'] = array_values((array)$event['alarm']); |
||
| 1111 | foreach($event['alarm'] as &$alarm) |
||
| 1112 | { |
||
| 1113 | unset($alarm['uid'], $alarm['id'], $alarm['time']); |
||
| 1114 | } |
||
| 1115 | |||
| 1116 | // Copy links |
||
| 1117 | if(!is_array($event['link_to'])) $event['link_to'] = array(); |
||
| 1118 | $event['link_to']['to_app'] = 'calendar'; |
||
| 1119 | $event['link_to']['to_id'] = 0; |
||
| 1120 | |||
| 1121 | foreach(Link::get_links($event['link_to']['to_app'], $event['id']) as $link) |
||
| 1122 | { |
||
| 1123 | if(!$link['id']) continue; |
||
| 1124 | View Code Duplication | if ($link['app'] != Link::VFS_APPNAME) |
|
| 1125 | { |
||
| 1126 | Link::link('calendar', $event['link_to']['to_id'], $link['app'], $link['id'], $link['remark']); |
||
| 1127 | } |
||
| 1128 | elseif ($link['app'] == Link::VFS_APPNAME) |
||
| 1129 | { |
||
| 1130 | Link::link('calendar', $event['link_to']['to_id'], Link::VFS_APPNAME, array( |
||
| 1131 | 'tmp_name' => Link::vfs_path($link['app2'], $link['id2']).'/'.$link['id'], |
||
| 1132 | 'name' => $link['id'], |
||
| 1133 | ), $link['remark']); |
||
| 1134 | } |
||
| 1135 | } |
||
| 1136 | |||
| 1137 | $event['links'] = $event['link_to']; |
||
| 1138 | |||
| 1139 | if($this->bo->check_perms(Acl::EDIT,$event)) |
||
| 1140 | { |
||
| 1141 | return lang('Save event as exception - Delete single occurrence - Edit status or alarms for this particular day'); |
||
| 1142 | } |
||
| 1143 | return lang('Edit status or alarms for this particular day'); |
||
| 1144 | } |
||
| 1145 | |||
| 1146 | /** |
||
| 1147 | * Since we cannot change recurrences in the past, break a recurring |
||
| 1148 | * event (that starts in the past), and create a new event. |
||
| 1149 | * |
||
| 1150 | * $old_event will be ended (if needed) and $event will be modified with the |
||
| 1151 | * new start date and time. It is not allowed to edit events in the past, |
||
| 1152 | * so if $as_of_date is in the past, it will be adjusted to today. |
||
| 1153 | * |
||
| 1154 | * @param array &$event Event to be modified |
||
| 1155 | * @param array $old_event Unmodified (original) event, as read from the database |
||
| 1156 | * @param date $as_of_date If provided, the break will be done as of this |
||
| 1157 | * date instead of today |
||
| 1158 | * @param boolean $no_notifications Toggle notifications to participants |
||
| 1159 | * |
||
| 1160 | * @return false or error message |
||
| 1161 | */ |
||
| 1162 | function _break_recurring(&$event, $old_event, $as_of_date = null, $no_notifications = true) |
||
| 1163 | { |
||
| 1164 | $msg = false; |
||
| 1165 | |||
| 1166 | if(!$as_of_date ) |
||
| 1167 | { |
||
| 1168 | $as_of_date = time(); |
||
| 1169 | } |
||
| 1170 | |||
| 1171 | //error_log(__METHOD__ . Api\DateTime::to($old_event['start']) . ' -> '. Api\DateTime::to($event['start']) . ' as of ' . Api\DateTime::to($as_of_date)); |
||
| 1172 | |||
| 1173 | if(!($next_occurrence = $this->bo->read($event['id'], $this->bo->now_su + 1, true))) |
||
| 1174 | { |
||
| 1175 | $msg = lang("Error: You can't shift a series from the past!"); |
||
| 1176 | return $msg; |
||
| 1177 | } |
||
| 1178 | |||
| 1179 | // Hold on to this in case something goes wrong |
||
| 1180 | $orig_event = $event; |
||
| 1181 | |||
| 1182 | $offset = $event['start'] - $old_event['start']; |
||
| 1183 | $duration = $event['duration'] ? $event['duration'] : $event['end'] - $event['start']; |
||
| 1184 | |||
| 1185 | // base start-date of new series on actual / clicked date |
||
| 1186 | $event['start'] = $as_of_date ; |
||
| 1187 | |||
| 1188 | if (Api\DateTime::to($old_event['start'],'Ymd') < Api\DateTime::to($as_of_date,'Ymd') || |
||
| 1189 | // Adjust for requested date in the past |
||
| 1190 | Api\DateTime::to($as_of_date,'ts') < time() |
||
| 1191 | ) |
||
| 1192 | { |
||
| 1193 | |||
| 1194 | unset($orig_event); |
||
| 1195 | // copy event by unsetting the id(s) |
||
| 1196 | unset($event['id']); |
||
| 1197 | unset($event['uid']); |
||
| 1198 | unset($event['caldav_name']); |
||
| 1199 | $event['alarm'] = array(); |
||
| 1200 | |||
| 1201 | // set enddate of existing event |
||
| 1202 | $rriter = calendar_rrule::event2rrule($old_event, true); |
||
| 1203 | $rriter->rewind(); |
||
| 1204 | $last = $rriter->current(); |
||
| 1205 | do |
||
| 1206 | { |
||
| 1207 | $rriter->next_no_exception(); |
||
| 1208 | $occurrence = $rriter->current(); |
||
| 1209 | } |
||
| 1210 | while ($rriter->valid() && ( |
||
| 1211 | Api\DateTime::to($occurrence, 'ts') <= time() || |
||
| 1212 | Api\DateTime::to($occurrence, 'Ymd') < Api\DateTime::to($as_of_date,'Ymd') |
||
| 1213 | ) && ($last = $occurrence)); |
||
| 1214 | |||
| 1215 | |||
| 1216 | // Make sure as_of_date is still valid, may have to move forward |
||
| 1217 | if(Api\DateTime::to($as_of_date,'ts') < Api\DateTime::to($last,'ts') || |
||
| 1218 | Api\DateTime::to($as_of_date, 'Ymd') == Api\DateTime::to($last, 'Ymd')) |
||
| 1219 | { |
||
| 1220 | $event['start'] = Api\DateTime::to($rriter->current(),'ts') + $offset; |
||
| 1221 | } |
||
| 1222 | |||
| 1223 | //error_log(__METHOD__ ." Series should end at " . Api\DateTime::to($last) . " New series starts at " . Api\DateTime::to($event['start'])); |
||
| 1224 | if ($duration) |
||
| 1225 | { |
||
| 1226 | $event['end'] = $event['start'] + $duration; |
||
| 1227 | } |
||
| 1228 | elseif($event['end'] < $event['start']) |
||
| 1229 | { |
||
| 1230 | $event['end'] = $old_event['end'] - $old_event['start'] + $event['start']; |
||
| 1231 | } |
||
| 1232 | //error_log(__LINE__.": event[start]=$event[start]=".Api\DateTime::to($event['start']).", duration={$duration}, event[end]=$event[end]=".Api\DateTime::to($event['end']).", offset=$offset\n"); |
||
| 1233 | |||
| 1234 | $event['participants'] = $old_event['participants']; |
||
| 1235 | foreach ($old_event['recur_exception'] as $key => $exdate) |
||
| 1236 | { |
||
| 1237 | if ($exdate > Api\DateTime::to($last,'ts')) |
||
| 1238 | { |
||
| 1239 | //error_log("Moved exception on " . Api\DateTime::to($exdate)); |
||
| 1240 | unset($old_event['recur_exception'][$key]); |
||
| 1241 | $event['recur_exception'][$key] += $offset; |
||
| 1242 | } |
||
| 1243 | else |
||
| 1244 | { |
||
| 1245 | //error_log("Kept exception on ". Api\DateTime::to($exdate)); |
||
| 1246 | unset($event['recur_exception'][$key]); |
||
| 1247 | } |
||
| 1248 | } |
||
| 1249 | $last->setTime(0, 0, 0); |
||
| 1250 | $old_event['recur_enddate'] = Api\DateTime::to($last, 'ts'); |
||
| 1251 | if (!$this->bo->update($old_event,true,true,false,true,$dummy=null,$no_notifications)) |
||
| 1252 | { |
||
| 1253 | $msg .= ($msg ? ', ' : '') .lang('Error: the entry has been updated since you opened it for editing!').'<br />'. |
||
| 1254 | lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','<a href="'. |
||
| 1255 | htmlspecialchars(Egw::link('/index.php',array( |
||
| 1256 | 'menuaction' => 'calendar.calendar_uiforms.edit', |
||
| 1257 | 'cal_id' => $event['id'], |
||
| 1258 | ))).'">','</a>'); |
||
| 1259 | $event = $orig_event; |
||
| 1260 | } |
||
| 1261 | } |
||
| 1262 | $event['start'] = Api\DateTime::to($event['start'],'ts'); |
||
| 1263 | return $msg; |
||
| 1264 | } |
||
| 1265 | |||
| 1266 | /** |
||
| 1267 | * return javascript to open mail compose window with preset content to mail all participants |
||
| 1268 | * |
||
| 1269 | * @param array $event |
||
| 1270 | * @param boolean $added |
||
| 1271 | * @return string javascript window.open command |
||
| 1272 | */ |
||
| 1273 | function ajax_custom_mail($event,$added,$asrequest=false) |
||
| 1274 | { |
||
| 1275 | $to = array(); |
||
| 1276 | |||
| 1277 | foreach($event['participants'] as $uid => $status) |
||
| 1278 | { |
||
| 1279 | //error_log(__METHOD__.__LINE__.' '.$uid.':'.array2string($status)); |
||
| 1280 | if (empty($status)) continue; |
||
| 1281 | $toadd = ''; |
||
| 1282 | if ((isset($status['status']) && $status['status'] == 'R') || (isset($status['uid']) && $status['uid'] == $this->user)) continue; |
||
| 1283 | |||
| 1284 | if (isset($status['uid']) && is_numeric($status['uid']) && $GLOBALS['egw']->accounts->get_type($status['uid']) == 'u') |
||
| 1285 | { |
||
| 1286 | if (!($email = $GLOBALS['egw']->accounts->id2name($status['uid'],'account_email'))) continue; |
||
| 1287 | |||
| 1288 | $toadd = $GLOBALS['egw']->accounts->id2name($status['uid'], 'account_firstname').' '. |
||
| 1289 | $GLOBALS['egw']->accounts->id2name($status['uid'], 'account_lastname').' <'.$email.'>'; |
||
| 1290 | |||
| 1291 | if (!in_array($toadd,$to)) $to[] = $toadd; |
||
| 1292 | } |
||
| 1293 | elseif ($uid < 0) |
||
| 1294 | { |
||
| 1295 | foreach($GLOBALS['egw']->accounts->members($uid,true) as $uid) |
||
| 1296 | { |
||
| 1297 | if (!($email = $GLOBALS['egw']->accounts->id2name($uid,'account_email'))) continue; |
||
| 1298 | |||
| 1299 | $toadd = $GLOBALS['egw']->accounts->id2name($uid, 'account_firstname').' '. |
||
| 1300 | $GLOBALS['egw']->accounts->id2name($uid, 'account_lastname').' <'.$email.'>'; |
||
| 1301 | |||
| 1302 | // dont add groupmembers if they already rejected the event, or are the current user |
||
| 1303 | if (!in_array($toadd,$to) && ($event['participants'][$uid] !== 'R' && $uid != $this->user)) $to[] = $toadd; |
||
| 1304 | } |
||
| 1305 | } |
||
| 1306 | elseif(!empty($status['uid'])&& !is_numeric(substr($status['uid'],0,1)) && ($info = $this->bo->resource_info($status['uid']))) |
||
| 1307 | { |
||
| 1308 | $to[] = $info['email']; |
||
| 1309 | //error_log(__METHOD__.__LINE__.array2string($to)); |
||
| 1310 | } |
||
| 1311 | elseif(!is_numeric(substr($uid,0,1)) && ($info = $this->bo->resource_info($uid))) |
||
| 1312 | { |
||
| 1313 | $to[] = $info['email']; |
||
| 1314 | //error_log(__METHOD__.__LINE__.array2string($to)); |
||
| 1315 | } |
||
| 1316 | } |
||
| 1317 | // prefer event description over standard notification text |
||
| 1318 | if (empty($event['description'])) |
||
| 1319 | { |
||
| 1320 | list(,$body) = $this->bo->get_update_message($event,$added ? MSG_ADDED : MSG_MODIFIED); // update-message is in TZ of the user |
||
| 1321 | } |
||
| 1322 | else |
||
| 1323 | { |
||
| 1324 | $body = $event['description']; |
||
| 1325 | } |
||
| 1326 | // respect user preference about html mail |
||
| 1327 | View Code Duplication | if ($GLOBALS['egw_info']['user']['preferences']['mail']['composeOptions'] != 'text') |
|
| 1328 | { |
||
| 1329 | $body = '<pre>'.$body.'</pre>'; |
||
| 1330 | } |
||
| 1331 | //error_log(__METHOD__.print_r($event,true)); |
||
| 1332 | $boical = new calendar_ical(); |
||
| 1333 | // we need to pass $event[id] so iCal class reads event again, |
||
| 1334 | // as event is in user TZ, but iCal class expects server TZ! |
||
| 1335 | $ics = $boical->exportVCal(array($event['id']),'2.0','REQUEST',false); |
||
| 1336 | |||
| 1337 | $ics_file = tempnam($GLOBALS['egw_info']['server']['temp_dir'],'ics'); |
||
| 1338 | if(($f = fopen($ics_file,'w'))) |
||
| 1339 | { |
||
| 1340 | fwrite($f,$ics); |
||
| 1341 | fclose($f); |
||
| 1342 | } |
||
| 1343 | //error_log(__METHOD__.__LINE__.array2string($to)); |
||
| 1344 | $vars = array( |
||
| 1345 | 'menuaction' => 'mail.mail_compose.compose', |
||
| 1346 | 'mimeType' => $GLOBALS['egw_info']['user']['preferences']['mail']['composeOptions'] != 'text' ? 'html' : 'plain', |
||
| 1347 | 'preset[to]' => $to, |
||
| 1348 | 'preset[subject]' => $event['title'], |
||
| 1349 | 'preset[body]' => $body, |
||
| 1350 | 'preset[name]' => 'event.ics', |
||
| 1351 | 'preset[file]' => $ics_file, |
||
| 1352 | 'preset[type]' => 'text/calendar'.($asrequest?'; method=REQUEST':''), |
||
| 1353 | 'preset[size]' => filesize($ics_file), |
||
| 1354 | ); |
||
| 1355 | if ($asrequest) $vars['preset[msg]'] = lang('You attempt to mail a meetingrequest to the recipients above. Depending on the client this mail is opened with, the recipient may or may not see the mailbody below, but only see the meeting request attached.'); |
||
| 1356 | $response = Api\Json\Response::get(); |
||
| 1357 | $response->call('app.calendar.custom_mail', $vars); |
||
| 1358 | } |
||
| 1359 | |||
| 1360 | /** |
||
| 1361 | * Get title of a uid / calendar participant |
||
| 1362 | * |
||
| 1363 | * @param int|string $uid |
||
| 1364 | * @return string |
||
| 1365 | */ |
||
| 1366 | public function get_title($uid) |
||
| 1367 | { |
||
| 1368 | if (is_numeric($uid)) |
||
| 1369 | { |
||
| 1370 | return Api\Accounts::username($uid); |
||
| 1371 | } |
||
| 1372 | elseif (($info = $this->bo->resource_info($uid))) |
||
| 1373 | { |
||
| 1374 | if ($uid[0] == 'e' && $info['name'] && $info['name'] != $info['email']) |
||
| 1375 | { |
||
| 1376 | return $info['name'].' <'.$info['email'].'>'; |
||
| 1377 | } |
||
| 1378 | return $info['name'] ? $info['name'] : $info['email']; |
||
| 1379 | } |
||
| 1380 | return '#'.$uid; |
||
| 1381 | } |
||
| 1382 | |||
| 1383 | /** |
||
| 1384 | * Compare two uid by there title |
||
| 1385 | * |
||
| 1386 | * @param int|string $uid1 |
||
| 1387 | * @param int|string $uid2 |
||
| 1388 | * @return int see strnatcasecmp |
||
| 1389 | */ |
||
| 1390 | public function uid_title_cmp($uid1, $uid2) |
||
| 1391 | { |
||
| 1392 | return strnatcasecmp($this->get_title($uid1), $this->get_title($uid2)); |
||
| 1393 | } |
||
| 1394 | |||
| 1395 | /** |
||
| 1396 | * Edit a calendar event |
||
| 1397 | * |
||
| 1398 | * @param array $event Event to edit, if not $_GET['cal_id'] contains the event-id |
||
| 1399 | * @param array $preserv following keys: |
||
| 1400 | * view boolean view-mode, if no edit-access we automatic fallback to view-mode |
||
| 1401 | * hide_delete boolean hide delete button |
||
| 1402 | * no_popup boolean use a popup or not |
||
| 1403 | * edit_single int timestamp of single event edited, unset/null otherwise |
||
| 1404 | * @param string $msg ='' msg to display |
||
| 1405 | * @param mixed $link_to_id ='' from or for the link-widget |
||
| 1406 | * @param string $msg_type =null default automatic detect, if it contains "error" |
||
| 1407 | */ |
||
| 1408 | function edit($event=null,$preserv=null,$msg='',$link_to_id='',$msg_type=null) |
||
| 1409 | { |
||
| 1410 | $sel_options = array( |
||
| 1411 | 'recur_type' => &$this->bo->recur_types, |
||
| 1412 | 'status' => $this->bo->verbose_status, |
||
| 1413 | 'duration' => $this->durations, |
||
| 1414 | 'role' => $this->bo->roles, |
||
| 1415 | 'new_alarm[options]' => $this->bo->alarms + array(0 => lang('Custom')), |
||
| 1416 | 'action' => array( |
||
| 1417 | 'copy' => array('label' => 'Copy', 'title' => 'Copy this event'), |
||
| 1418 | 'ical' => array('label' => 'Export', 'title' => 'Download this event as iCal'), |
||
| 1419 | 'print' => array('label' => 'Print', 'title' => 'Print this event'), |
||
| 1420 | 'infolog' => array('label' => 'InfoLog', 'title' => 'Create an InfoLog from this event'), |
||
| 1421 | 'mail' => array('label' => 'Mail all participants', 'title' => 'Compose a mail to all participants after the event is saved'), |
||
| 1422 | 'sendrequest' => array('label' => 'Meetingrequest to all participants', 'title' => 'Send meetingrequest to all participants after the event is saved'), |
||
| 1423 | ), |
||
| 1424 | ); |
||
| 1425 | unset($sel_options['status']['G']); |
||
| 1426 | if (!is_array($event)) |
||
| 1427 | { |
||
| 1428 | $preserv = array( |
||
| 1429 | 'no_popup' => isset($_GET['no_popup']), |
||
| 1430 | 'template' => isset($_GET['template']) ? $_GET['template'] : (isset($_REQUEST['print']) ? 'calendar.print' : 'calendar.edit'), |
||
| 1431 | ); |
||
| 1432 | $cal_id = (int) $_GET['cal_id']; |
||
| 1433 | if($_GET['action']) |
||
| 1434 | { |
||
| 1435 | $event = $this->bo->read($cal_id); |
||
| 1436 | $event['action'] = $_GET['action']; |
||
| 1437 | unset($event['participants']); |
||
| 1438 | return $this->process_edit($event); |
||
| 1439 | } |
||
| 1440 | // vfs url |
||
| 1441 | if (!empty($_GET['ical_url']) && parse_url($_GET['ical_url'], PHP_URL_SCHEME) == 'vfs') |
||
| 1442 | { |
||
| 1443 | $_GET['ical_vfs'] = parse_url($_GET['ical_url'], PHP_URL_PATH); |
||
| 1444 | } |
||
| 1445 | // vfs path |
||
| 1446 | if (!empty($_GET['ical_vfs']) && |
||
| 1447 | (!Vfs::file_exists($_GET['ical_vfs']) || !($_GET['ical'] = file_get_contents(Vfs::PREFIX.$_GET['ical_vfs'])))) |
||
| 1448 | { |
||
| 1449 | //error_log(__METHOD__."() Error: importing the iCal: vfs file not found '$_GET[ical_vfs]'!"); |
||
| 1450 | $msg = lang('Error: importing the iCal').': '.lang('VFS file not found').': '.$_GET['ical_vfs']; |
||
| 1451 | $event =& $this->default_add_event(); |
||
| 1452 | } |
||
| 1453 | if (!empty($_GET['ical_data']) && |
||
| 1454 | !($_GET['ical'] = Link::get_data($_GET['ical_data']))) |
||
| 1455 | { |
||
| 1456 | //error_log(__METHOD__."() Error: importing the iCal: data not found '$_GET[ical_data]'!"); |
||
| 1457 | $msg = lang('Error: importing the iCal').': '.lang('Data not found').': '.$_GET['ical_data']; |
||
| 1458 | $event =& $this->default_add_event(); |
||
| 1459 | } |
||
| 1460 | if (!empty($_GET['ical'])) |
||
| 1461 | { |
||
| 1462 | $ical = new calendar_ical(); |
||
| 1463 | if (!($events = $ical->icaltoegw($_GET['ical'], '', 'utf-8'))) |
||
| 1464 | { |
||
| 1465 | error_log(__METHOD__."('$_GET[ical]') error parsing iCal!"); |
||
| 1466 | $msg = lang('Error: importing the iCal'); |
||
| 1467 | $event =& $this->default_add_event(); |
||
| 1468 | } |
||
| 1469 | else |
||
| 1470 | { |
||
| 1471 | if (count($events) > 1) |
||
| 1472 | { |
||
| 1473 | $msg = lang('%1 events in iCal file, only first one imported and displayed!', count($events)); |
||
| 1474 | $msg_type = 'notice'; // no not hide automatic |
||
| 1475 | } |
||
| 1476 | // as icaltoegw returns timestamps in server-time, we have to convert them here to user-time |
||
| 1477 | $this->bo->db2data($events, 'ts'); |
||
| 1478 | |||
| 1479 | $event = array_shift($events); |
||
| 1480 | if (($existing_event = $this->bo->read($event['uid']))) |
||
| 1481 | { |
||
| 1482 | $event = $existing_event; |
||
| 1483 | } |
||
| 1484 | else |
||
| 1485 | { |
||
| 1486 | $event['participant_types'] = array(); |
||
| 1487 | foreach($event['participants'] as $uid => $status) |
||
| 1488 | { |
||
| 1489 | $user_type = $user_id = null; |
||
| 1490 | calendar_so::split_user($uid, $user_type, $user_id); |
||
| 1491 | $event['participant_types'][$user_type][$user_id] = $status; |
||
| 1492 | } |
||
| 1493 | } |
||
| 1494 | //error_log(__METHOD__."(...) parsed as ".array2string($event)); |
||
| 1495 | } |
||
| 1496 | unset($ical); |
||
| 1497 | } |
||
| 1498 | elseif (!$cal_id || $cal_id && !($event = $this->bo->read($cal_id))) |
||
| 1499 | { |
||
| 1500 | if ($cal_id) |
||
| 1501 | { |
||
| 1502 | if (!$preserv['no_popup']) |
||
| 1503 | { |
||
| 1504 | Framework::window_close(lang('Permission denied')); |
||
| 1505 | } |
||
| 1506 | else |
||
| 1507 | { |
||
| 1508 | $GLOBALS['egw']->framework->render('<p class="message" align="center">'.lang('Permission denied')."</p>\n",null,true); |
||
| 1509 | exit(); |
||
| 1510 | } |
||
| 1511 | } |
||
| 1512 | $event =& $this->default_add_event(); |
||
| 1513 | } |
||
| 1514 | else |
||
| 1515 | { |
||
| 1516 | $preserv['actual_date'] = $event['start']; // remember the date clicked |
||
| 1517 | if ($event['recur_type'] != MCAL_RECUR_NONE) |
||
| 1518 | { |
||
| 1519 | View Code Duplication | if (empty($event['whole_day'])) |
|
| 1520 | { |
||
| 1521 | $date = $_GET['date']; |
||
| 1522 | } |
||
| 1523 | else |
||
| 1524 | { |
||
| 1525 | $date = $this->bo->so->startOfDay(new Api\DateTime($_GET['date'], Api\DateTime::$user_timezone)); |
||
| 1526 | $date->setUser(); |
||
| 1527 | } |
||
| 1528 | $event = $this->bo->read($cal_id, $date, true); |
||
| 1529 | $preserv['actual_date'] = $event['start']; // remember the date clicked |
||
| 1530 | if ($_GET['exception']) |
||
| 1531 | { |
||
| 1532 | $msg = $this->_create_exception($event,$preserv); |
||
| 1533 | } |
||
| 1534 | else |
||
| 1535 | { |
||
| 1536 | $event = $this->bo->read($cal_id, null, true); |
||
| 1537 | } |
||
| 1538 | } |
||
| 1539 | } |
||
| 1540 | // set new start and end if given by $_GET |
||
| 1541 | if(isset($_GET['start'])) { $event['start'] = Api\DateTime::to($_GET['start'],'ts'); } |
||
| 1542 | if(isset($_GET['end'])) { $event['end'] = Api\DateTime::to($_GET['end'],'ts'); } |
||
| 1543 | if(isset($_GET['non_blocking'])) { $event['non_blocking'] = (bool)$_GET['non_blocking']; } |
||
| 1544 | // check if the event is the whole day |
||
| 1545 | $start = $this->bo->date2array($event['start']); |
||
| 1546 | $end = $this->bo->date2array($event['end']); |
||
| 1547 | $event['whole_day'] = !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59; |
||
| 1548 | |||
| 1549 | $link_to_id = $event['id']; |
||
| 1550 | if (!$event['id'] && isset($_REQUEST['link_app']) && isset($_REQUEST['link_id'])) |
||
| 1551 | { |
||
| 1552 | $link_ids = is_array($_REQUEST['link_id']) ? $_REQUEST['link_id'] : array($_REQUEST['link_id']); |
||
| 1553 | foreach(is_array($_REQUEST['link_app']) ? $_REQUEST['link_app'] : array($_REQUEST['link_app']) as $n => $link_app) |
||
| 1554 | { |
||
| 1555 | $link_id = $link_ids[$n]; |
||
| 1556 | if(!preg_match('/^[a-z_0-9-]+:[:a-z_0-9-]+$/i',$link_app.':'.$link_id)) // guard against XSS |
||
| 1557 | { |
||
| 1558 | continue; |
||
| 1559 | } |
||
| 1560 | if(!$n) |
||
| 1561 | { |
||
| 1562 | $event['title'] = Link::title($link_app,$link_id); |
||
| 1563 | // ask first linked app via "calendar_set" hook, for further data to set, incl. links |
||
| 1564 | View Code Duplication | if (($set = Api\Hooks::single($event+array('location'=>'calendar_set','entry_id'=>$link_id),$link_app))) |
|
| 1565 | { |
||
| 1566 | foreach((array)$set['link_app'] as $i => $l_app) |
||
| 1567 | { |
||
| 1568 | if (($l_id=$set['link_id'][$i])) Link::link('calendar',$event['link_to']['to_id'],$l_app,$l_id); |
||
| 1569 | } |
||
| 1570 | unset($set['link_app']); |
||
| 1571 | unset($set['link_id']); |
||
| 1572 | |||
| 1573 | $event = array_merge($event,$set); |
||
| 1574 | } |
||
| 1575 | } |
||
| 1576 | Link::link('calendar',$link_to_id,$link_app,$link_id); |
||
| 1577 | } |
||
| 1578 | } |
||
| 1579 | } |
||
| 1580 | |||
| 1581 | $etpl = new Etemplate(); |
||
| 1582 | if (!$etpl->read($preserv['template'])) |
||
| 1583 | { |
||
| 1584 | $etpl->read($preserv['template'] = 'calendar.edit'); |
||
| 1585 | } |
||
| 1586 | $view = $preserv['view'] = $preserv['view'] || $event['id'] && !$this->bo->check_perms(Acl::EDIT,$event); |
||
| 1587 | //echo "view=$view, event="; _debug_array($event); |
||
| 1588 | // shared locking of entries to edit |
||
| 1589 | if (!$view && ($locktime = $GLOBALS['egw_info']['server']['Lock_Time_Calender']) && $event['id']) |
||
| 1590 | { |
||
| 1591 | $lock_path = Vfs::app_entry_lock_path('calendar',$event['id']); |
||
| 1592 | $lock_owner = 'mailto:'.$GLOBALS['egw_info']['user']['account_email']; |
||
| 1593 | |||
| 1594 | if (($preserv['lock_token'] = $event['lock_token'])) // already locked --> refresh the lock |
||
| 1595 | { |
||
| 1596 | Vfs::lock($lock_path,$preserv['lock_token'],$locktime,$lock_owner,$scope='shared',$type='write',true,false); |
||
| 1597 | } |
||
| 1598 | if (($lock = Vfs::checkLock($lock_path)) && $lock['owner'] != $lock_owner) |
||
| 1599 | { |
||
| 1600 | $msg .= ' '.lang('This entry is currently opened by %1!', |
||
| 1601 | (($lock_uid = $GLOBALS['egw']->accounts->name2id(substr($lock['owner'],7),'account_email')) ? |
||
| 1602 | Api\Accounts::username($lock_uid) : $lock['owner'])); |
||
| 1603 | } |
||
| 1604 | elseif($lock) |
||
| 1605 | { |
||
| 1606 | $preserv['lock_token'] = $lock['token']; |
||
| 1607 | } |
||
| 1608 | elseif(Vfs::lock($lock_path,$preserv['lock_token'],$locktime,$lock_owner,$scope='shared',$type='write',false,false)) |
||
| 1609 | { |
||
| 1610 | //We handle AJAX_REQUEST in client-side for unlocking the locked entry, in case of closing the entry by X button or close button |
||
| 1611 | } |
||
| 1612 | else |
||
| 1613 | { |
||
| 1614 | $msg .= ' '.lang("Can't aquire lock!"); // eg. an exclusive lock via CalDAV ... |
||
| 1615 | $view = true; |
||
| 1616 | } |
||
| 1617 | } |
||
| 1618 | $content = array_merge($event,array( |
||
| 1619 | 'link_to' => array( |
||
| 1620 | 'to_id' => $link_to_id, |
||
| 1621 | 'to_app' => 'calendar', |
||
| 1622 | ), |
||
| 1623 | 'edit_single' => $preserv['edit_single'], // need to be in content too, as it is used in the template |
||
| 1624 | 'tabs' => $preserv['tabs'], |
||
| 1625 | 'view' => $view, |
||
| 1626 | 'query_delete_exceptions' => (int)($event['recur_type'] && $event['recur_exception']), |
||
| 1627 | )); |
||
| 1628 | Framework::message($msg, $msg_type); |
||
| 1629 | $content['duration'] = $content['end'] - $content['start']; |
||
| 1630 | if (isset($this->durations[$content['duration']])) $content['end'] = ''; |
||
| 1631 | |||
| 1632 | $row = 3; |
||
| 1633 | $readonlys = $content['participants'] = $preserv['participants'] = array(); |
||
| 1634 | // preserve some ui elements, if set eg. under error-conditions |
||
| 1635 | foreach(array('quantity','resource','role') as $n) |
||
| 1636 | { |
||
| 1637 | if (isset($event['participants'][$n])) $content['participants'][$n] = $event['participants'][$n]; |
||
| 1638 | } |
||
| 1639 | foreach($event['participant_types'] as $type => $participants) |
||
| 1640 | { |
||
| 1641 | $name = 'accounts'; |
||
| 1642 | if (isset($this->bo->resources[$type])) |
||
| 1643 | { |
||
| 1644 | $name = $this->bo->resources[$type]['app']; |
||
| 1645 | } |
||
| 1646 | // sort participants (in there group/app) by title |
||
| 1647 | uksort($participants, array($this, 'uid_title_cmp')); |
||
| 1648 | foreach($participants as $id => $status) |
||
| 1649 | { |
||
| 1650 | $uid = $type == 'u' ? $id : $type.$id; |
||
| 1651 | $quantity = $role = null; |
||
| 1652 | calendar_so::split_status($status,$quantity,$role); |
||
| 1653 | $preserv['participants'][$row] = $content['participants'][$row] = array( |
||
| 1654 | 'app' => $name == 'accounts' ? ($GLOBALS['egw']->accounts->get_type($id) == 'g' ? 'Group' : 'User') : $name, |
||
| 1655 | 'uid' => $uid, |
||
| 1656 | 'status' => $status, |
||
| 1657 | 'old_status' => $status, |
||
| 1658 | 'quantity' => $quantity > 1 || $uid[0] == 'r' ? $quantity : '', // only display quantity for resources or if > 1 |
||
| 1659 | 'role' => $role, |
||
| 1660 | ); |
||
| 1661 | // replace iCal roles with a nicer label and remove regular REQ-PARTICIPANT |
||
| 1662 | if (isset($this->bo->roles[$role])) |
||
| 1663 | { |
||
| 1664 | $content['participants'][$row]['role_label'] = lang($this->bo->roles[$role]); |
||
| 1665 | } |
||
| 1666 | // allow third party apps to use categories for roles |
||
| 1667 | elseif(substr($role,0,6) == 'X-CAT-') |
||
| 1668 | { |
||
| 1669 | $content['participants'][$row]['role_label'] = $GLOBALS['egw']->categories->id2name(substr($role,6)); |
||
| 1670 | } |
||
| 1671 | else |
||
| 1672 | { |
||
| 1673 | $content['participants'][$row]['role_label'] = lang(str_replace('X-','',$role)); |
||
| 1674 | } |
||
| 1675 | $content['participants'][$row]['delete_id'] = strpbrk($uid,'"\'<>') !== false ? md5($uid) : $uid; |
||
| 1676 | //echo "<p>$uid ($quantity): $role --> {$content['participants'][$row]['role']}</p>\n"; |
||
| 1677 | |||
| 1678 | if (($no_status = !$this->bo->check_status_perms($uid,$event)) || $view) |
||
| 1679 | $readonlys['participants'][$row]['status'] = $no_status; |
||
| 1680 | View Code Duplication | if ($preserv['hide_delete'] || !$this->bo->check_perms(Acl::EDIT,$event)) |
|
| 1681 | $readonlys['participants']['delete'][$uid] = true; |
||
| 1682 | // todo: make the participants available as links with email as title |
||
| 1683 | $content['participants'][$row++]['title'] = $this->get_title($uid); |
||
| 1684 | // enumerate group-invitations, so people can accept/reject them |
||
| 1685 | if ($name == 'accounts' && $GLOBALS['egw']->accounts->get_type($id) == 'g' && |
||
| 1686 | ($members = $GLOBALS['egw']->accounts->members($id,true))) |
||
| 1687 | { |
||
| 1688 | $sel_options['status']['G'] = lang('Select one'); |
||
| 1689 | // sort members by title |
||
| 1690 | usort($members, array($this, 'uid_title_cmp')); |
||
| 1691 | foreach($members as $member) |
||
| 1692 | { |
||
| 1693 | if (!isset($participants[$member]) && $this->bo->check_perms(Acl::READ,0,$member)) |
||
| 1694 | { |
||
| 1695 | $preserv['participants'][$row] = $content['participants'][$row] = array( |
||
| 1696 | 'app' => 'Group invitation', |
||
| 1697 | 'uid' => $member, |
||
| 1698 | 'status' => 'G', |
||
| 1699 | ); |
||
| 1700 | $readonlys['participants'][$row]['quantity'] = $readonlys['participants']['delete'][$member] = true; |
||
| 1701 | // read access is enough to invite participants, but you need edit rights to change status |
||
| 1702 | $readonlys['participants'][$row]['status'] = !$this->bo->check_perms(Acl::EDIT,0,$member); |
||
| 1703 | $content['participants'][$row++]['title'] = Api\Accounts::username($member); |
||
| 1704 | } |
||
| 1705 | } |
||
| 1706 | } |
||
| 1707 | } |
||
| 1708 | // resouces / apps we shedule, atm. resources and addressbook |
||
| 1709 | $content['participants']['cal_resources'] = ''; |
||
| 1710 | foreach($this->bo->resources as $data) |
||
| 1711 | { |
||
| 1712 | if ($data['app'] == 'email') continue; // make no sense, as we cant search for email |
||
| 1713 | $content['participants']['cal_resources'] .= ','.$data['app']; |
||
| 1714 | } |
||
| 1715 | } |
||
| 1716 | $content['participants']['status_date'] = $preserv['actual_date']; |
||
| 1717 | $preserved = array_merge($preserv,$content); |
||
| 1718 | $event['new_alarm']['options'] = $content['new_alarm']['options']; |
||
| 1719 | if ($event['alarm']) |
||
| 1720 | { |
||
| 1721 | // makes keys of the alarm-array starting with 1 |
||
| 1722 | $content['alarm'] = array(false); |
||
| 1723 | foreach(array_values($event['alarm']) as $id => $alarm) |
||
| 1724 | { |
||
| 1725 | if (!$alarm['all'] && !$this->bo->check_perms(Acl::READ,0,$alarm['owner'])) |
||
| 1726 | { |
||
| 1727 | continue; // no read rights to the calendar of the alarm-owner, dont show the alarm |
||
| 1728 | } |
||
| 1729 | $alarm['all'] = (int) $alarm['all']; |
||
| 1730 | $after = false; |
||
| 1731 | if($alarm['offset'] < 0) |
||
| 1732 | { |
||
| 1733 | $after = true; |
||
| 1734 | $alarm['offset'] = -1 * $alarm['offset']; |
||
| 1735 | } |
||
| 1736 | $days = (int) ($alarm['offset'] / DAY_s); |
||
| 1737 | $hours = (int) (($alarm['offset'] % DAY_s) / HOUR_s); |
||
| 1738 | $minutes = (int) (($alarm['offset'] % HOUR_s) / 60); |
||
| 1739 | $label = array(); |
||
| 1740 | if ($days) $label[] = $days.' '.lang('days'); |
||
| 1741 | if ($hours) $label[] = $hours.' '.lang('hours'); |
||
| 1742 | if ($minutes) $label[] = $minutes.' '.lang('Minutes'); |
||
| 1743 | $alarm['offset'] = implode(', ',$label) . ' ' . ($after ? lang('after') : lang('before')); |
||
| 1744 | $content['alarm'][] = $alarm; |
||
| 1745 | |||
| 1746 | $readonlys['alarm[delete_alarm]['.$alarm['id'].']'] = !$this->bo->check_perms(Acl::EDIT,$alarm['all'] ? $event : 0,$alarm['owner']); |
||
| 1747 | } |
||
| 1748 | if (count($content['alarm']) == 1) |
||
| 1749 | { |
||
| 1750 | $content['alarm'] = false; // no alarms added to content array |
||
| 1751 | } |
||
| 1752 | } |
||
| 1753 | else |
||
| 1754 | { |
||
| 1755 | $content['alarm'] = false; |
||
| 1756 | } |
||
| 1757 | $content['msg'] = $msg; |
||
| 1758 | |||
| 1759 | if ($view) |
||
| 1760 | { |
||
| 1761 | $readonlys['__ALL__'] = true; // making everything readonly, but widgets set explicitly to false |
||
| 1762 | $readonlys['button[cancel]'] = $readonlys['action'] = |
||
| 1763 | $readonlys['before_after'] = $readonlys['button[add_alarm]'] = $readonlys['new_alarm[owner]'] = |
||
| 1764 | $readonlys['new_alarm[options]'] = $readonlys['new_alarm[date]'] = false; |
||
| 1765 | |||
| 1766 | $content['participants']['no_add'] = true; |
||
| 1767 | |||
| 1768 | if(!$event['whole_day']) |
||
| 1769 | { |
||
| 1770 | $etpl->setElementAttribute('whole_day', 'disabled', true); |
||
| 1771 | } |
||
| 1772 | |||
| 1773 | // respect category permissions |
||
| 1774 | View Code Duplication | if(!empty($event['category'])) |
|
| 1775 | { |
||
| 1776 | $content['category'] = $this->categories->check_list(Acl::READ, $event['category']); |
||
| 1777 | } |
||
| 1778 | } |
||
| 1779 | else |
||
| 1780 | { |
||
| 1781 | $readonlys['recur_exception'] = true; |
||
| 1782 | |||
| 1783 | if ($event['recur_type'] != MCAL_RECUR_NONE) |
||
| 1784 | { |
||
| 1785 | $readonlys['recur_exception'] = !count($content['recur_exception']); // otherwise we get a delete button |
||
| 1786 | //$onclick =& $etpl->get_cell_attribute('button[delete]','onclick'); |
||
| 1787 | //$onclick = str_replace('Delete this event','Delete this series of recuring events',$onclick); |
||
| 1788 | } |
||
| 1789 | elseif ($event['reference'] != 0) |
||
| 1790 | { |
||
| 1791 | $readonlys['recur_type'] = $readonlys['recur_enddate'] = true; |
||
| 1792 | $readonlys['recur_interval'] = $readonlys['recur_data'] = true; |
||
| 1793 | } |
||
| 1794 | } |
||
| 1795 | View Code Duplication | if($content['category'] && !is_array($content['category'])) |
|
| 1796 | { |
||
| 1797 | $content['category'] = explode(',',$event['category']); |
||
| 1798 | } |
||
| 1799 | // disabling the custom fields tab, if there are none |
||
| 1800 | $readonlys['tabs'] = array( |
||
| 1801 | 'custom' => !count($this->bo->customfields), |
||
| 1802 | 'participants' => $this->accountsel->account_selection == 'none', |
||
| 1803 | 'history' => !$event['id'], |
||
| 1804 | ); |
||
| 1805 | if (!isset($GLOBALS['egw_info']['user']['apps']['mail'])) // no mail without mail-app |
||
| 1806 | { |
||
| 1807 | unset($sel_options['action']['mail']); |
||
| 1808 | unset($sel_options['action']['sendmeetingrequest']); |
||
| 1809 | } |
||
| 1810 | if (!$event['id']) // no ical export for new (not saved) events |
||
| 1811 | { |
||
| 1812 | $readonlys['action'] = true; |
||
| 1813 | } |
||
| 1814 | if (!($readonlys['button[exception]'] = !$this->bo->check_perms(Acl::EDIT,$event) || $event['recur_type'] == MCAL_RECUR_NONE || ($event['recur_enddate'] &&$event['start'] > $event['recur_enddate']))) |
||
| 1815 | { |
||
| 1816 | $content['exception_label'] = $this->bo->long_date(max($preserved['actual_date'], $event['start'])); |
||
| 1817 | } |
||
| 1818 | $readonlys['button[delete]'] = !$event['id'] || $preserved['hide_delete'] || !$this->bo->check_perms(Acl::DELETE,$event); |
||
| 1819 | |||
| 1820 | View Code Duplication | if (!$event['id'] || $this->bo->check_perms(Acl::EDIT,$event)) // new event or edit rights to the event ==> allow to add alarm for all users |
|
| 1821 | { |
||
| 1822 | $sel_options['owner'][0] = lang('All participants'); |
||
| 1823 | } |
||
| 1824 | if (isset($event['participant_types']['u'][$this->user])) |
||
| 1825 | { |
||
| 1826 | $sel_options['owner'][$this->user] = $this->bo->participant_name($this->user); |
||
| 1827 | } |
||
| 1828 | foreach((array) $event['participant_types']['u'] as $uid => $status) |
||
| 1829 | { |
||
| 1830 | if ($uid != $this->user && $status != 'R' && $this->bo->check_perms(Acl::EDIT,0,$uid)) |
||
| 1831 | { |
||
| 1832 | $sel_options['owner'][$uid] = $this->bo->participant_name($uid); |
||
| 1833 | } |
||
| 1834 | } |
||
| 1835 | $content['no_add_alarm'] = !count($sel_options['owner']); // no rights to set any alarm |
||
| 1836 | if (!$event['id']) |
||
| 1837 | { |
||
| 1838 | $etpl->set_cell_attribute('button[new_alarm]','type','checkbox'); |
||
| 1839 | } |
||
| 1840 | if ($preserved['no_popup']) |
||
| 1841 | { |
||
| 1842 | $etpl->set_cell_attribute('button[cancel]','onclick',''); |
||
| 1843 | } |
||
| 1844 | |||
| 1845 | // Allow admins to restore deleted events |
||
| 1846 | if($GLOBALS['egw_info']['server']['calendar_delete_history'] && $event['deleted'] ) |
||
| 1847 | { |
||
| 1848 | $content['deleted'] = $preserved['deleted'] = null; |
||
| 1849 | $etpl->set_cell_attribute('button[save]', 'label', 'Recover'); |
||
| 1850 | $etpl->set_cell_attribute('button[apply]', 'disabled', true); |
||
| 1851 | } |
||
| 1852 | // Allow users to prevent notifications? |
||
| 1853 | $etpl->set_cell_attribute('no_notifications', 'disabled', !$GLOBALS['egw_info']['server']['calendar_allow_no_notification']); |
||
| 1854 | |||
| 1855 | // Setup history tab |
||
| 1856 | $this->setup_history($content, $sel_options); |
||
| 1857 | |||
| 1858 | $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' |
||
| 1859 | . (!$event['id'] ? lang('Add') |
||
| 1860 | : ($view ? ($content['edit_single'] ? lang('View exception') : ($content['recur_type'] ? lang('View series') : lang('View'))) |
||
| 1861 | : ($content['edit_single'] ? lang('Create exception') : ($content['recur_type'] ? lang('Edit series') : lang('Edit'))))); |
||
| 1862 | |||
| 1863 | $content['cancel_needs_refresh'] = (bool)$_GET['cancel_needs_refresh']; |
||
| 1864 | |||
| 1865 | if (!empty($preserved['lock_token'])) $content['lock_token'] = $preserved['lock_token']; |
||
| 1866 | |||
| 1867 | // non_interactive==true from $_GET calls immediate save action without displaying the edit form |
||
| 1868 | if(isset($_GET['non_interactive']) && (bool)$_GET['non_interactive'] === true) |
||
| 1869 | { |
||
| 1870 | unset($_GET['non_interactive']); // prevent process_exec <--> edit loops |
||
| 1871 | $content['button']['save'] = true; |
||
| 1872 | $this->process_edit(array_merge($content,$preserved)); |
||
| 1873 | } |
||
| 1874 | else |
||
| 1875 | { |
||
| 1876 | $etpl->exec('calendar.calendar_uiforms.process_edit',$content,$sel_options,$readonlys,$preserved,$preserved['no_popup'] ? 0 : 2); |
||
| 1877 | } |
||
| 1878 | } |
||
| 1879 | |||
| 1880 | /** |
||
| 1881 | * Remove (shared) lock via ajax, when edit popup get's closed |
||
| 1882 | * |
||
| 1883 | * @param int $id |
||
| 1884 | * @param string $token |
||
| 1885 | */ |
||
| 1886 | function ajax_unlock($id,$token) |
||
| 1896 | |||
| 1897 | /** |
||
| 1898 | * Display for FMail an iCal meeting request and allow to accept, tentative or reject it or a reply and allow to apply it |
||
| 1899 | * |
||
| 1900 | * @todo Handle situation when user is NOT invited, but eg. can view that mail ... |
||
| 1901 | * @param array $event = null; special usage if $event is array('event'=>null,'msg'=>'','useSession'=>true) we |
||
| 1902 | * are called by new mail-app; and we intend to use the stuff passed on by session |
||
| 1903 | * @param string $msg = null |
||
| 1904 | */ |
||
| 1905 | function meeting(array $event=null, $msg=null) |
||
| 2108 | |||
| 2109 | /** |
||
| 2110 | * displays a scheduling conflict |
||
| 2111 | * |
||
| 2112 | * @param array $event |
||
| 2113 | * @param array $conflicts array with conflicting events, the events are not garantied to be readable by the user! |
||
| 2114 | * @param array $preserv data to preserv |
||
| 2115 | */ |
||
| 2116 | function conflicts($event,$conflicts,$preserv) |
||
| 2168 | |||
| 2169 | /** |
||
| 2170 | * Callback for freetimesearch button in edit |
||
| 2171 | * |
||
| 2172 | * It stores the data of the submitted form in the session under 'freetimesearch_args_'.$edit_content['id'], |
||
| 2173 | * for later retrival of the freetimesearch method, called by the returned window.open() command. |
||
| 2174 | * |
||
| 2175 | * @param array $edit_content |
||
| 2176 | * @return string with xajaxResponse |
||
| 2177 | */ |
||
| 2178 | function ajax_freetimesearch(array $edit_content) |
||
| 2248 | |||
| 2249 | /** |
||
| 2250 | * Freetime search |
||
| 2251 | * |
||
| 2252 | * As the function is called in a popup via javascript, parametes get initialy transfered via the url |
||
| 2253 | * @param array $content=null array with parameters or false (default) to use the get-params |
||
| 2254 | * @param string start[str] start-date |
||
| 2255 | * @param string start[hour] start-hour |
||
| 2256 | * @param string start[min] start-minutes |
||
| 2257 | * @param string end[str] end-date |
||
| 2258 | * @param string end[hour] end-hour |
||
| 2259 | * @param string end[min] end-minutes |
||
| 2260 | * @param string participants ':' delimited string of user-id's |
||
| 2261 | */ |
||
| 2262 | function freetimesearch($content = null) |
||
| 2326 | |||
| 2327 | /** |
||
| 2328 | * calculate the freetime of given $participants in a certain time-span |
||
| 2329 | * |
||
| 2330 | * @param array $participants user-id's |
||
| 2331 | * @param int $start start-time timestamp in user-time |
||
| 2332 | * @param int $end end-time timestamp in user-time |
||
| 2333 | * @param int $duration min. duration in sec, default 1 |
||
| 2334 | * @param int $cal_id own id for existing events, to exclude them from being busy-time, default 0 |
||
| 2335 | * @return array of free time-slots: array with start and end values |
||
| 2336 | */ |
||
| 2337 | function freetime($participants,$start,$end,$duration=1,$cal_id=0) |
||
| 2413 | |||
| 2414 | /** |
||
| 2415 | * split the freetime in daywise slot, taking into account weekdays, start- and stop-times |
||
| 2416 | * |
||
| 2417 | * If the duration is bigger then the difference of start- and end_time, the end_time is ignored |
||
| 2418 | * |
||
| 2419 | * @param array $freetime free time-slots: array with start and end values |
||
| 2420 | * @param int $duration min. duration in sec |
||
| 2421 | * @param int $weekdays allowed weekdays, bitfield of MCAL_M_... |
||
| 2422 | * @param int $_start_time minimum start-hour 0-23 |
||
| 2423 | * @param int $_end_time maximum end-hour 0-23, or 0 for none |
||
| 2424 | * @param array $sel_options on return options for start-time selectbox |
||
| 2425 | * @return array of free time-slots: array with start and end values |
||
| 2426 | */ |
||
| 2427 | function split_freetime_daywise($freetime, $duration, $weekdays, $_start_time, $_end_time, &$sel_options) |
||
| 2493 | |||
| 2494 | /** |
||
| 2495 | * Export events as vCalendar version 2.0 files (iCal) |
||
| 2496 | * |
||
| 2497 | * @param int|array $content numeric cal_id or submitted content from etempalte::exec |
||
| 2498 | * @param boolean $return_error should an error-msg be returned or a regular page with it generated (default) |
||
| 2499 | * @return string error-msg if $return_error |
||
| 2500 | */ |
||
| 2501 | function export($content=0,$return_error=false) |
||
| 2557 | |||
| 2558 | /** |
||
| 2559 | * Edit category ACL (admin only) |
||
| 2560 | * |
||
| 2561 | * @param array $_content |
||
| 2562 | */ |
||
| 2563 | function cat_acl(array $_content=null) |
||
| 2626 | |||
| 2627 | /** |
||
| 2628 | * Set up the required fields to get the history tab |
||
| 2629 | */ |
||
| 2630 | public function setup_history(&$content, &$sel_options) |
||
| 2691 | |||
| 2692 | /** |
||
| 2693 | * moves an event to another date/time |
||
| 2694 | * |
||
| 2695 | * @param string $_eventId id of the event which has to be moved |
||
| 2696 | * @param string $calendarOwner the owner of the calendar the event is in |
||
| 2697 | * @param string $targetDateTime the datetime where the event should be moved to, format: YYYYMMDD |
||
| 2698 | * @param string|string[] $targetOwner the owner of the target calendar |
||
| 2699 | * @param string $durationT the duration to support resizable calendar event |
||
| 2700 | * @param string $seriesInstance If moving a whole series, not an exception, this is |
||
| 2701 | * which particular instance was dragged |
||
| 2702 | * @return string XML response if no error occurs |
||
| 2703 | */ |
||
| 2704 | function ajax_moveEvent($_eventId,$calendarOwner,$targetDateTime,$targetOwner,$durationT=null,$seriesInstance=null) |
||
| 2906 | |||
| 2907 | /** |
||
| 2908 | * Change the status via ajax |
||
| 2909 | * @param string $_eventId |
||
| 2910 | * @param integer $uid |
||
| 2911 | * @param string $status |
||
| 2912 | */ |
||
| 2913 | function ajax_status($_eventId, $uid, $status) |
||
| 2959 | |||
| 2960 | /** |
||
| 2961 | * Deletes an event |
||
| 2962 | */ |
||
| 2963 | public function ajax_delete($eventId) |
||
| 2986 | |||
| 2987 | /** |
||
| 2988 | * |
||
| 2989 | * @param string $_eventId id of the event to be changed. For recurring events |
||
| 2990 | * it may contain the instance date |
||
| 2991 | * @param string[] $invite Resources to invite |
||
| 2992 | * @param string[] $remove Remove resource from participants |
||
| 2993 | */ |
||
| 2994 | public function ajax_invite($_eventId, $invite = array(), $remove = array()) |
||
| 3085 | |||
| 3086 | /** |
||
| 3087 | * imports a mail as Calendar |
||
| 3088 | * |
||
| 3089 | * @param array $mailContent = null mail content |
||
| 3090 | * @return array |
||
| 3091 | */ |
||
| 3092 | function mail_import(array $mailContent=null) |
||
| 3183 | } |
||
| 3184 |
This function has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.