Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 3 /** 4 * BENNU - PHP iCalendar library 5 * (c) 2005-2006 Ioannis Papaioannou (pj@moodle.org). All rights reserved. 6 * 7 * Released under the LGPL. 8 * 9 * See http://bennu.sourceforge.net/ for more information and downloads. 10 * 11 * @author Ioannis Papaioannou 12 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 13 */ 14 15 class iCalendar_component { 16 var $name = NULL; 17 var $properties = NULL; 18 var $components = NULL; 19 var $valid_properties = NULL; 20 var $valid_components = NULL; 21 /** 22 * Added to hold errors from last run of unserialize 23 * @var $parser_errors array 24 */ 25 var $parser_errors = NULL; 26 27 function __construct() { 28 // Initialize the components array 29 if(empty($this->components)) { 30 $this->components = array(); 31 foreach($this->valid_components as $name) { 32 $this->components[$name] = array(); 33 } 34 } 35 } 36 37 function get_name() { 38 return $this->name; 39 } 40 41 function add_property($name, $value = NULL, $parameters = NULL) { 42 43 // Uppercase first of all 44 $name = strtoupper($name); 45 46 // Are we trying to add a valid property? 47 $xname = false; 48 if(!isset($this->valid_properties[$name])) { 49 // If not, is it an x-name as per RFC 2445? 50 if(!rfc2445_is_xname($name)) { 51 return false; 52 } 53 // Since this is an xname, all components are supposed to allow this property 54 $xname = true; 55 } 56 57 // Create a property object of the correct class 58 if($xname) { 59 $property = new iCalendar_property_x; 60 $property->set_name($name); 61 } 62 else { 63 $classname = 'iCalendar_property_'.strtolower(str_replace('-', '_', $name)); 64 $property = new $classname; 65 } 66 67 // If $value is NULL, then this property must define a default value. 68 if($value === NULL) { 69 $value = $property->default_value(); 70 if($value === NULL) { 71 return false; 72 } 73 } 74 75 // Set this property's parent component to ourselves, because some 76 // properties behave differently according to what component they apply to. 77 $property->set_parent_component($this->name); 78 79 // Set parameters before value; this helps with some properties which 80 // accept a VALUE parameter, and thus change their default value type. 81 82 // The parameters must be valid according to property specifications 83 if(!empty($parameters)) { 84 foreach($parameters as $paramname => $paramvalue) { 85 if(!$property->set_parameter($paramname, $paramvalue)) { 86 return false; 87 } 88 } 89 90 // Some parameters interact among themselves (e.g. ENCODING and VALUE) 91 // so make sure that after the dust settles, these invariants hold true 92 if(!$property->invariant_holds()) { 93 return false; 94 } 95 } 96 97 // $value MUST be valid according to the property data type 98 if(!$property->set_value($value)) { 99 return false; 100 } 101 102 // Check if the property already exists, and is limited to one occurrance, 103 // DON'T overwrite the value - this can be done explicity with set_value() instead. 104 if(!$xname && $this->valid_properties[$name] & RFC2445_ONCE && isset($this->properties[$name])) { 105 return false; 106 } 107 else { 108 // Otherwise add it to the instance array for this property 109 $this->properties[$name][] = $property; 110 } 111 112 // Finally: after all these, does the component invariant hold? 113 if(!$this->invariant_holds()) { 114 // If not, completely undo the property addition 115 array_pop($this->properties[$name]); 116 if(empty($this->properties[$name])) { 117 unset($this->properties[$name]); 118 } 119 return false; 120 } 121 122 return true; 123 124 } 125 126 function add_component($component) { 127 128 // With the detailed interface, you can add only components with this function 129 if(!is_object($component) || !is_subclass_of($component, 'iCalendar_component')) { 130 return false; 131 } 132 133 $name = $component->get_name(); 134 135 // Only valid components as specified by this component are allowed 136 if(!in_array($name, $this->valid_components)) { 137 return false; 138 } 139 140 // Add it 141 $this->components[$name][] = $component; 142 143 return true; 144 } 145 146 function get_property_list($name) { 147 } 148 149 function invariant_holds() { 150 return true; 151 } 152 153 function is_valid() { 154 // If we have any child components, check that they are all valid 155 if(!empty($this->components)) { 156 foreach($this->components as $component => $instances) { 157 foreach($instances as $number => $instance) { 158 if(!$instance->is_valid()) { 159 return false; 160 } 161 } 162 } 163 } 164 165 // Finally, check the valid property list for any mandatory properties 166 // that have not been set and do not have a default value 167 foreach($this->valid_properties as $property => $propdata) { 168 if(($propdata & RFC2445_REQUIRED) && empty($this->properties[$property])) { 169 $classname = 'iCalendar_property_'.strtolower(str_replace('-', '_', $property)); 170 $object = new $classname; 171 if($object->default_value() === NULL) { 172 return false; 173 } 174 unset($object); 175 } 176 } 177 178 return true; 179 } 180 181 function serialize() { 182 // Check for validity of the object 183 if(!$this->is_valid()) { 184 return false; 185 } 186 187 // Maybe the object is valid, but there are some required properties that 188 // have not been given explicit values. In that case, set them to defaults. 189 foreach($this->valid_properties as $property => $propdata) { 190 if(($propdata & RFC2445_REQUIRED) && empty($this->properties[$property])) { 191 $this->add_property($property); 192 } 193 } 194 195 // Start tag 196 $string = rfc2445_fold('BEGIN:'.$this->name) . RFC2445_CRLF; 197 198 // List of properties 199 if(!empty($this->properties)) { 200 foreach($this->properties as $name => $properties) { 201 foreach($properties as $property) { 202 $string .= $property->serialize(); 203 } 204 } 205 } 206 207 // List of components 208 if(!empty($this->components)) { 209 foreach($this->components as $name => $components) { 210 foreach($components as $component) { 211 $string .= $component->serialize(); 212 } 213 } 214 } 215 216 // End tag 217 $string .= rfc2445_fold('END:'.$this->name) . RFC2445_CRLF; 218 219 return $string; 220 } 221 222 /** 223 * unserialize() 224 * 225 * I needed a way to convert an iCalendar component back to a Bennu object so I could 226 * easily access and modify it after it had been stored; if this functionality is already 227 * present somewhere in the library, I apologize for adding it here unnecessarily; however, 228 * I couldn't find it so I added it myself. 229 * @param string $string the iCalendar object to load in to this iCalendar_component 230 * @return bool true if the file parsed with no errors. False if there were errors. 231 */ 232 233 function unserialize($string) { 234 $string = rfc2445_unfold($string); // Unfold any long lines 235 $lines = preg_split("<".RFC2445_CRLF."|\n|\r>", $string, 0, PREG_SPLIT_NO_EMPTY); // Create an array of lines. 236 237 $components = array(); // Initialise a stack of components 238 $this->clear_errors(); 239 foreach ($lines as $key => $line) { 240 // ignore empty lines 241 if (trim($line) == '') { 242 continue; 243 } 244 245 // Divide the line up into label, parameters and data fields. 246 if (!preg_match('#^(?P<label>[-[:alnum:]]+)(?P<params>(?:;(?:(?:[-[:alnum:]]+)=(?:[^[:cntrl:]";:,]+|"[^[:cntrl:]"]+")))*):(?P<data>.*)$#', $line, $match)) { 247 $this->parser_error('Invalid line: '.$key.', ignoring'); 248 continue; 249 } 250 251 // parse parameters 252 $params = array(); 253 if (preg_match_all('#;(?P<param>[-[:alnum:]]+)=(?P<value>[^[:cntrl:]";:,]+|"[^[:cntrl:]"]+")#', $match['params'], $pmatch)) { 254 $params = array_combine($pmatch['param'], $pmatch['value']); 255 } 256 $label = $match['label']; 257 $data = $match['data']; 258 unset($match, $pmatch); 259 260 if ($label == 'BEGIN') { 261 // This is the start of a component. 262 $current_component = array_pop($components); // Get the current component off the stack so we can check its valid components 263 if ($current_component == null) { // If there's nothing on the stack 264 $current_component = $this; // use the iCalendar 265 } 266 if (in_array($data, $current_component->valid_components)) { // Check that the new component is a valid subcomponent of the current one 267 if($current_component != $this) { 268 array_push($components, $current_component); // We're done with the current component, put it back on the stack. 269 } 270 if(strpos($data, 'V') === 0) { 271 $data = substr($data, 1); 272 } 273 $cname = 'iCalendar_' . strtolower($data); 274 $new_component = new $cname; 275 array_push($components, $new_component); // Push a new component onto the stack 276 } else { 277 if($current_component != $this) { 278 array_push($components, $current_component); 279 $this->parser_error('Invalid component type on line '.$key); 280 } 281 } 282 unset($current_component, $new_component); 283 } else if ($label == 'END') { 284 // It's the END of a component. 285 $component = array_pop($components); // Pop the top component off the stack - we're now done with it 286 $parent_component = array_pop($components); // Pop the component's conatining component off the stack so we can add this component to it. 287 if($parent_component == null) { 288 $parent_component = $this; // If there's no components on the stack, use the iCalendar object 289 } 290 if ($component !== null) { 291 if ($parent_component->add_component($component) === false) { 292 $this->parser_error("Failed to add component on line $key"); 293 } 294 } 295 if ($parent_component != $this) { // If we're not using the iCalendar 296 array_push($components, $parent_component); // Put the component back on the stack 297 } 298 unset($parent_component, $component); 299 } else { 300 301 $component = array_pop($components); // Get the component off the stack so we can add properties to it 302 if ($component == null) { // If there's nothing on the stack 303 $component = $this; // use the iCalendar 304 } 305 306 if ($component->add_property($label, $data, $params) === false) { 307 $this->parser_error("Failed to add property '$label' on line $key"); 308 } 309 310 if($component != $this) { // If we're not using the iCalendar 311 array_push($components, $component); // Put the component back on the stack 312 } 313 unset($component); 314 } 315 316 } 317 318 } 319 320 function clear_errors() { 321 $this->parser_errors = array(); 322 } 323 324 function parser_error($error) { 325 $this->parser_errors[] = $error; 326 } 327 328 } 329 330 class iCalendar extends iCalendar_component { 331 var $name = 'VCALENDAR'; 332 333 function __construct() { 334 $this->valid_properties = array( 335 'CALSCALE' => RFC2445_OPTIONAL | RFC2445_ONCE, 336 'METHOD' => RFC2445_OPTIONAL | RFC2445_ONCE, 337 'PRODID' => RFC2445_REQUIRED | RFC2445_ONCE, 338 'VERSION' => RFC2445_REQUIRED | RFC2445_ONCE, 339 RFC2445_XNAME => RFC2445_OPTIONAL 340 ); 341 342 $this->valid_components = array( 343 'VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VTIMEZONE', 'VALARM' 344 ); 345 parent::__construct(); 346 } 347 348 } 349 350 class iCalendar_event extends iCalendar_component { 351 352 var $name = 'VEVENT'; 353 var $properties; 354 355 function __construct() { 356 357 $this->valid_components = array('VALARM'); 358 359 $this->valid_properties = array( 360 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE, 361 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE, 362 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 363 // Standard ambiguous here: in 4.6.1 it says that DTSTAMP in optional, 364 // while in 4.8.7.2 it says it's REQUIRED. Go with REQUIRED. 365 'DTSTAMP' => RFC2445_REQUIRED | RFC2445_ONCE, 366 // Standard ambiguous here: in 4.6.1 it says that DTSTART in optional, 367 // while in 4.8.2.4 it says it's REQUIRED. Go with REQUIRED. 368 'DTSTART' => RFC2445_REQUIRED | RFC2445_ONCE, 369 'GEO' => RFC2445_OPTIONAL | RFC2445_ONCE, 370 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 371 'LOCATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 372 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 373 'PRIORITY' => RFC2445_OPTIONAL | RFC2445_ONCE, 374 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE, 375 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE, 376 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 377 'TRANSP' => RFC2445_OPTIONAL | RFC2445_ONCE, 378 // Standard ambiguous here: in 4.6.1 it says that UID in optional, 379 // while in 4.8.4.7 it says it's REQUIRED. Go with REQUIRED. 380 'UID' => RFC2445_REQUIRED | RFC2445_ONCE, 381 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 382 'RECURRENCE-ID' => RFC2445_OPTIONAL | RFC2445_ONCE, 383 'DTEND' => RFC2445_OPTIONAL | RFC2445_ONCE, 384 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 385 'ATTACH' => RFC2445_OPTIONAL, 386 'ATTENDEE' => RFC2445_OPTIONAL, 387 'CATEGORIES' => RFC2445_OPTIONAL, 388 'COMMENT' => RFC2445_OPTIONAL, 389 'CONTACT' => RFC2445_OPTIONAL, 390 'EXDATE' => RFC2445_OPTIONAL, 391 'EXRULE' => RFC2445_OPTIONAL, 392 'REQUEST-STATUS' => RFC2445_OPTIONAL, 393 'RELATED-TO' => RFC2445_OPTIONAL, 394 'RESOURCES' => RFC2445_OPTIONAL, 395 'RDATE' => RFC2445_OPTIONAL, 396 'RRULE' => RFC2445_OPTIONAL, 397 RFC2445_XNAME => RFC2445_OPTIONAL 398 ); 399 400 parent::__construct(); 401 } 402 403 function invariant_holds() { 404 // DTEND and DURATION must not appear together 405 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) { 406 return false; 407 } 408 409 410 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) { 411 // DTEND must be later than DTSTART 412 // The standard is not clear on how to hande different value types though 413 // TODO: handle this correctly even if the value types are different 414 if($this->properties['DTEND'][0]->value < $this->properties['DTSTART'][0]->value) { 415 return false; 416 } 417 418 // DTEND and DTSTART must have the same value type 419 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) { 420 return false; 421 } 422 423 } 424 return true; 425 } 426 427 } 428 429 class iCalendar_todo extends iCalendar_component { 430 var $name = 'VTODO'; 431 var $properties; 432 433 function __construct() { 434 435 $this->valid_components = array('VALARM'); 436 437 $this->valid_properties = array( 438 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE, 439 'COMPLETED' => RFC2445_OPTIONAL | RFC2445_ONCE, 440 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE, 441 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 442 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE, 443 'DTSTAP' => RFC2445_OPTIONAL | RFC2445_ONCE, 444 'GEO' => RFC2445_OPTIONAL | RFC2445_ONCE, 445 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 446 'LOCATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 447 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 448 'PERCENT' => RFC2445_OPTIONAL | RFC2445_ONCE, 449 'PRIORITY' => RFC2445_OPTIONAL | RFC2445_ONCE, 450 'RECURID' => RFC2445_OPTIONAL | RFC2445_ONCE, 451 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE, 452 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE, 453 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 454 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE, 455 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 456 'DUE' => RFC2445_OPTIONAL | RFC2445_ONCE, 457 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 458 'ATTACH' => RFC2445_OPTIONAL, 459 'ATTENDEE' => RFC2445_OPTIONAL, 460 'CATEGORIES' => RFC2445_OPTIONAL, 461 'COMMENT' => RFC2445_OPTIONAL, 462 'CONTACT' => RFC2445_OPTIONAL, 463 'EXDATE' => RFC2445_OPTIONAL, 464 'EXRULE' => RFC2445_OPTIONAL, 465 'RSTATUS' => RFC2445_OPTIONAL, 466 'RELATED' => RFC2445_OPTIONAL, 467 'RESOURCES' => RFC2445_OPTIONAL, 468 'RDATE' => RFC2445_OPTIONAL, 469 'RRULE' => RFC2445_OPTIONAL, 470 RFC2445_XNAME => RFC2445_OPTIONAL 471 ); 472 473 parent::__construct(); 474 } 475 476 function invariant_holds() { 477 // DTEND and DURATION must not appear together 478 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) { 479 return false; 480 } 481 482 483 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) { 484 // DTEND must be later than DTSTART 485 // The standard is not clear on how to hande different value types though 486 // TODO: handle this correctly even if the value types are different 487 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) { 488 return false; 489 } 490 491 // DTEND and DTSTART must have the same value type 492 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) { 493 return false; 494 } 495 496 } 497 498 if(isset($this->properties['DUE']) && isset($this->properties['DTSTART'])) { 499 if($this->properties['DUE'][0]->value <= $this->properties['DTSTART'][0]->value) { 500 return false; 501 } 502 } 503 504 return true; 505 } 506 507 } 508 509 class iCalendar_journal extends iCalendar_component { 510 var $name = 'VJOURNAL'; 511 var $properties; 512 513 function __construct() { 514 515 $this->valid_properties = array( 516 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE, 517 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE, 518 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 519 'DTSTART' => RFC2445_OPTIONAL | RFC2445_ONCE, 520 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE, 521 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 522 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 523 'RECURRANCE-ID' => RFC2445_OPTIONAL | RFC2445_ONCE, 524 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE, 525 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE, 526 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 527 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE, 528 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 529 'ATTACH' => RFC2445_OPTIONAL, 530 'ATTENDEE' => RFC2445_OPTIONAL, 531 'CATEGORIES' => RFC2445_OPTIONAL, 532 'COMMENT' => RFC2445_OPTIONAL, 533 'CONTACT' => RFC2445_OPTIONAL, 534 'EXDATE' => RFC2445_OPTIONAL, 535 'EXRULE' => RFC2445_OPTIONAL, 536 'RELATED-TO' => RFC2445_OPTIONAL, 537 'RDATE' => RFC2445_OPTIONAL, 538 'RRULE' => RFC2445_OPTIONAL, 539 RFC2445_XNAME => RFC2445_OPTIONAL 540 ); 541 542 parent::__construct(); 543 544 } 545 } 546 547 class iCalendar_freebusy extends iCalendar_component { 548 var $name = 'VFREEBUSY'; 549 var $properties; 550 551 function __construct() { 552 $this->valid_components = array(); 553 $this->valid_properties = array( 554 'CONTACT' => RFC2445_OPTIONAL | RFC2445_ONCE, 555 'DTSTART' => RFC2445_OPTIONAL | RFC2445_ONCE, 556 'DTEND' => RFC2445_OPTIONAL | RFC2445_ONCE, 557 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 558 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE, 559 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 560 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE, 561 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 562 // TODO: the next two are components of their own! 563 'ATTENDEE' => RFC2445_OPTIONAL, 564 'COMMENT' => RFC2445_OPTIONAL, 565 'FREEBUSY' => RFC2445_OPTIONAL, 566 'RSTATUS' => RFC2445_OPTIONAL, 567 RFC2445_XNAME => RFC2445_OPTIONAL 568 ); 569 570 parent::__construct(); 571 } 572 573 function invariant_holds() { 574 // DTEND and DURATION must not appear together 575 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) { 576 return false; 577 } 578 579 580 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) { 581 // DTEND must be later than DTSTART 582 // The standard is not clear on how to hande different value types though 583 // TODO: handle this correctly even if the value types are different 584 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) { 585 return false; 586 } 587 588 // DTEND and DTSTART must have the same value type 589 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) { 590 return false; 591 } 592 593 } 594 return true; 595 } 596 } 597 598 class iCalendar_alarm extends iCalendar_component { 599 var $name = 'VALARM'; 600 var $properties; 601 602 function __construct() { 603 $this->valid_components = array(); 604 $this->valid_properties = array( 605 'ACTION' => RFC2445_REQUIRED | RFC2445_ONCE, 606 'TRIGGER' => RFC2445_REQUIRED | RFC2445_ONCE, 607 // If one of these 2 occurs, so must the other. 608 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 609 'REPEAT' => RFC2445_OPTIONAL | RFC2445_ONCE, 610 // The following is required if action == "PROCEDURE" | "AUDIO" 611 'ATTACH' => RFC2445_OPTIONAL, 612 // The following is required if trigger == "EMAIL" | "DISPLAY" 613 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 614 // The following are required if action == "EMAIL" 615 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 616 'ATTENDEE' => RFC2445_OPTIONAL, 617 RFC2445_XNAME => RFC2445_OPTIONAL 618 ); 619 620 parent::__construct(); 621 } 622 623 function invariant_holds() { 624 // DTEND and DURATION must not appear together 625 if(isset($this->properties['ACTION'])) { 626 switch ($this->properties['ACTION'][0]->value) { 627 case 'AUDIO': 628 if (!isset($this->properties['ATTACH'])) { 629 return false; 630 } 631 break; 632 case 'DISPLAY': 633 if (!isset($this->properties['DESCRIPTION'])) { 634 return false; 635 } 636 break; 637 case 'EMAIL': 638 if (!isset($this->properties['DESCRIPTION']) || !isset($this->properties['SUMMARY']) || !isset($this->properties['ATTACH'])) { 639 return false; 640 } 641 break; 642 case 'PROCEDURE': 643 if (!isset($this->properties['ATTACH']) || count($this->properties['ATTACH']) > 1) { 644 return false; 645 } 646 break; 647 } 648 } 649 return true; 650 } 651 652 653 } 654 655 class iCalendar_timezone extends iCalendar_component { 656 var $name = 'VTIMEZONE'; 657 var $properties; 658 659 function __construct() { 660 661 $this->valid_components = array('STANDARD', 'DAYLIGHT'); 662 663 $this->valid_properties = array( 664 'TZID' => RFC2445_REQUIRED | RFC2445_ONCE, 665 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 666 'TZURL' => RFC2445_OPTIONAL | RFC2445_ONCE, 667 RFC2445_XNAME => RFC2445_OPTIONAL 668 ); 669 670 parent::__construct(); 671 } 672 673 } 674 675 class iCalendar_standard extends iCalendar_component { 676 var $name = 'STANDARD'; 677 var $properties; 678 679 function __construct() { 680 $this->valid_components = array(); 681 $this->valid_properties = array( 682 'DTSTART' => RFC2445_REQUIRED | RFC2445_ONCE, 683 'TZOFFSETTO' => RFC2445_REQUIRED | RFC2445_ONCE, 684 'TZOFFSETFROM' => RFC2445_REQUIRED | RFC2445_ONCE, 685 'COMMENT' => RFC2445_OPTIONAL, 686 'RDATE' => RFC2445_OPTIONAL, 687 'RRULE' => RFC2445_OPTIONAL, 688 'TZNAME' => RFC2445_OPTIONAL, 689 'TZURL' => RFC2445_OPTIONAL, 690 RFC2445_XNAME => RFC2445_OPTIONAL, 691 ); 692 parent::__construct(); 693 } 694 } 695 696 class iCalendar_daylight extends iCalendar_standard { 697 var $name = 'DAYLIGHT'; 698 } 699 700 // REMINDER: DTEND must be later than DTSTART for all components which support both 701 // REMINDER: DUE must be later than DTSTART for all components which support both 702
title
Description
Body
title
Description
Body
title
Description
Body
title
Body