1<?php 2 3/** 4 * SquirrelMail Shared Calendar Plugin 5 * Copyright (C) 2004-2005 Paul Lesneiwski <pdontthink@angrynerds.com> 6 * This program is licensed under GPL. See COPYING for details 7 * 8 */ 9 10 11/** 12 * Calendar class 13 * 14 */ 15class Calendar 16{ 17 18 var $id; 19 var $sequence; 20 var $dom; 21 var $prodID; 22 var $type; 23 var $name; 24 var $owners; 25 var $readable_users; 26 var $writeable_users; 27 var $createdOn; 28 var $lastUpdatedOn; 29 var $createdBy; 30 var $lastUpdatedBy; 31 var $holidays = array(); 32 var $events; 33 var $allEvents; 34 var $unknownAttributes = array(); 35 36 var $isExternalSource = FALSE; 37 38 var $currentMonthEventCache = 0; 39 40 41 42 /** 43 * Calendar constructor 44 * 45 * @param mixed $id The ID of this calendar (optional; ID is auto- 46 * generated if not given - NOTE that this is never 47 * a correct ID for (default) private type calendars!). 48 * May be specified as a string or a Property object. 49 * @param mixed $sequence The edit sequence ID for this calendar 50 * May be specified as an int or a Property object. 51 * @param mixed $dom The domain this calendar belongs under 52 * May be specified as a string or a Property object. 53 * @param mixed $prodID The product ID of the creating calendar system 54 * May be specified as a string or a Property object. 55 * @param mixed $type The type of this calendar, which should 56 * correspond to the calendar type constants 57 * defined in {@link constants.php} 58 * May be specified as a string or a Property object. 59 * @param mixed $name The name of this calendar 60 * May be specified as a string or a Property object. 61 * @param mixed $createdBy The name of the user who created this calendar 62 * May be specified as a string or a Property object. 63 * @param mixed $createdOn The date/time this calendar was created (optional; 64 * defaults to today's date) 65 * May be specified as a UTC-formatted timestamp 66 * string or a Property object. 67 * @param mixed $lastUpdatedBy The name of the user who last updated this calendar 68 * May be specified as a string or a Property object. 69 * @param mixed $lastUpdatedOn The date/time this calendar was last updated 70 * May be specified as a UTC-formatted timestamp 71 * string or a Property object. 72 * @param mixed $owners The users who share ownership of this calendar 73 * May be specified as an array or a Property object. 74 * @param mixed $readable_users The users who have read access to this calendar 75 * May be specified as an array or a Property object. 76 * @param mixed $writeable_users The users who have write access to this calendar 77 * May be specified as an array or a Property object. 78 * @param array $unknownAttributes Extra unknown attributes in an 79 * array keyed by attribute name, although 80 * the value MUST be the full iCal line 81 * describing the property, INCLUDING its 82 * name. These properties are often 83 * derived from custom attributes from an 84 * imported iCal file 85 * @param string $fallbackName An additional name to be used if 86 * none is given above or in any of the 87 * calendar's extra attributes (optional) 88 * @param string $uploadFilename A flag that indicates the source of this 89 * calendar; if given, it represents the 90 * filename from which it was uploaded, 91 * in which case the ID (if not 92 * given) will be constructed differently, 93 * so that it is not create-time based. 94 * (optional; default = empty string) 95 * 96 * Note that by default, if no owner, readable or writeable user is 97 * specified, the current user is given read permission only. 98 * 99 * Note that by default, if no calendar type is specified, it is assumed 100 * to be a PUBLIC calendar! 101 * 102 */ 103 function Calendar($id='', $sequence=0, $dom='', $prodID='', $type='', $name='', 104 $createdBy='', $createdOn='', $lastUpdatedBy='', $lastUpdatedOn='', 105 $owners=array(), $readable_users=array(), $writeable_users=array(), 106 $unknownAttributes=array(), $fallbackName='', $uploadFilename='') 107 { 108 109 if (is_object($id) && strtolower(get_class($id)) == 'property') 110 $this->id = $id; 111 else 112 $this->id = new Property('X-SQ-CALID', $id); 113 114 if (is_object($sequence) && strtolower(get_class($sequence)) == 'property') 115 $this->sequence = $sequence; 116 else 117 $this->sequence = new Property('X-SQ-CALSEQUENCE', $sequence); 118 119 if (is_object($dom) && strtolower(get_class($dom)) == 'property') 120 $this->dom = $dom; 121 else 122 $this->dom = new Property('X-SQ-CALDOMAIN', 123 strtr($dom, '@|_-.:/ \\', '________')); 124 125 if (is_object($prodID) && strtolower(get_class($prodID)) == 'property') 126 $this->prodID = $prodID; 127 else 128 $this->prodID = new Property('PRODID', $prodID); 129 130 if (is_object($type) && strtolower(get_class($type)) == 'property') 131 $this->type = $type; 132 else 133 $this->type = new Property('X-SQ-CALTYPE', $type); 134 135 if (is_object($name) && strtolower(get_class($name)) == 'property') 136 $this->name = $name; 137 else 138 $this->name = new Property('X-SQ-CALNAME', $name); 139 140 if (is_object($createdBy) && strtolower(get_class($createdBy)) == 'property') 141 $this->createdBy = $createdBy; 142 else 143 $this->createdBy = new Property('X-SQ-CALCREATOR', $createdBy); 144 145 if (is_object($createdOn) && strtolower(get_class($createdOn)) == 'property') 146 $this->createdOn = $createdOn; 147 else 148 $this->createdOn = new Property('X-SQ-CALCREATED', 149 (empty($createdOn) ? gmdate('Ymd\THis\Z') : $createdOn), 150 array(), SM_CAL_ICAL_PROPERTY_TYPE_DATETIME_UTC); 151 152 if (is_object($lastUpdatedBy) && strtolower(get_class($lastUpdatedBy)) == 'property') 153 $this->lastUpdatedBy = $lastUpdatedBy; 154 else 155 $this->lastUpdatedBy = new Property('X-SQ-CALLASTUPDATOR', 156 (empty($lastUpdatedBy) ? $this->createdBy() : $lastUpdatedBy)); 157 158 if (is_object($lastUpdatedOn) && strtolower(get_class($lastUpdatedOn)) == 'property') 159 $this->lastUpdatedOn = $lastUpdatedOn; 160 else 161 $this->lastUpdatedOn = new Property('X-SQ-CALLAST-MODIFIED', 162 (empty($lastUpdatedOn) ? $this->createdOn->getRawValue() : $lastUpdatedOn), 163 array(), SM_CAL_ICAL_PROPERTY_TYPE_DATETIME_UTC); 164 165 if (is_object($owners) && strtolower(get_class($owners)) == 'property') 166 $this->owners = $owners; 167 else 168 $this->owners = new Property('X-SQ-CALOWNERS', $owners); 169 170 if (is_object($readable_users) && strtolower(get_class($readable_users)) == 'property') 171 $this->readable_users = $readable_users; 172 else 173 $this->readable_users = new Property('X-SQ-CALREADABLEUSERS', $readable_users); 174 175 if (is_object($writeable_users) && strtolower(get_class($writeable_users)) == 'property') 176 $this->writeable_users = $writeable_users; 177 else 178 $this->writeable_users = new Property('X-SQ-CALWRITEABLEUSERS', $writeable_users); 179 180 $this->unknownAttributes = $unknownAttributes; 181 182 183 184 $sm_cal_prodid = str_replace('###VERSION###', calendar_version(), SM_CAL_PRODID); 185 global $domain, $username, $useDomainInCalID; 186 187 188 189 // insert default values if not given above 190 // 191 // note that user is only given read access by default; 192 // caller must explicitly give any more than that 193 // 194 $o = $this->getOwners(); 195 $w = $this->getWriteableUsers(); 196 $r = $this->getReadableUsers(); 197 if (empty($o) && empty($w) && empty($r)) 198 $this->readable_users->setValue(array($username)); 199 200 $c = $this->createdBy(); 201 if (empty($c)) 202 $this->setCreator($username); 203 204 205 $p = $this->getProductID(); 206 if (empty($p)) 207 $this->prodID->setValue($sm_cal_prodid); 208 209 $d = $this->dom->getValue(); 210 if (empty($d)) 211 $this->dom->setValue(strtr($domain, '@|_-.:/ \\', '________')); 212 213 $i = $this->getID(); 214 if (empty($i)) 215 { 216 //Note: Apple uses this field for cal ID: X-WR-RELCALID 217 if (!empty($unknownAttributes['X-WR-RELCALID'])) 218 { 219 $this->id = Property::extractICalProperty($unknownAttributes['X-WR-RELCALID']); 220 unset($unknownAttributes['X-WR-RELCALID']); 221 } 222 223 224 // uploaded calendars get an ID that is constructed w/out 225 // the default of having the create time in it 226 // 227 else if ($uploadFilename) 228 { 229 // if calendar itself already has createdOn date, 230 // use that 231 // 232 if (is_object($createdOn) && strtolower(get_class($createdOn)) == 'property') 233 $this->id->setValue('sm_cal_' . gmdate('Ymd\THis\Z', $this->createdOn()) 234 . ($useDomainInCalID ? '_' . $this->getDomain() : '')); 235 236 237 // otherwise, fudge it 238 // 239 else 240 { 241 // try to chop off extension/incrememtal file numbers 242 // 243 if (strlen($uploadFilename) > 6) 244 $uploadFilename = substr($uploadFilename, 0, strlen($uploadFilename) - 5); 245 $this->id->setValue(strtr('sm_uploaded_cal_' . $uploadFilename . '__' . $username 246 . ($useDomainInCalID ? '__' . $domain : ''), 247 '@|_-.:/\ ', '________')); 248 } 249 } 250 251 252 else 253//Note: this is not a correct ID for (default) private cals! caller should not 254// rely on auto-ID generation for private calendars! 255 $this->id->setValue('sm_cal_' . gmdate('Ymd\THis\Z') 256 . ($useDomainInCalID ? '_' . $this->getDomain() : '')); 257 } 258 259 $n = $this->getName(); 260 if (empty($n)) 261 { 262 //Note: Apple uses this field for cal name: X-WR-CALNAME 263 if (!empty($unknownAttributes['X-WR-CALNAME'])) 264 { 265 $this->name = Property::extractICalProperty($unknownAttributes['X-WR-CALNAME']); 266 unset($unknownAttributes['X-WR-CALNAME']); 267 } 268 else if (!empty($fallbackName)) 269 $this->name->setValue($fallbackName); 270 else 271//TODO: there has to be a better way to resolve a calendar name...! 272 $this->name->setValue($this->getProductID()); 273 } 274 275//TODO: yikes, is this too permissive? should we go the other way, assume private? 276// although a user only has ONE private calendar, so that might be a bad idea 277 $t = $this->getCalendarType(); 278 if (empty($t)) 279 $this->type->setValue(SM_CAL_TYPE_PUBLIC); 280 281//sm_print_r($this); 282 } 283 284 285 286// ---------- PRIVATE ---------- 287 288 289 290 /** 291 * Gathers calendar event data for the given month. 292 * 293 * NOTE that any events already in this object with 294 * the same event IDs as those being retrieved will 295 * be overwritten - the caller must save them first 296 * to avoid loss. 297 * 298 * @param int $year The year of the month to be retrieved 299 * @param int $month The month to be retrieved 300 * @param string $user The user for which events are being retrieved 301 * 302 * @access private 303 * 304 */ 305 function retrieveEventsForMonth($year, $month, $user) 306 { 307 308 // if source is external, we already have everything 309 // 310 if ($this->isExternal()) return; 311 312 313 if (!is_array($this->events)) 314 $this->events = array(); 315 316 317 if (!isset($this->events['onetime']) 318 || !is_array($this->events['onetime'])) 319 $this->events['onetime'] = array(); 320 321 322 // get events from calendar backend 323 // and merge them with any other events 324 // that we might already have 325 // 326 $this->events['onetime'] 327 = array_merge($this->events['onetime'], 328 get_events_for_month($this->getID(), $year, $month, $user)); 329 330 } 331 332 333 334 /** 335 * Gathers all calendar event, holiday and other data for 336 * all time periods. 337 * 338 * Most useful for exporting all events, as normal operation 339 * will be faster if only the needed events are in memory. 340 * 341 * In fact, events pulled in this function are stored in a 342 * separate array from what is normally used; the array 343 * is not categorized between one-time, recurring, holiday 344 * events, etc. 345 * 346 * NOTE that any events already in this object with 347 * the same event IDs as those being retrieved will 348 * be overwritten - the caller must save them first 349 * to avoid loss. 350 * 351 * @param string $user The user for which events are being retrieved 352 * 353 * @access private 354 * 355 */ 356 function retrieveAllEvents($user) 357 { 358 359 // if source is external, we already have everything 360 // 361 if ($this->isExternal()) return; 362 363 364 if (!is_array($this->allEvents)) 365 $this->allEvents = array(); 366 367 368 // get events from calendar backend 369 // and merge them with any other events 370 // that we might already have 371 // 372 $this->allEvents = array_merge($this->allEvents, get_all_events($this->getID(), $user)); 373 374 } 375 376 377 378 /** 379 * Gathers recurring calendar event data 380 * 381 * NOTE that any recurring events already in this object 382 * will be removed first - the caller must save them 383 * first to avoid loss. 384 * 385 * @param string $user The user for which events are being retrieved 386 * 387 * @access private 388 * 389 */ 390 function retrieveRecurringEvents($user) 391 { 392 393 // if source is external, we already have everything 394 // 395 if ($this->isExternal()) return; 396 397 398 if (!is_array($this->events)) 399 $this->events = array(); 400 401 402 if (!isset($this->events['recurring']) 403 || !is_array($this->events['recurring'])) 404 $this->events['recurring'] = array(); 405 406 407 // get events from calendar backend 408 // 409 $this->events['recurring'] = get_recurring_events($this->getID(), $user); 410 411 } 412 413 414 415 /** 416 * Gathers calendar holiday data 417 * 418 * NOTE that any holidays already in this object 419 * will be removed first - the caller must save them 420 * first to avoid loss. 421 * 422 * @param string $user The user for which holidays are being retrieved 423 * 424 * @access private 425 * 426 */ 427 function retrieveHolidays($user) 428 { 429 430 // if source is external, we already have everything 431 // 432 if ($this->isExternal()) return; 433 434 435 if (!is_array($this->holidays)) 436 $this->holidays = array(); 437 438 439 // get holidays from calendar backend 440 // 441 $this->holidays = get_calendar_holidays($this->getID(), $user); 442 443 } 444 445 446 447// ---------- PUBLIC ---------- 448 449 450 451 /** 452 * Determines if the source of this calendar is externally located 453 * 454 * @return boolean TRUE if calendar source is an external URI, FALSE otherwise 455 * 456 * @access public 457 * 458 */ 459 function isExternal() 460 { 461 return $this->isExternalSource; 462 } 463 464 465 466 /** 467 * Set As External 468 * 469 * Tells us if this calendar's source was from the outside world 470 * 471 * @param boolean $external TRUE if the source is external, FALSE otherwise 472 * 473 * @access public 474 * 475 */ 476 function setExternal($external) 477 { 478 $this->isExternalSource = $external; 479 } 480 481 482 483 /** 484 * Get Calendar ID 485 * 486 * @return string This calendar's internal ID 487 * 488 * @access public 489 * 490 */ 491 function getID() 492 { 493 494 // external calendars always have this prepended to the ID... 495 // 496 if ($this->isExternal()) 497 return 'SM_EXTERNAL' . $this->id->getValue(); 498 else 499 return $this->id->getValue(); 500 } 501 502 503 504 /** 505 * Set Calendar ID 506 * 507 * @param string $id The new ID to be assigned to this calendar 508 * 509 * @access public 510 * 511 */ 512 function setID($id) 513 { 514 515 // if already prefixed with external indicator, strip that off 516 // 517 if (strpos($id, 'SM_EXTERNAL') === 0) 518 $id = substr($id, 11); 519 520 $this->id->setValue($id); 521 } 522 523 524 525 /** 526 * Increment Sequence Number 527 * 528 * @access public 529 * 530 */ 531 function incrementSequence() 532 { 533 $this->sequence->setValue($this->sequence->getValue() + 1); 534 } 535 536 537 538 /** 539 * Set Calendar Type 540 * 541 * @param string $type The new type to assign to this calendar, 542 * which should correspond to the calendar type 543 * constants defined in {@link constants.php} 544 * 545 * @access public 546 * 547 */ 548 function setType($type) 549 { 550 $this->type->setValue($type); 551 } 552 553 554 555 /** 556 * Get Calendar Type 557 * 558 * @return string This calendar's type, which will 559 * correspond to the calendar type 560 * constants defined in {@link constants.php} 561 * 562 * @access public 563 * 564 */ 565 function getCalendarType() 566 { 567 return $this->type->getValue(); 568 } 569 570 571 572 /** 573 * Get Calendar Name 574 * 575 * @return string This calendar's name 576 * 577 * @access public 578 * 579 */ 580 function getName() 581 { 582 583 // need to translate (default) personal calendar name 584 // here since we can't always count on the correct 585 // language having been used when it was created 586 // 587 global $username, $domain; 588 if ($this->getCalendarType() == SM_CAL_TYPE_PERSONAL 589 && get_personal_cal_id($username, $domain, TRUE) == $this->getID()) 590 { 591 global $username; 592 return sprintf(_("Personal Calendar for %s"), $username); 593 } 594 595 return $this->name->getValue(); 596 } 597 598 599 600 /** 601 * Set Calendar Name 602 * 603 * @param string $name The new name to be assigned to this calendar 604 * 605 * @access public 606 * 607 */ 608 function setName($name) 609 { 610 $this->name->setValue($name); 611 } 612 613 614 615 /** 616 * Get Calendar Domain 617 * 618 * @return string This calendar's domain 619 * 620 * @access public 621 * 622 */ 623 function getDomain() 624 { 625 return $this->dom->getValue(); 626 } 627 628 629 630 /** 631 * Get Product ID 632 * 633 * @return string The product ID for the product that created this calendar 634 * 635 * @access public 636 * 637 */ 638 function getProductID() 639 { 640 return $this->prodID->getValue(); 641 } 642 643 644 645 /** 646 * Get Creator Name 647 * 648 * @return string This event's creator 649 * 650 * @access public 651 * 652 */ 653 function createdBy() 654 { 655 return $this->createdBy->getValue(); 656 } 657 658 659 660 /** 661 * Set Username of User Who Created This Calendar 662 * 663 * @param string $user The name of the user who created this calendar 664 * 665 * @access public 666 * 667 */ 668 function setCreator($user) 669 { 670 return $this->createdBy->setValue($user); 671 } 672 673 674 675 /** 676 * Get Creation Date 677 * 678 * @return timestamp This calendar's creation date 679 * 680 * @access public 681 * 682 */ 683 function createdOn() 684 { 685 return $this->createdOn->getValue(); 686 } 687 688 689 690 /** 691 * Get User That Last Updated Calendar 692 * 693 * @return string This calendar's last editor 694 * 695 * @access public 696 * 697 */ 698 function lastUpdatedBy() 699 { 700 return $this->lastUpdatedBy->getValue(); 701 } 702 703 704 705 /** 706 * Set Username of User Who Last Updated This Calendar 707 * 708 * @param string $user The name of the user updating this calendar 709 * 710 * @access public 711 * 712 */ 713 function setLastUpdator($user) 714 { 715 return $this->lastUpdatedBy->setValue($user); 716 } 717 718 719 720 /** 721 * Get Last Update Date 722 * 723 * @return timestamp The date of this calendar's last update 724 * 725 * @access public 726 * 727 */ 728 function lastUpdatedOn() 729 { 730 return $this->lastUpdatedOn->getValue(); 731 } 732 733 734 735 /** 736 * Set Last Update Date 737 * 738 * @param string $timestamp The date this calendar was last updated 739 * (should be a UTC-formatted date/time string) 740 * 741 * @access public 742 * 743 */ 744 function setLastUpdateDate($timestamp) 745 { 746 return $this->lastUpdatedOn->setValue($timestamp); 747 } 748 749 750 751 /** 752 * Get Calendar Owners 753 * 754 * @return array An array listing all calendar owners 755 * 756 * @access public 757 * 758 */ 759 function getOwners() 760 { 761 $o = $this->owners->getValue(); 762 if (empty($o)) 763 return array(); 764 else if (is_string($o)) 765 return array($o); 766 else 767 return $o; 768 } 769 770 771 772 /** 773 * Get Readable Users 774 * 775 * @return array An array listing all users 776 * who have read access to this calendar 777 * 778 * @access public 779 * 780 */ 781 function getReadableUsers() 782 { 783 $r = $this->readable_users->getValue(); 784 if (empty($r)) 785 return array(); 786 else if (is_string($r)) 787 return array($r); 788 else 789 return $r; 790 } 791 792 793 794 /** 795 * Get Writeable Users 796 * 797 * @return array An array listing all users 798 * who have write access to this calendar 799 * 800 * @access public 801 * 802 */ 803 function getWriteableUsers() 804 { 805 $w = $this->writeable_users->getValue(); 806 if (empty($w)) 807 return array(); 808 else if (is_string($w)) 809 return array($w); 810 else 811 return $w; 812 } 813 814 815 816 /** 817 * Get Unknown Attributes 818 * 819 * @return array An array of all unknown attributes 820 * 821 * @access public 822 * 823 */ 824 function getUnknownAttributes() 825 { 826 return $this->unknownAttributes; 827 } 828 829 830 831 /** 832 * Add a series of new events to this calendar. Events 833 * are merged with those already in the calendar. 834 * 835 * Note: can only accomodate up to 1,000,000 events in one 836 * calendar when $forceAddAll is TRUE. 837 * 838 * @param array $newEvents An array of event arrays, keyed 839 * by string sindicating event 840 * type ("onetime", "recurring", etc) 841 * @param string $oldParentCalID The ID of the calendar that 842 * has owned the events up to now 843 * @param boolean $holdNewEvents If TRUE, will NOT save new events 844 * (updated events are ALWAYS saved) 845 * to the backend, and will instead 846 * just keep the events in memory 847 * inside of the calendar object for 848 * saving at a later time. (optional; 849 * default = FALSE) 850 * @param boolean $forceAddAll If TRUE, will add all events, 851 * even when event IDs conflict 852 * (when events would otherwise be 853 * synched) (conflicts are avoided 854 * by adding random numbers to event 855 * IDs) (optional; default = FALSE) 856 * 857 * @access public 858 * 859 */ 860 function addEvents($newEvents, $oldParentCalID, 861 $holdNewEvents=FALSE, $forceAddAll=FALSE) 862 { 863 864 if (empty($newEvents) || !is_array($newEvents)) return; 865 866 867 if (!isset($this->events) 868 || !is_array($this->events)) 869 $this->events = array(); 870 if (!isset($this->events['onetime']) 871 || !is_array($this->events['onetime'])) 872 $this->events['onetime'] = array(); 873 if (!isset($this->events['recurring']) 874 || !is_array($this->events['recurring'])) 875 $this->events['recurring'] = array(); 876 877 878 $allEvents = array(); 879//TODO: what about other event types??? todos? holidays? 880 if (isset($newEvents['onetime']) && is_array($newEvents['onetime'])) 881 $allEvents = array_merge($allEvents, $newEvents['onetime']); 882 883 if (isset($newEvents['recurring']) && is_array($newEvents['recurring'])) 884 $allEvents = array_merge($allEvents, $newEvents['recurring']); 885 886 887 // 888 // 889 foreach ($allEvents as $event) 890 { 891 892 // attempt to get same event from backend 893 // 894 $event2 = get_event($this->getID(), $event->getID()); 895 if ($event2 === FALSE) $event2 = $this->getEvent($event->getID()); 896 897 898 // if we have a duplicate event ID but are forced 899 // to add it anyway, create unique ID and then add it 900 // 901 if ($forceAddAll) 902 { 903 sq_mt_randomize(); 904 $id = $event->getID(); 905 while ($event2 !== FALSE) 906 { 907 $id = $event->getID() . mt_rand(1, 1000000); 908 $event2 = get_event($this->getID(), $id); 909 if ($event2 === FALSE) $event2 = $this->getEvent($id); 910 } 911 $event->setID($id); 912 913 } 914 915 916 // add event to this cal if not found 917 // 918 if ($event2 === FALSE) 919 { 920 921 $event->removeParentCalendar($oldParentCalID); 922 $event->addParentCalendar($this->getID()); 923//TODO: see notes below about permissions not getting preserved 924 $event->resetPermissionsToParent(array($this)); 925 $event->removeParentCalendar($oldParentCalID); 926 927 928 // save event in correct place in memory 929 // if we are not saving to the backend 930 // 931 if ($holdNewEvents) 932 { 933//TODO: what about other event types??? todos? holidays? 934 if ($event->isRecurring()) 935 $this->events['recurring'][] = $event; 936 else if ($event->isOneTime()) 937 $this->events['onetime'][] = $event; 938 } 939 else 940 create_event($this->getID(), $event); 941 942 } 943 944 945 // synch: if date of new event is newer, 946 // replace event, otherwise, skip it 947 // 948 else if ($event->lastUpdatedOn() > $event2->lastUpdatedOn()) 949 { 950 $event->removeParentCalendar($oldParentCalID); 951 $event->addParentCalendar($this->getID()); 952//TODO: uploaded events may at some point have different permissions 953// that we want to obey, but for now, there should not be a 954// difference between cal and evt perms. and I am too lazy to 955// check if an upload will somehow munge any perms we may have 956// had (because it gets uploaded to a temporary new calendar 957// that is different from this one) 958 $event->resetPermissionsToParent(array($this)); 959 update_event($this->getID(), $event); 960 } 961 962 } 963 964 } 965 966 967 968 /** 969 * Dump all events currently cached in this object. Note 970 * that this may not in all cases correspond to ALL the 971 * events on en entire calendar (although it will in the 972 * case of an uploaded/external calendar or after calling 973 * $this->retrieveAllEvents()). 974 * 975 * @return array An array of event arrays, keyed by strings 976 * indicating event type ("onetime", 977 * "recurring", etc) 978 * 979 * @access public 980 * 981 */ 982 function getEvents() 983 { 984 return $this->events; 985 } 986 987 988 989 /** 990 * Get Event 991 * 992 * Looks for (and returns) an event amongst whatever events 993 * are currently loaded into this calendar. FALSE is returned 994 * when the event is not found -- note that sometimes this may 995 * not mean that there is no such event in this calendar! 996 * 997 * @param string $eventID The ID of the event to search for 998 * 999 * @return object An event object if the event was found 1000 * in memory in the current object; FALSE otherwise 1001 * 1002 * @access public 1003 * 1004 */ 1005 function getEvent($eventID) 1006 { 1007//TODO: what about other event types??? todos? holidays? 1008 1009 // look amongst one time events 1010 // 1011 foreach ($this->events['onetime'] as $event) 1012 if ($event->getID() == $eventID) 1013 return $event; 1014 1015 1016 // look amongst recurring events 1017 // 1018 foreach ($this->events['recurring'] as $event) 1019 if ($event->getID() == $eventID) 1020 return $event; 1021 1022 1023 return FALSE; 1024 } 1025 1026 1027 1028 /** 1029 * Determines if this calendar comes before 1030 * or after the given calendar, for use with 1031 * sorting calendars. 1032 * 1033 * @param object $otherCalendar The calendar to 1034 * compare to this 1035 * one. 1036 * 1037 * @return int -1 if this calendar comes first, 1038 * 1 if this calendar comes second, 1039 * or 0 if the calendars should be 1040 * considered to be "equal". 1041 * 1042 * @access public 1043 * 1044 */ 1045 function compareTo($otherCalendar) 1046 { 1047 1048 return strcasecmp($this->getName(), $otherCalendar->getName()); 1049 1050 } 1051 1052 1053 1054 /** 1055 * Returns any events that occur on the given day 1056 * 1057 * @param int $year The year of the day whose events are being returned 1058 * @param int $month The month of the day whose events are being returned 1059 * @param int $day The day whose events are being returned 1060 * @param string $user The user for which events are being returned 1061 * 1062 * @return array A list of all the events that occur today, sorted 1063 * chronologically (array of Event objects) 1064 * 1065 * @access public 1066 * 1067 */ 1068 function getEventsForDay($year, $month, $day, $user) 1069 { 1070 1071 // we only cache one month's worth of events at once 1072 // 1073 if (!$this->isExternal() && $this->currentMonthEventCache != $month) 1074 unset($this->events['onetime']); 1075 1076 1077 // get events if needed 1078 // 1079 if (!isset($this->events['onetime']) 1080 || !is_array($this->events['onetime'])) 1081 $this->retrieveEventsForMonth($year, $month, $user); 1082 if (!isset($this->events['recurring']) 1083 || !is_array($this->events['recurring'])) 1084 { 1085 $this->retrieveRecurringEvents($user); 1086 1087 // force events to cache all recurrences, which speeds 1088 // responsiveness tremendously 1089 // 1090 // Cache thru the 7th of January, two years ahead, 1091 // because the most extreme case is year view where 1092 // we start by getting the last few days of the previous 1093 // year. For month and day view, this is quite a bit 1094 // of overkill; I don't think it is much of a hit, but 1095 // it *could* be, especially with many complicated RRULEs. 1096 // So if needed, it might be possible to make a 1097 // differentiation for that here and only cache as 1098 // much as is really needed. 1099 // 1100 $cacheDate = mktime(12, 0, 0, 1, 7, $year + 2); 1101 foreach ($this->events['recurring'] as $index => $event) 1102 $this->events['recurring'][$index]->forceEventOccurrenceCache($cacheDate); 1103 } 1104 1105 1106 1107 1108 // loop through all events, looking for ones for this day 1109 // 1110 $returnArray = array(); 1111 foreach ($this->events['onetime'] as $event) 1112 if ($event->occursOnDay($year, $month, $day)) 1113 $returnArray[] = $event; 1114 foreach ($this->events['recurring'] as $event) 1115 if ($event->occursOnDay($year, $month, $day)) 1116 $returnArray[] = $event; 1117 1118 1119 // resort return array by starting event time 1120 // 1121 usort($returnArray, 'sortEventsByTime'); 1122 1123 1124 $this->currentMonthEventCache = $month; 1125 1126 return $returnArray; 1127 1128 } 1129 1130 1131 1132 /** 1133 * Displays the requested month 1134 * 1135 * @param int $year The year of the month to be shown 1136 * @param int $month The month to be shown 1137 * @param int $startDay The day of the week for first display column 1138 * @param string $user The user for which events are being displayed 1139 * @param string $viewType The type of month view to be shown, which 1140 * should correspond to the MONTH view mode 1141 * constants defined in {@link constants.php} 1142 * (optional; default is normal month view mode) 1143 * 1144 * @access public 1145 * 1146 */ 1147 function showMonth($year, $month, $startDay, $user, $viewType=SM_CAL_VIEW_MODE_MONTH) 1148 { 1149 1150 // display events 1151 // 1152 if ($viewType == SM_CAL_VIEW_MODE_MONTH || $viewType == SM_CAL_VIEW_MODE_ALL_MONTHS 1153 || $viewType == SM_CAL_VIEW_MODE_MONTH_MINIATURE) 1154 { 1155 include_once(SM_PATH . 'plugins/calendar/interface/month.php'); 1156 showCalendarMonth($this, $year, $month, $startDay, $user, $viewType); 1157 } 1158 else 1159 { 1160 global $color; 1161 plain_error_message('ERROR IN CALENDAR CLASS (showMonth): Invalid view mode', $color); 1162 exit; 1163 } 1164 1165 } 1166 1167 1168 1169 /** 1170 * Displays the requested year 1171 * 1172 * @param int $year The year to be shown 1173 * @param string $user The user for which events are being displayed 1174 * 1175 * @access public 1176 * 1177 */ 1178 function showYear($year, $user) 1179 { 1180 1181//LEFT OFF HERE 1182//LEFT OFF HERE 1183 1184 } 1185 1186 1187 1188 /** 1189 * Displays all months for the requested year 1190 * 1191 * @param int $year The year to be shown 1192 * @param string $user The user for which events are being displayed 1193 * @param int $startDay The day of the week for first display column 1194 * 1195 * @access public 1196 * 1197 */ 1198 function showAllMonths($year, $user, $startDay) 1199 { 1200 1201 for ($i = 1; $i < 13; $i++) 1202 { 1203 $this->showMonth($year, $i, $startDay, $user, SM_CAL_VIEW_MODE_ALL_MONTHS); 1204 } 1205 1206 } 1207 1208 1209 1210 /** 1211 * Displays the requested day 1212 * 1213 * @param int $year The year of the day to be shown 1214 * @param int $month The month of the day to be shown 1215 * @param int $day The day to be shown 1216 * @param string $user The user for which events are being displayed 1217 * @param boolean $allDay Indicates if the whole day should be 1218 * displayed, from midnight to midnight. If 1219 * FALSE, a limited number of hours are 1220 * displayed per the configuration file. 1221 * 1222 * @access public 1223 * 1224 */ 1225 function showDay($year, $month, $day, $user, $allDay=FALSE) 1226 { 1227 1228 // display events 1229 // 1230 include_once(SM_PATH . 'plugins/calendar/interface/day.php'); 1231 showCalendarDay($this, $year, $month, $day, $user, $allDay); 1232 1233 } 1234 1235 1236 1237 /** 1238 * Returns any holidays that occur on the given day 1239 * 1240 * @param int $year The year of the day whose holidays are being returned 1241 * @param int $month The month of the day whose holidays are being returned 1242 * @param int $day The day whose holidays are being returned 1243 * @param string $user The user for which holidays are being returned 1244 * 1245 * @return array A list of all the holidays that occur today, sorted 1246 * chronologically (array of Event objects) 1247 * 1248 * @access public 1249 * 1250 */ 1251 function getHolidaysForDay($year, $month, $day, $user) 1252 { 1253 1254 // do we already have holidays in memory? 1255 // then we don't need to load them from the backend 1256 // 1257 if (empty($this->holidays) || !is_array($this->holidays) || sizeof($this->holidays) == 0) 1258 { 1259 $this->retrieveHolidays($user); 1260 1261 // force events to cache all recurrences, which speeds 1262 // responsiveness tremendously 1263 // 1264 // Cache thru the 7th of January, two years ahead, 1265 // because the most extreme case is year view where 1266 // we start by getting the last few days of the previous 1267 // year. For month and day view, this is quite a bit 1268 // of overkill; I don't think it is much of a hit, but 1269 // it *could* be, especially with many complicated RRULEs. 1270 // So if needed, it might be possible to make a 1271 // differentiation for that here and only cache as 1272 // much as is really needed. 1273 // 1274 $cacheDate = mktime(12, 0, 0, 1, 7, $year + 2); 1275 foreach ($this->holidays as $index => $holiday) 1276 $this->holidays[$index]->forceEventOccurrenceCache($cacheDate); 1277 1278 } 1279 1280 1281 // loop through all holidays, looking for ones for this day 1282 // 1283 $returnArray = array(); 1284 foreach ($this->holidays as $holiday) 1285 if ($holiday->occursOnDay($year, $month, $day)) 1286 $returnArray[] = $holiday; 1287 1288 1289 return $returnArray; 1290 1291 } 1292 1293 1294 1295 /** 1296 * Removes a user from this calendar, from the list 1297 * of owners, readable users, and writeable users 1298 * 1299 * @param string $user The user to be removed 1300 * 1301 * @access public 1302 * 1303 */ 1304 function remove_user($user) 1305 { 1306 1307 $this->owners->setValue(array_diff($this->getOwners(), array($user))); 1308 $this->readable_users->setValue(array_diff($this->getReadableUsers(), array($user))); 1309 $this->writeable_users->setValue(array_diff($this->getWriteableUsers(), array($user))); 1310 1311 } 1312 1313 1314 1315 /** 1316 * Adds a new user to this calendar 1317 * 1318 * @param string $user The user name being added 1319 * @param string $accessLevel The access level being 1320 * granted to the new user, 1321 * which should correspond 1322 * to the calendar access constants 1323 * defined in {@link constants.php} 1324 * @access public 1325 * 1326 */ 1327 function add_user($user, $accessLevel) 1328 { 1329 1330 if ($accessLevel == SM_CAL_ACCESS_LEVEL_OWNER) 1331 $this->owners->setValue(array_merge($this->getOwners(), array($user))); 1332 1333 else if ($accessLevel == SM_CAL_ACCESS_LEVEL_READ) 1334 $this->readable_users->setValue(array_merge($this->getReadableUsers(), array($user))); 1335 1336 else if ($accessLevel == SM_CAL_ACCESS_LEVEL_WRITE) 1337 $this->writeable_users->setValue(array_merge($this->getWriteableUsers(), array($user))); 1338 1339 } 1340 1341 1342 1343 /** 1344 * Determines if the given user is an owner of this calendar 1345 * 1346 * @param string $user The user to inspect for ownership 1347 * 1348 * @return boolean TRUE if the user is an owner of this calendar, FALSE otherwise 1349 * 1350 * @access public 1351 * 1352 */ 1353 function isOwner($user) 1354 { 1355 1356 // can't just test to see if user is in owner array, since 1357 // we allow for wildcards in owner names 1358 // 1359 foreach ($this->getOwners() as $owner) 1360 if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'), 1361 strtoupper($owner)) . '$/', strtoupper($user))) 1362 return TRUE; 1363 1364 1365 return FALSE; 1366 1367 } 1368 1369 1370 1371 /** 1372 * Determines if the given user has read access to this calendar 1373 * 1374 * @param string $user The user to inspect for read permission 1375 * 1376 * @return boolean TRUE if the user has read access to this calendar, FALSE otherwise 1377 * 1378 * @access public 1379 * 1380 */ 1381 function canRead($user) 1382 { 1383 1384 // public calendars are always readable to anyone 1385 // (TODO: in the future, we may want to limit this 1386 // per domain) 1387 // 1388 if ($this->getCalendarType() == SM_CAL_TYPE_PUBLIC) 1389 return TRUE; 1390 1391 1392 // can't just test to see if user is in readable_users array, since 1393 // we allow for wildcards in user names 1394 // 1395 foreach ($this->getReadableUsers() as $readUser) 1396 if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'), 1397 strtoupper($readUser)) . '$/', strtoupper($user))) 1398 return TRUE; 1399 1400 1401 return FALSE; 1402 1403 } 1404 1405 1406 1407 /** 1408 * Determines if the given user has write access to this calendar 1409 * 1410 * @param string $user The user to inspect for write permission 1411 * 1412 * @return boolean TRUE if the user has write access to this calendar, FALSE otherwise 1413 * 1414 * @access public 1415 * 1416 */ 1417 function canWrite($user) 1418 { 1419 1420 // can't just test to see if user is in writeable_users array, since 1421 // we allow for wildcards in user names 1422 // 1423 foreach ($this->getWriteableUsers() as $writeUser) 1424 if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'), 1425 strtoupper($writeUser)) . '$/', strtoupper($user))) 1426 return TRUE; 1427 1428 1429 return FALSE; 1430 1431 } 1432 1433 1434 1435 /** 1436 * Save Events To Backend 1437 * 1438 * Saves all currently loaded events into the backend (NOTE 1439 * that this may not always represent ALL events for the 1440 * calendar!). This should only be called once; events are 1441 * CREATED, and if you attempt to create them twice, errors 1442 * will be thrown. 1443 * 1444 * @param boolean $resetPermissions If TRUE, all events will 1445 * have their permissions 1446 * wiped and reset to match 1447 * their parent calendar. 1448 * 1449 * @access public 1450 * 1451 */ 1452 function saveEvents($resetPermissions) 1453 { 1454 1455 assert(is_bool($resetPermissions)); 1456 1457 1458 // save one time events 1459 // 1460 foreach ($this->events['onetime'] as $event) 1461 { 1462 if ($resetPermissions) 1463 $event->resetPermissionsToParent(); 1464 1465 create_event($this->getID(), $event); 1466 } 1467 1468 // save recurring events 1469 // 1470 foreach ($this->events['recurring'] as $event) 1471 { 1472 if ($resetPermissions) 1473 $event->resetPermissionsToParent(); 1474 1475 create_event($this->getID(), $event); 1476 } 1477 1478//TODO: holidays? todos? 1479 1480 } 1481 1482 1483 1484 /** 1485 * Constructs iCal representation of this calendar 1486 * 1487 * Note that the returned text ONLY includes calendar 1488 * attributes, which is not, per RFC2445, a valid iCal 1489 * object. The caller should either insert at least 1490 * one other iCal component (event, timezone, etc) 1491 * or use the $includeEventsForUser parameter herein. 1492 * 1493 * @param boolean $includeExtras When TRUE, all calendar 1494 * attributes are included 1495 * in the return value, even 1496 * those that are for internal 1497 * use only and should NOT be 1498 * part of output to the 1499 * outside world. When FALSE, 1500 * only RFC-allowable fields 1501 * are included (optional; 1502 * default = FALSE). 1503 * @param string $includeEventsForUser When non-empty, this should 1504 * specify a user for which all 1505 * events, holidays, and others 1506 * are included (only events that 1507 * the specified user has at least 1508 * read access to are included). 1509 * When not given, no events are 1510 * included; only the calendar itself 1511 * is returned (optional; default = empty). 1512 * @param string $icalLineDelimOverride If given, will be used instead 1513 * the RFC-standard CRLF to terminate 1514 * iCal lines (optional) 1515 * 1516 * @return string The iCal stream representing this calendar 1517 * 1518 * @access public 1519 * 1520 */ 1521 function getICal($includeExtras=FALSE, $includeEventsForUser='', 1522 $icalLineDelimOverride='') 1523 { 1524 1525 // figure out line delimiter 1526 // 1527 if (empty($icalLineDelimOverride)) 1528 $icalLineDelim = ICAL_LINE_DELIM; 1529 else 1530 $icalLineDelim = $icalLineDelimOverride; 1531 1532 1533 $iCalVersion = '2.0'; 1534 $sm_cal_prodid = str_replace('###VERSION###', calendar_version(), SM_CAL_PRODID); 1535 1536 1537 $iCalText = 'BEGIN:VCALENDAR' . $icalLineDelim 1538 . 'VERSION:' . $iCalVersion . $icalLineDelim 1539 . $this->prodID->getICal($icalLineDelim) 1540 . 'CALSCALE:GREGORIAN' . $icalLineDelim 1541 . 'METHOD:PUBLISH' . $icalLineDelim 1542 1543 // make Apple happy 1544 // 1545 . 'X-WR-CALNAME:' . $this->getName() . $icalLineDelim 1546 . 'X-WR-RELCALID:' . $this->id->getValue() . $icalLineDelim; 1547 1548 1549 // only show our own custom ID and name fields 1550 // if this calendar was created by this application 1551 // 1552 if ($this->getProductID() == $sm_cal_prodid) 1553 $iCalText .= $this->id->getICal($icalLineDelim) 1554//LEFT OFF HERE 1555//TODO: following property should have language and encoding info! (take care of in constructor??) 1556 . $this->name->getICal($icalLineDelim); 1557 1558 1559 // unknown attributes are included, since they 1560 // were probably custom attributes defined by an 1561 // external source 1562 // 1563 foreach ($this->unknownAttributes as $name => $attr) 1564 1565 // already printed these 1566 // 1567 if ($name != 'X-WR-CALNAME' && $name != 'X-WR-RELCALID') 1568 $iCalText .= $attr . $icalLineDelim; 1569 1570 1571 // include all our internal attributes? 1572 // 1573 if ($includeExtras) 1574 { 1575 $iCalText .= $this->dom->getICal($icalLineDelim) 1576 . $this->type->getICal($icalLineDelim) 1577 . $this->sequence->getICal($icalLineDelim) 1578 . $this->createdBy->getICal($icalLineDelim) 1579 . $this->createdOn->getICal($icalLineDelim) 1580 . $this->lastUpdatedBy->getICal($icalLineDelim) 1581 . $this->lastUpdatedOn->getICal($icalLineDelim) 1582 . $this->owners->getICal($icalLineDelim) 1583 . $this->readable_users->getICal($icalLineDelim) 1584 . $this->writeable_users->getICal($icalLineDelim); 1585 } 1586 1587 1588 1589 // if we need to dump all events, do that here right before ending the calendar 1590 // 1591 if (!empty($includeEventsForUser)) 1592 { 1593 $this->retrieveAllEvents($includeEventsForUser); 1594 foreach ($this->allEvents as $event) 1595 $iCalText .= $event->getICal(FALSE, $icalLineDelim); 1596 } 1597 1598 1599 1600 $iCalText .= 'END:VCALENDAR' . $icalLineDelim; 1601 1602 1603 // fold lines that are too long 1604 // 1605 foldICalStreamByRef($iCalText); 1606 1607 1608 return $iCalText; 1609 1610 } 1611 1612 1613 1614 /** 1615 * Constructs a Calendar object from the given iCal stream 1616 * 1617 * @param array $iCalData The text value to be converted to a 1618 * Calendar object, one text line in 1619 * each array value. 1620 * @param string $fallbackName An additional name to be used if 1621 * none is given above or in any of the 1622 * calendar's extra attributes (optional) 1623 * @param string $uploadFilename A flag that indicates the source of this 1624 * calendar; if given, it represents the 1625 * filename from which it was uploaded, 1626 * in which case the ID (if not 1627 * given) will be constructed differently, 1628 * so that it is not create-time based. 1629 * (optional; default = empty string) 1630 * 1631 * @return object A Calendar object representing the given iCal stream. 1632 * 1633 * @access public 1634 * 1635 */ 1636 function getCalendarFromICal($iCalText, $fallbackName='', $uploadFilename='') 1637 { 1638 1639 global $color; 1640 1641 1642 // strip out CRLFs from each line 1643 // 1644 foreach ($iCalText as $x => $line) 1645 $iCalText[$x] = str_replace(ICAL_LINE_DELIM, '', $line); 1646 1647 1648 // unfold text 1649 // 1650 unfoldICalStreamByRef($iCalText); 1651 1652 1653 $id = ''; 1654 $dom = ''; 1655 $type = ''; 1656 $prodID = ''; 1657 $name = ''; 1658 $createdBy = ''; 1659 $createdOn = ''; 1660 $lastUpdatedBy = ''; 1661 $lastUpdatedOn = ''; 1662 $sequence = ''; 1663 $owners = ''; 1664 $writeable_users = ''; 1665 $readable_users = ''; 1666 $unknownAttributes = array(); 1667 $embeddedEvents = array(); 1668 1669 1670 // pull out properties 1671 // 1672 reset($iCalText); 1673 while (list($junk, $line) = each($iCalText)) 1674 // use reset/each/next instead so we can use next below inside the loop 1675 // foreach ($iCalText as $line) 1676 { 1677 1678 $property = Property::extractICalProperty($line); 1679 1680 1681 // what do we do with each property? 1682 // 1683 switch ($property->getName()) 1684 { 1685 1686 // skip this unless it is an embedded event 1687 // 1688 case 'BEGIN': 1689 if ($property->getValue() == 'VEVENT') 1690 { 1691 1692 // build event array and pass to event parser 1693 // 1694 $event = array($line); 1695 while (list($junk, $line) = each($iCalText)) 1696 { 1697 $event[] = $line; 1698 $property = Property::extractICalProperty($line); 1699 if ($property->getName() == 'END') 1700 break; 1701 } 1702 1703 $embeddedEvents[] = Event::getEventFromICal($event); 1704 1705 } 1706 break; 1707 1708 1709 // just skip these 1710 // 1711 case 'END': 1712 break; 1713 1714 1715 // version check 1716 // 1717 case 'VERSION': 1718 if ($property->getValue() != '2.0') 1719 { 1720 plain_error_message('ERROR IN CALENDAR CLASS (getCalendarFromICal): Unsupported iCal version ' . $property->getValue(), $color); 1721 exit; 1722 } 1723 break; 1724 1725 1726 // calendar scale check 1727 // 1728 case 'CALSCALE': 1729 if ($property->getValue() != 'GREGORIAN') 1730 { 1731 plain_error_message('ERROR IN CALENDAR CLASS (getCalendarFromICal): Unsupported calendar scale ' . $property->getValue(), $color); 1732 exit; 1733 } 1734 break; 1735 1736 1737 // product ID 1738 // 1739 case 'PRODID': 1740 $prodID = $property; 1741 break; 1742 1743 1744 // cal id for calendars created with this application 1745 // 1746 case 'X-SQ-CALID': 1747 $id = $property; 1748 break; 1749 1750 1751 // cal name for calendars created with this application 1752 // 1753 case 'X-SQ-CALNAME': 1754 $name = $property; 1755 break; 1756 1757 1758 // cal domain for calendars created with this application 1759 // 1760 case 'X-SQ-CALDOMAIN': 1761 $dom = $property; 1762 break; 1763 1764 1765 // cal type for calendars created with this application 1766 // 1767 case 'X-SQ-CALTYPE': 1768 $type = $property; 1769 break; 1770 1771 1772 // cal sequence for calendars created with this application 1773 // 1774 case 'X-SQ-CALSEQUENCE': 1775 $sequence = $property; 1776 break; 1777 1778 1779 // user who created this calendar for calendars created with this application 1780 // 1781 case 'X-SQ-CALCREATOR': 1782 $createdBy = $property; 1783 break; 1784 1785 1786 // creation date for calendars created with this application 1787 // (need to reparse the value as a date) 1788 // 1789 case 'X-SQ-CALCREATED': 1790 $createdOn = Property::extractICalProperty($line, SM_CAL_ICAL_PROPERTY_TYPE_DATE); 1791 break; 1792 1793 1794 // user who last modified this calendar for calendars created with this application 1795 // 1796 case 'X-SQ-CALLASTUPDATOR': 1797 $lastUpdatedBy = $property; 1798 break; 1799 1800 1801 // date last modified for calendars created with this application 1802 // (need to reparse the value as a date) 1803 // 1804 case 'X-SQ-CALLAST-MODIFIED': 1805 $lastUpdatedOn = Property::extractICalProperty($line, SM_CAL_ICAL_PROPERTY_TYPE_DATE); 1806 break; 1807 1808 1809 // cal owners for calendars created with this application 1810 // 1811 case 'X-SQ-CALOWNERS': 1812 $owners = $property; 1813 break; 1814 1815 1816 // cal writeable users for calendars created with this application 1817 // 1818 case 'X-SQ-CALWRITEABLEUSERS': 1819 $writeable_users = $property; 1820 break; 1821 1822 1823 // cal readable users for calendars created with this application 1824 // 1825 case 'X-SQ-CALREADABLEUSERS': 1826 $readable_users = $property; 1827 break; 1828 1829 1830 // unknown parameters just pile into this array 1831 // 1832 default: 1833 $unknownAttributes[$property->getName()] = $line; 1834 break; 1835 1836 } 1837 1838 } 1839 1840 1841 $cal = new Calendar($id, $sequence, $dom, $prodID, $type, $name, 1842 $createdBy, $createdOn, $lastUpdatedBy, $lastUpdatedOn, 1843 $owners, $readable_users, $writeable_users, 1844 $unknownAttributes, $fallbackName, $uploadFilename); 1845 1846 1847 // add events (only if any were found) 1848 // 1849 if (sizeof($embeddedEvents)) 1850 { 1851 1852//LEFT OFF HERE um, if this is the only place we call the caching function for external calendar events, will it not be able to cache events outside of this date range? will it slow considerably if the user views months outside this range? does this help (getting it out of the FORM first)? 1853 sqgetGlobalVar('year', $year, SQ_FORM); 1854 if (empty($year)) 1855 $year = date('Y'); 1856 1857 $cacheDate = mktime(12, 0, 0, 1, 7, $year + 2); 1858 1859 $newEvents = array(); 1860 $newEvents['onetime'] = array(); 1861 $newEvents['recurring'] = array(); 1862//TODO.... how do we store Todo items??? 1863// $newEvents['todo'] = array(); 1864 foreach ($embeddedEvents as $event) 1865 { 1866 if ($event->isOneTime()) 1867 $newEvents['onetime'][] = $event; 1868 else if ($event->isRecurring()) 1869 { 1870 $newEvents['recurring'][] = $event; 1871 } 1872//TODO.... how do we store Todo items??? 1873// else if ($event->isTask()) 1874// $newEvents['todo'][] = $event; 1875 } 1876 1877 // precache recurring events 1878 // 1879 foreach (array_keys($newEvents['recurring']) as $index) 1880 $newEvents['recurring'][$index]->forceEventOccurrenceCache($cacheDate); 1881 1882 1883 // Add all events to the calendar now. 1884 // 1885 // Setting the third parameter below to TRUE means 1886 // we trust that all events are legitimate and no 1887 // duplicates exist; imported calendars that have 1888 // those problems should be fixed at the source anyway. 1889 // This also fixes problem when importing events w/no 1890 // IDs quickly while looping through uploaded iCal file, 1891 // timestamps are likely to match, so IDs will overlap, 1892 // but the TRUE below fixes that 1893 // 1894 $cal->addEvents($newEvents, 'xxx', TRUE, TRUE); 1895 1896 } 1897 1898 1899 return $cal; 1900 1901 1902 } 1903 1904 1905 1906} 1907 1908 1909 1910?> 1911