See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 $cleanedparams = []; 307 // Some parameter values are wrapped by DQUOTE character. 308 // We need to go through and get the actual value inside the quoted string. 309 foreach ($params as $param => $value) { 310 if (preg_match('#"(?P<actualvalue>[^"]*?)"#', $value, $matches)) { 311 $cleanedparams[$param] = $matches['actualvalue']; 312 } else { 313 $cleanedparams[$param] = $value; 314 } 315 } 316 $params = $cleanedparams; 317 318 if ($component->add_property($label, $data, $params) === false) { 319 $this->parser_error("Failed to add property '$label' on line $key"); 320 } 321 322 if($component != $this) { // If we're not using the iCalendar 323 array_push($components, $component); // Put the component back on the stack 324 } 325 unset($component); 326 } 327 328 } 329 330 } 331 332 function clear_errors() { 333 $this->parser_errors = array(); 334 } 335 336 function parser_error($error) { 337 $this->parser_errors[] = $error; 338 } 339 340 } 341 342 class iCalendar extends iCalendar_component { 343 var $name = 'VCALENDAR'; 344 345 function __construct() { 346 $this->valid_properties = array( 347 'CALSCALE' => RFC2445_OPTIONAL | RFC2445_ONCE, 348 'METHOD' => RFC2445_OPTIONAL | RFC2445_ONCE, 349 'PRODID' => RFC2445_REQUIRED | RFC2445_ONCE, 350 'VERSION' => RFC2445_REQUIRED | RFC2445_ONCE, 351 RFC2445_XNAME => RFC2445_OPTIONAL 352 ); 353 354 $this->valid_components = array( 355 'VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VTIMEZONE', 'VALARM' 356 ); 357 parent::__construct(); 358 } 359 360 } 361 362 class iCalendar_event extends iCalendar_component { 363 364 var $name = 'VEVENT'; 365 var $properties; 366 367 function __construct() { 368 369 $this->valid_components = array('VALARM'); 370 371 $this->valid_properties = array( 372 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE, 373 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE, 374 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 375 // Standard ambiguous here: in 4.6.1 it says that DTSTAMP in optional, 376 // while in 4.8.7.2 it says it's REQUIRED. Go with REQUIRED. 377 'DTSTAMP' => RFC2445_REQUIRED | RFC2445_ONCE, 378 // Standard ambiguous here: in 4.6.1 it says that DTSTART in optional, 379 // while in 4.8.2.4 it says it's REQUIRED. Go with REQUIRED. 380 'DTSTART' => RFC2445_REQUIRED | RFC2445_ONCE, 381 'GEO' => RFC2445_OPTIONAL | RFC2445_ONCE, 382 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 383 'LOCATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 384 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 385 'PRIORITY' => RFC2445_OPTIONAL | RFC2445_ONCE, 386 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE, 387 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE, 388 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 389 'TRANSP' => RFC2445_OPTIONAL | RFC2445_ONCE, 390 // Standard ambiguous here: in 4.6.1 it says that UID in optional, 391 // while in 4.8.4.7 it says it's REQUIRED. Go with REQUIRED. 392 'UID' => RFC2445_REQUIRED | RFC2445_ONCE, 393 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 394 'RECURRENCE-ID' => RFC2445_OPTIONAL | RFC2445_ONCE, 395 'DTEND' => RFC2445_OPTIONAL | RFC2445_ONCE, 396 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 397 'ATTACH' => RFC2445_OPTIONAL, 398 'ATTENDEE' => RFC2445_OPTIONAL, 399 'CATEGORIES' => RFC2445_OPTIONAL, 400 'COMMENT' => RFC2445_OPTIONAL, 401 'CONTACT' => RFC2445_OPTIONAL, 402 'EXDATE' => RFC2445_OPTIONAL, 403 'EXRULE' => RFC2445_OPTIONAL, 404 'REQUEST-STATUS' => RFC2445_OPTIONAL, 405 'RELATED-TO' => RFC2445_OPTIONAL, 406 'RESOURCES' => RFC2445_OPTIONAL, 407 'RDATE' => RFC2445_OPTIONAL, 408 'RRULE' => RFC2445_OPTIONAL, 409 RFC2445_XNAME => RFC2445_OPTIONAL 410 ); 411 412 parent::__construct(); 413 } 414 415 function invariant_holds() { 416 // DTEND and DURATION must not appear together 417 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) { 418 return false; 419 } 420 421 422 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) { 423 // DTEND must be later than DTSTART 424 // The standard is not clear on how to hande different value types though 425 // TODO: handle this correctly even if the value types are different 426 if($this->properties['DTEND'][0]->value < $this->properties['DTSTART'][0]->value) { 427 return false; 428 } 429 430 // DTEND and DTSTART must have the same value type 431 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) { 432 return false; 433 } 434 435 } 436 return true; 437 } 438 439 } 440 441 class iCalendar_todo extends iCalendar_component { 442 var $name = 'VTODO'; 443 var $properties; 444 445 function __construct() { 446 447 $this->valid_components = array('VALARM'); 448 449 $this->valid_properties = array( 450 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE, 451 'COMPLETED' => RFC2445_OPTIONAL | RFC2445_ONCE, 452 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE, 453 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 454 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE, 455 'DTSTAP' => RFC2445_OPTIONAL | RFC2445_ONCE, 456 'GEO' => RFC2445_OPTIONAL | RFC2445_ONCE, 457 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 458 'LOCATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 459 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 460 'PERCENT' => RFC2445_OPTIONAL | RFC2445_ONCE, 461 'PRIORITY' => RFC2445_OPTIONAL | RFC2445_ONCE, 462 'RECURID' => RFC2445_OPTIONAL | RFC2445_ONCE, 463 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE, 464 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE, 465 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 466 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE, 467 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 468 'DUE' => RFC2445_OPTIONAL | RFC2445_ONCE, 469 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 470 'ATTACH' => RFC2445_OPTIONAL, 471 'ATTENDEE' => RFC2445_OPTIONAL, 472 'CATEGORIES' => RFC2445_OPTIONAL, 473 'COMMENT' => RFC2445_OPTIONAL, 474 'CONTACT' => RFC2445_OPTIONAL, 475 'EXDATE' => RFC2445_OPTIONAL, 476 'EXRULE' => RFC2445_OPTIONAL, 477 'RSTATUS' => RFC2445_OPTIONAL, 478 'RELATED' => RFC2445_OPTIONAL, 479 'RESOURCES' => RFC2445_OPTIONAL, 480 'RDATE' => RFC2445_OPTIONAL, 481 'RRULE' => RFC2445_OPTIONAL, 482 RFC2445_XNAME => RFC2445_OPTIONAL 483 ); 484 485 parent::__construct(); 486 } 487 488 function invariant_holds() { 489 // DTEND and DURATION must not appear together 490 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) { 491 return false; 492 } 493 494 495 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) { 496 // DTEND must be later than DTSTART 497 // The standard is not clear on how to hande different value types though 498 // TODO: handle this correctly even if the value types are different 499 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) { 500 return false; 501 } 502 503 // DTEND and DTSTART must have the same value type 504 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) { 505 return false; 506 } 507 508 } 509 510 if(isset($this->properties['DUE']) && isset($this->properties['DTSTART'])) { 511 if($this->properties['DUE'][0]->value <= $this->properties['DTSTART'][0]->value) { 512 return false; 513 } 514 } 515 516 return true; 517 } 518 519 } 520 521 class iCalendar_journal extends iCalendar_component { 522 var $name = 'VJOURNAL'; 523 var $properties; 524 525 function __construct() { 526 527 $this->valid_properties = array( 528 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE, 529 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE, 530 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 531 'DTSTART' => RFC2445_OPTIONAL | RFC2445_ONCE, 532 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE, 533 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 534 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 535 'RECURRANCE-ID' => RFC2445_OPTIONAL | RFC2445_ONCE, 536 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE, 537 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE, 538 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 539 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE, 540 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 541 'ATTACH' => RFC2445_OPTIONAL, 542 'ATTENDEE' => RFC2445_OPTIONAL, 543 'CATEGORIES' => RFC2445_OPTIONAL, 544 'COMMENT' => RFC2445_OPTIONAL, 545 'CONTACT' => RFC2445_OPTIONAL, 546 'EXDATE' => RFC2445_OPTIONAL, 547 'EXRULE' => RFC2445_OPTIONAL, 548 'RELATED-TO' => RFC2445_OPTIONAL, 549 'RDATE' => RFC2445_OPTIONAL, 550 'RRULE' => RFC2445_OPTIONAL, 551 RFC2445_XNAME => RFC2445_OPTIONAL 552 ); 553 554 parent::__construct(); 555 556 } 557 } 558 559 class iCalendar_freebusy extends iCalendar_component { 560 var $name = 'VFREEBUSY'; 561 var $properties; 562 563 function __construct() { 564 $this->valid_components = array(); 565 $this->valid_properties = array( 566 'CONTACT' => RFC2445_OPTIONAL | RFC2445_ONCE, 567 'DTSTART' => RFC2445_OPTIONAL | RFC2445_ONCE, 568 'DTEND' => RFC2445_OPTIONAL | RFC2445_ONCE, 569 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 570 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE, 571 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE, 572 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE, 573 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE, 574 // TODO: the next two are components of their own! 575 'ATTENDEE' => RFC2445_OPTIONAL, 576 'COMMENT' => RFC2445_OPTIONAL, 577 'FREEBUSY' => RFC2445_OPTIONAL, 578 'RSTATUS' => RFC2445_OPTIONAL, 579 RFC2445_XNAME => RFC2445_OPTIONAL 580 ); 581 582 parent::__construct(); 583 } 584 585 function invariant_holds() { 586 // DTEND and DURATION must not appear together 587 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) { 588 return false; 589 } 590 591 592 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) { 593 // DTEND must be later than DTSTART 594 // The standard is not clear on how to hande different value types though 595 // TODO: handle this correctly even if the value types are different 596 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) { 597 return false; 598 } 599 600 // DTEND and DTSTART must have the same value type 601 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) { 602 return false; 603 } 604 605 } 606 return true; 607 } 608 } 609 610 class iCalendar_alarm extends iCalendar_component { 611 var $name = 'VALARM'; 612 var $properties; 613 614 function __construct() { 615 $this->valid_components = array(); 616 $this->valid_properties = array( 617 'ACTION' => RFC2445_REQUIRED | RFC2445_ONCE, 618 'TRIGGER' => RFC2445_REQUIRED | RFC2445_ONCE, 619 // If one of these 2 occurs, so must the other. 620 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE, 621 'REPEAT' => RFC2445_OPTIONAL | RFC2445_ONCE, 622 // The following is required if action == "PROCEDURE" | "AUDIO" 623 'ATTACH' => RFC2445_OPTIONAL, 624 // The following is required if trigger == "EMAIL" | "DISPLAY" 625 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE, 626 // The following are required if action == "EMAIL" 627 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE, 628 'ATTENDEE' => RFC2445_OPTIONAL, 629 RFC2445_XNAME => RFC2445_OPTIONAL 630 ); 631 632 parent::__construct(); 633 } 634 635 function invariant_holds() { 636 // DTEND and DURATION must not appear together 637 if(isset($this->properties['ACTION'])) { 638 switch ($this->properties['ACTION'][0]->value) { 639 case 'AUDIO': 640 if (!isset($this->properties['ATTACH'])) { 641 return false; 642 } 643 break; 644 case 'DISPLAY': 645 if (!isset($this->properties['DESCRIPTION'])) { 646 return false; 647 } 648 break; 649 case 'EMAIL': 650 if (!isset($this->properties['DESCRIPTION']) || !isset($this->properties['SUMMARY']) || !isset($this->properties['ATTACH'])) { 651 return false; 652 } 653 break; 654 case 'PROCEDURE': 655 if (!isset($this->properties['ATTACH']) || count($this->properties['ATTACH']) > 1) { 656 return false; 657 } 658 break; 659 } 660 } 661 return true; 662 } 663 664 665 } 666 667 class iCalendar_timezone extends iCalendar_component { 668 var $name = 'VTIMEZONE'; 669 var $properties; 670 671 function __construct() { 672 673 $this->valid_components = array('STANDARD', 'DAYLIGHT'); 674 675 $this->valid_properties = array( 676 'TZID' => RFC2445_REQUIRED | RFC2445_ONCE, 677 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE, 678 'TZURL' => RFC2445_OPTIONAL | RFC2445_ONCE, 679 RFC2445_XNAME => RFC2445_OPTIONAL 680 ); 681 682 parent::__construct(); 683 } 684 685 } 686 687 class iCalendar_standard extends iCalendar_component { 688 var $name = 'STANDARD'; 689 var $properties; 690 691 function __construct() { 692 $this->valid_components = array(); 693 $this->valid_properties = array( 694 'DTSTART' => RFC2445_REQUIRED | RFC2445_ONCE, 695 'TZOFFSETTO' => RFC2445_REQUIRED | RFC2445_ONCE, 696 'TZOFFSETFROM' => RFC2445_REQUIRED | RFC2445_ONCE, 697 'COMMENT' => RFC2445_OPTIONAL, 698 'RDATE' => RFC2445_OPTIONAL, 699 'RRULE' => RFC2445_OPTIONAL, 700 'TZNAME' => RFC2445_OPTIONAL, 701 'TZURL' => RFC2445_OPTIONAL, 702 RFC2445_XNAME => RFC2445_OPTIONAL, 703 ); 704 parent::__construct(); 705 } 706 } 707 708 class iCalendar_daylight extends iCalendar_standard { 709 var $name = 'DAYLIGHT'; 710 } 711 712 // REMINDER: DTEND must be later than DTSTART for all components which support both 713 // REMINDER: DUE must be later than DTSTART for all components which support both 714
title
Description
Body
title
Description
Body
title
Description
Body
title
Body