MDL-22308 Update lib/bennu to latest upstream.
[moodle.git] / lib / bennu / iCalendar_components.php
CommitLineData
32e11709 1<?php
f3b6ac15 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
f3b6ac15 12 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
13 */
14
15class iCalendar_component {
16 var $name = NULL;
17 var $properties = NULL;
18 var $components = NULL;
19 var $valid_properties = NULL;
20 var $valid_components = NULL;
6e9ce8e7
JH
21 /**
22 * Added to hold errors from last run of unserialize
23 * @var $parser_errors array
24 */
25 var $parser_errors = NULL;
f3b6ac15 26
6e9ce8e7 27 function __construct() {
f3b6ac15 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
6e9ce8e7
JH
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
f3b6ac15 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 }
6e9ce8e7
JH
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 = explode(RFC2445_CRLF, $string); // 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 // Divide the line up into label, parameters and data fields.
241 if (!preg_match('#^(?P<label>[-[:alnum:]]+)(?P<params>(?:;(?:(?:[-[:alnum:]]+)=(?:[^[:cntrl:]";:,]+|"[^[:cntrl:]"]+")))*):(?P<data>.*)$#', $line, $match)) {
242 $this->parser_error('Invalid line: '.$key.', ignoring');
243 continue;
244 }
245
246 // parse parameters
247 $params = array();
248 if (preg_match_all('#;(?P<param>[-[:alnum:]]+)=(?P<value>[^[:cntrl:]";:,]+|"[^[:cntrl:]"]+")#', $match['params'], $pmatch)) {
249 $params = array_combine($pmatch['param'], $pmatch['value']);
250 }
251 $label = $match['label'];
252 $data = $match['data'];
253 unset($match, $pmatch);
254
255 if ($label == 'BEGIN') {
256 // This is the start of a component.
257 $current_component = array_pop($components); // Get the current component off the stack so we can check its valid components
258 if ($current_component == null) { // If there's nothing on the stack
259 $current_component = $this; // use the iCalendar
260 }
261 if (in_array($data, $current_component->valid_components)) { // Check that the new component is a valid subcomponent of the current one
262 if($current_component != $this) {
263 array_push($components, $current_component); // We're done with the current component, put it back on the stack.
264 }
265 if(strpos($data, 'V') === 0) {
266 $data = substr($data, 1);
267 }
268 $cname = 'iCalendar_' . strtolower($data);
269 $new_component = new $cname;
270 array_push($components, $new_component); // Push a new component onto the stack
271 } else {
272 if($current_component != $this) {
273 array_push($components, $current_component);
274 $this->parser_error('Invalid component type on line '.$key);
275 }
276 }
277 unset($current_component, $new_component);
278 } else if ($label == 'END') {
279 // It's the END of a component.
280 $component = array_pop($components); // Pop the top component off the stack - we're now done with it
281 $parent_component = array_pop($components); // Pop the component's conatining component off the stack so we can add this component to it.
282 if($parent_component == null) {
283 $parent_component = $this; // If there's no components on the stack, use the iCalendar object
284 }
285 if ($parent_component->add_component($component) === false) {
286 $this->parser_error("Failed to add component on line $key");
287 }
288 if ($parent_component != $this) { // If we're not using the iCalendar
289 array_push($components, $parent_component); // Put the component back on the stack
290 }
291 unset($parent_component, $component);
292 } else {
293
294 $component = array_pop($components); // Get the component off the stack so we can add properties to it
295 if ($component == null) { // If there's nothing on the stack
296 $component = $this; // use the iCalendar
297 }
298
299 if ($component->add_property($label, $data, $params) === false) {
300 $this->parser_error("Failed to add property '$label' on line $key");
301 }
302
303 if($component != $this) { // If we're not using the iCalendar
304 array_push($components, $component); // Put the component back on the stack
305 }
306 unset($component);
307 }
308
309 }
310
311 }
312
313 function clear_errors() {
314 $this->parser_errors = array();
315 }
316
317 function parser_error($error) {
318 $this->parser_errors[] = $error;
319 }
f3b6ac15 320
321}
322
323class iCalendar extends iCalendar_component {
324 var $name = 'VCALENDAR';
325
6e9ce8e7 326 function __construct() {
f3b6ac15 327 $this->valid_properties = array(
328 'CALSCALE' => RFC2445_OPTIONAL | RFC2445_ONCE,
329 'METHOD' => RFC2445_OPTIONAL | RFC2445_ONCE,
330 'PRODID' => RFC2445_REQUIRED | RFC2445_ONCE,
331 'VERSION' => RFC2445_REQUIRED | RFC2445_ONCE,
332 RFC2445_XNAME => RFC2445_OPTIONAL
333 );
334
335 $this->valid_components = array(
6e9ce8e7 336 'VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VTIMEZONE', 'VALARM'
f3b6ac15 337 );
6e9ce8e7 338 parent::__construct();
f3b6ac15 339 }
340
341}
342
343class iCalendar_event extends iCalendar_component {
344
345 var $name = 'VEVENT';
346 var $properties;
347
6e9ce8e7 348 function __construct() {
f3b6ac15 349
350 $this->valid_components = array('VALARM');
351
352 $this->valid_properties = array(
353 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE,
354 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
355 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE,
356 // Standard ambiguous here: in 4.6.1 it says that DTSTAMP in optional,
357 // while in 4.8.7.2 it says it's REQUIRED. Go with REQUIRED.
358 'DTSTAMP' => RFC2445_REQUIRED | RFC2445_ONCE,
359 // Standard ambiguous here: in 4.6.1 it says that DTSTART in optional,
360 // while in 4.8.2.4 it says it's REQUIRED. Go with REQUIRED.
361 'DTSTART' => RFC2445_REQUIRED | RFC2445_ONCE,
362 'GEO' => RFC2445_OPTIONAL | RFC2445_ONCE,
363 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE,
364 'LOCATION' => RFC2445_OPTIONAL | RFC2445_ONCE,
365 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE,
366 'PRIORITY' => RFC2445_OPTIONAL | RFC2445_ONCE,
367 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE,
368 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE,
369 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE,
370 'TRANSP' => RFC2445_OPTIONAL | RFC2445_ONCE,
371 // Standard ambiguous here: in 4.6.1 it says that UID in optional,
372 // while in 4.8.4.7 it says it's REQUIRED. Go with REQUIRED.
373 'UID' => RFC2445_REQUIRED | RFC2445_ONCE,
374 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE,
375 'RECURRENCE-ID' => RFC2445_OPTIONAL | RFC2445_ONCE,
376 'DTEND' => RFC2445_OPTIONAL | RFC2445_ONCE,
377 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE,
378 'ATTACH' => RFC2445_OPTIONAL,
379 'ATTENDEE' => RFC2445_OPTIONAL,
380 'CATEGORIES' => RFC2445_OPTIONAL,
381 'COMMENT' => RFC2445_OPTIONAL,
382 'CONTACT' => RFC2445_OPTIONAL,
383 'EXDATE' => RFC2445_OPTIONAL,
384 'EXRULE' => RFC2445_OPTIONAL,
385 'REQUEST-STATUS' => RFC2445_OPTIONAL,
386 'RELATED-TO' => RFC2445_OPTIONAL,
387 'RESOURCES' => RFC2445_OPTIONAL,
388 'RDATE' => RFC2445_OPTIONAL,
389 'RRULE' => RFC2445_OPTIONAL,
390 RFC2445_XNAME => RFC2445_OPTIONAL
391 );
392
6e9ce8e7 393 parent::__construct();
f3b6ac15 394 }
395
396 function invariant_holds() {
397 // DTEND and DURATION must not appear together
398 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) {
399 return false;
400 }
401
402
403 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) {
404 // DTEND must be later than DTSTART
405 // The standard is not clear on how to hande different value types though
406 // TODO: handle this correctly even if the value types are different
407 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) {
408 return false;
409 }
410
411 // DTEND and DTSTART must have the same value type
412 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) {
413 return false;
414 }
415
416 }
417 return true;
418 }
419
420}
421
422class iCalendar_todo extends iCalendar_component {
423 var $name = 'VTODO';
424 var $properties;
425
6e9ce8e7
JH
426 function __construct() {
427
428 $this->valid_components = array('VALARM');
429
430 $this->valid_properties = array(
431 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE,
432 'COMPLETED' => RFC2445_OPTIONAL | RFC2445_ONCE,
433 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
434 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE,
435 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE,
436 'DTSTAP' => RFC2445_OPTIONAL | RFC2445_ONCE,
437 'GEO' => RFC2445_OPTIONAL | RFC2445_ONCE,
438 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE,
439 'LOCATION' => RFC2445_OPTIONAL | RFC2445_ONCE,
440 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE,
441 'PERCENT' => RFC2445_OPTIONAL | RFC2445_ONCE,
442 'PRIORITY' => RFC2445_OPTIONAL | RFC2445_ONCE,
443 'RECURID' => RFC2445_OPTIONAL | RFC2445_ONCE,
444 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE,
445 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE,
446 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE,
447 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE,
448 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE,
449 'DUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
450 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE,
451 'ATTACH' => RFC2445_OPTIONAL,
452 'ATTENDEE' => RFC2445_OPTIONAL,
453 'CATEGORIES' => RFC2445_OPTIONAL,
454 'COMMENT' => RFC2445_OPTIONAL,
455 'CONTACT' => RFC2445_OPTIONAL,
456 'EXDATE' => RFC2445_OPTIONAL,
457 'EXRULE' => RFC2445_OPTIONAL,
458 'RSTATUS' => RFC2445_OPTIONAL,
459 'RELATED' => RFC2445_OPTIONAL,
460 'RESOURCES' => RFC2445_OPTIONAL,
461 'RDATE' => RFC2445_OPTIONAL,
462 'RRULE' => RFC2445_OPTIONAL,
463 RFC2445_XNAME => RFC2445_OPTIONAL
f3b6ac15 464 );
465
6e9ce8e7 466 parent::__construct();
f3b6ac15 467 }
6e9ce8e7
JH
468
469 function invariant_holds() {
470 // DTEND and DURATION must not appear together
471 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) {
472 return false;
473 }
474
475
476 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) {
477 // DTEND must be later than DTSTART
478 // The standard is not clear on how to hande different value types though
479 // TODO: handle this correctly even if the value types are different
480 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) {
481 return false;
482 }
483
484 // DTEND and DTSTART must have the same value type
485 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) {
486 return false;
487 }
488
489 }
490
491 if(isset($this->properties['DUE']) && isset($this->properties['DTSTART'])) {
492 if($this->properties['DUE'][0]->value <= $this->properties['DTSTART'][0]->value) {
493 return false;
494 }
495 }
496
497 return true;
498 }
499
f3b6ac15 500}
501
502class iCalendar_journal extends iCalendar_component {
6e9ce8e7
JH
503 var $name = 'VJOURNAL';
504 var $properties;
505
506 function __construct() {
507
508 $this->valid_properties = array(
509 'CLASS' => RFC2445_OPTIONAL | RFC2445_ONCE,
510 'CREATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
511 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE,
512 'DTSTART' => RFC2445_OPTIONAL | RFC2445_ONCE,
513 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE,
514 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE,
515 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE,
516 'RECURRANCE-ID' => RFC2445_OPTIONAL | RFC2445_ONCE,
517 'SEQUENCE' => RFC2445_OPTIONAL | RFC2445_ONCE,
518 'STATUS' => RFC2445_OPTIONAL | RFC2445_ONCE,
519 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE,
520 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE,
521 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE,
522 'ATTACH' => RFC2445_OPTIONAL,
523 'ATTENDEE' => RFC2445_OPTIONAL,
524 'CATEGORIES' => RFC2445_OPTIONAL,
525 'COMMENT' => RFC2445_OPTIONAL,
526 'CONTACT' => RFC2445_OPTIONAL,
527 'EXDATE' => RFC2445_OPTIONAL,
528 'EXRULE' => RFC2445_OPTIONAL,
529 'RELATED-TO' => RFC2445_OPTIONAL,
530 'RDATE' => RFC2445_OPTIONAL,
531 'RRULE' => RFC2445_OPTIONAL,
532 RFC2445_XNAME => RFC2445_OPTIONAL
533 );
534
535 parent::__construct();
536
537 }
f3b6ac15 538}
539
540class iCalendar_freebusy extends iCalendar_component {
6e9ce8e7
JH
541 var $name = 'VFREEBUSY';
542 var $properties;
543
544 function __construct() {
545 $this->valid_components = array();
546 $this->valid_properties = array(
547 'CONTACT' => RFC2445_OPTIONAL | RFC2445_ONCE,
548 'DTSTART' => RFC2445_OPTIONAL | RFC2445_ONCE,
549 'DTEND' => RFC2445_OPTIONAL | RFC2445_ONCE,
550 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE,
551 'DTSTAMP' => RFC2445_OPTIONAL | RFC2445_ONCE,
552 'ORGANIZER' => RFC2445_OPTIONAL | RFC2445_ONCE,
553 'UID' => RFC2445_OPTIONAL | RFC2445_ONCE,
554 'URL' => RFC2445_OPTIONAL | RFC2445_ONCE,
555 // TODO: the next two are components of their own!
556 'ATTENDEE' => RFC2445_OPTIONAL,
557 'COMMENT' => RFC2445_OPTIONAL,
558 'FREEBUSY' => RFC2445_OPTIONAL,
559 'RSTATUS' => RFC2445_OPTIONAL,
560 RFC2445_XNAME => RFC2445_OPTIONAL
561 );
562
563 parent::__construct();
564 }
565
566 function invariant_holds() {
567 // DTEND and DURATION must not appear together
568 if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) {
569 return false;
570 }
571
572
573 if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) {
574 // DTEND must be later than DTSTART
575 // The standard is not clear on how to hande different value types though
576 // TODO: handle this correctly even if the value types are different
577 if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) {
578 return false;
579 }
580
581 // DTEND and DTSTART must have the same value type
582 if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) {
583 return false;
584 }
585
586 }
587 return true;
588 }
f3b6ac15 589}
590
591class iCalendar_alarm extends iCalendar_component {
6e9ce8e7
JH
592 var $name = 'VALARM';
593 var $properties;
594
595 function __construct() {
596 $this->valid_components = array();
597 $this->valid_properties = array(
598 'ACTION' => RFC2445_REQUIRED | RFC2445_ONCE,
599 'TRIGGER' => RFC2445_REQUIRED | RFC2445_ONCE,
600 // If one of these 2 occurs, so must the other.
601 'DURATION' => RFC2445_OPTIONAL | RFC2445_ONCE,
602 'REPEAT' => RFC2445_OPTIONAL | RFC2445_ONCE,
603 // The following is required if action == "PROCEDURE" | "AUDIO"
604 'ATTACH' => RFC2445_OPTIONAL,
605 // The following is required if trigger == "EMAIL" | "DISPLAY"
606 'DESCRIPTION' => RFC2445_OPTIONAL | RFC2445_ONCE,
607 // The following are required if action == "EMAIL"
608 'SUMMARY' => RFC2445_OPTIONAL | RFC2445_ONCE,
609 'ATTENDEE' => RFC2445_OPTIONAL,
610 RFC2445_XNAME => RFC2445_OPTIONAL
611 );
612
613 parent::__construct();
614 }
615
616 function invariant_holds() {
617 // DTEND and DURATION must not appear together
618 if(isset($this->properties['ACTION'])) {
619 switch ($this->properties['ACTION'][0]->value) {
620 case 'AUDIO':
621 if (!isset($this->properties['ATTACH'])) {
622 return false;
623 }
624 break;
625 case 'DISPLAY':
626 if (!isset($this->properties['DESCRIPTION'])) {
627 return false;
628 }
629 break;
630 case 'EMAIL':
631 if (!isset($this->properties['DESCRIPTION']) || !isset($this->properties['SUMMARY']) || !isset($this->properties['ATTACH'])) {
632 return false;
633 }
634 break;
635 case 'PROCEDURE':
636 if (!isset($this->properties['ATTACH']) || count($this->properties['ATTACH']) > 1) {
637 return false;
638 }
639 break;
640 }
641 }
642 return true;
643 }
644
645
f3b6ac15 646}
647
648class iCalendar_timezone extends iCalendar_component {
649 var $name = 'VTIMEZONE';
650 var $properties;
651
6e9ce8e7 652 function __construct() {
f3b6ac15 653
6e9ce8e7
JH
654 $this->valid_components = array('STANDARD', 'DAYLIGHT');
655
656 $this->valid_properties = array(
657 'TZID' => RFC2445_REQUIRED | RFC2445_ONCE,
658 'LAST-MODIFIED' => RFC2445_OPTIONAL | RFC2445_ONCE,
659 'TZURL' => RFC2445_OPTIONAL | RFC2445_ONCE,
660 RFC2445_XNAME => RFC2445_OPTIONAL
f3b6ac15 661 );
662
6e9ce8e7 663 parent::__construct();
f3b6ac15 664 }
665
666}
667
6e9ce8e7
JH
668class iCalendar_standard extends iCalendar_component {
669 var $name = 'STANDARD';
670 var $properties;
671
672 function __construct() {
673 $this->valid_components = array();
674 $this->valid_properties = array(
675 'DTSTART' => RFC2445_REQUIRED | RFC2445_ONCE,
676 'TZOFFSETTO' => RFC2445_REQUIRED | RFC2445_ONCE,
677 'TZOFFSETFROM' => RFC2445_REQUIRED | RFC2445_ONCE,
678 'COMMENT' => RFC2445_OPTIONAL,
679 'RDATE' => RFC2445_OPTIONAL,
680 'RRULE' => RFC2445_OPTIONAL,
681 'TZNAME' => RFC2445_OPTIONAL,
682 RFC2445_XNAME => RFC2445_OPTIONAL,
683 );
684 parent::__construct();
685 }
686}
687
688class iCalendar_daylight extends iCalendar_standard {
689 var $name = 'DAYLIGHT';
690}
691
f3b6ac15 692// REMINDER: DTEND must be later than DTSTART for all components which support both
693// REMINDER: DUE must be later than DTSTART for all components which support both
694