MDL-75012 js: Bump stylelint and components
[moodle.git] / lib / bennu / iCalendar_properties.php
blob15f916e3efa41d87e7b53481ff7a4d2537d2f359
1 <?php
3 /**
4 * BENNU - PHP iCalendar library
5 * (c) 2005-2006 Ioannis Papaioannou (pj@moodle.org). All rights reserved.
7 * Released under the LGPL.
9 * See http://bennu.sourceforge.net/ for more information and downloads.
11 * @author Ioannis Papaioannou
12 * @version $Id$
13 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
16 class iCalendar_property {
17 // Properties can have parameters, but cannot have other properties or components
19 var $parent_component = NULL;
20 var $value = NULL;
21 var $parameters = NULL;
22 var $valid_parameters = NULL;
24 // These are common for 95% of properties, so define them here and override as necessary
25 var $val_multi = false;
26 var $val_default = NULL;
28 function __construct() {
29 $this->parameters = array();
32 // If some property needs extra care with its parameters, override this
33 // IMPORTANT: the parameter name MUST BE CAPITALIZED!
34 function is_valid_parameter($parameter, $value) {
36 if(is_array($value)) {
37 if(!iCalendar_parameter::multiple_values_allowed($parameter)) {
38 return false;
40 foreach($value as $item) {
41 if(!iCalendar_parameter::is_valid_value($this, $parameter, $item)) {
42 return false;
45 return true;
48 return iCalendar_parameter::is_valid_value($this, $parameter, $value);
51 function invariant_holds() {
52 return true;
55 // If some property is very picky about its values, it should do the work itself
56 // Only data type validation is done here
57 function is_valid_value($value) {
58 if(is_array($value)) {
59 if(!$this->val_multi) {
60 return false;
62 else {
63 foreach($value as $oneval) {
64 if(!rfc2445_is_valid_value($oneval, $this->val_type)) {
65 return false;
69 return true;
71 return rfc2445_is_valid_value($value, $this->val_type);
74 function default_value() {
75 return $this->val_default;
78 function set_parent_component($componentname) {
79 if(class_exists('iCalendar_'.strtolower(substr($componentname, 1)))) {
80 $this->parent_component = strtoupper($componentname);
81 return true;
84 return false;
87 function set_value($value) {
88 if($this->is_valid_value($value)) {
89 // This transparently formats any value type according to the iCalendar specs
90 if(is_array($value)) {
91 foreach($value as $key => $item) {
92 $value[$key] = rfc2445_do_value_formatting($item, $this->val_type);
94 $this->value = implode(',', $value);
96 else {
97 $this->value = rfc2445_do_value_formatting($value, $this->val_type);
100 return true;
102 return false;
105 function get_value() {
106 // First of all, assume that we have multiple values
107 $valarray = explode('\\,', $this->value);
109 // Undo transparent formatting
110 $valtype = $this->val_type;
111 $replace_function = function($a) use ($valtype) {
112 return rfc2445_undo_value_formatting($a, $valtype);
114 $valarray = array_map($replace_function, $valarray);
116 // Now, if this property cannot have multiple values, don't return as an array
117 if(!$this->val_multi) {
118 return $valarray[0];
121 // Otherwise return an array even if it has one element, for uniformity
122 return $valarray;
126 function set_parameter($name, $value) {
128 // Uppercase
129 $name = strtoupper($name);
131 // Are we trying to add a valid parameter?
132 $xname = false;
133 if(!isset($this->valid_parameters[$name])) {
134 // If not, is it an x-name as per RFC 2445?
135 if(!rfc2445_is_xname($name)) {
136 return false;
138 // No more checks -- all components are supposed to allow x-name parameters
139 $xname = true;
142 if(!$this->is_valid_parameter($name, $value)) {
143 return false;
146 if(is_array($value)) {
147 foreach($value as $key => $element) {
148 $value[$key] = iCalendar_parameter::do_value_formatting($name, $element);
151 else {
152 $value = iCalendar_parameter::do_value_formatting($name, $value);
155 $this->parameters[$name] = $value;
157 // Special case: if we just changed the VALUE parameter, reflect this
158 // in the object's status so that it only accepts correct type values
159 if($name == 'VALUE') {
160 // TODO: what if this invalidates an already-set value?
161 $this->val_type = constant('RFC2445_TYPE_'.str_replace('-', '_', $value));
164 return true;
168 function get_parameter($name) {
170 // Uppercase
171 $name = strtoupper($name);
173 if(isset($this->parameters[$name])) {
174 // If there are any double quotes in the value, invisibly strip them
175 if(is_array($this->parameters[$name])) {
176 foreach($this->parameters[$name] as $key => $value) {
177 if(substr($value, 0, 1) == '"') {
178 $this->parameters[$name][$key] = substr($value, 1, strlen($value) - 2);
181 return $this->parameters[$name];
184 else {
185 if(substr($this->parameters[$name], 0, 1) == '"') {
186 return substr($this->parameters[$name], 1, strlen($this->parameters[$name]) - 2);
191 return NULL;
194 function serialize() {
195 $string = $this->name;
197 if(!empty($this->parameters)) {
198 foreach($this->parameters as $name => $value) {
199 $string .= ';'.$name.'=';
200 if(is_array($value)) {
201 $string .= implode(',', $value);
203 else {
204 $string .= $value;
209 $string .= ':'.$this->value;
211 return rfc2445_fold($string) . RFC2445_CRLF;
215 // 4.7 Calendar Properties
216 // -----------------------
218 class iCalendar_property_calscale extends iCalendar_property {
220 var $name = 'CALSCALE';
221 var $val_type = RFC2445_TYPE_TEXT;
223 function __construct() {
224 parent::__construct();
225 $this->valid_parameters = array(
226 RFC2445_XNAME => RFC2445_OPTIONAL
230 function is_valid_value($value) {
231 // This is case-sensitive
232 return ($value === 'GREGORIAN');
236 class iCalendar_property_method extends iCalendar_property {
238 var $name = 'METHOD';
239 var $val_type = RFC2445_TYPE_TEXT;
241 function __construct() {
242 parent::__construct();
243 $this->valid_parameters = array(
244 RFC2445_XNAME => RFC2445_OPTIONAL
248 function is_valid_value($value) {
249 // This is case-sensitive
250 // Methods from RFC 2446
251 $methods = array('PUBLISH', 'REQUEST', 'REPLY', 'ADD', 'CANCEL', 'REFRESH', 'COUNTER', 'DECLINECOUNTER');
252 return in_array($value, $methods);
256 class iCalendar_property_prodid extends iCalendar_property {
258 var $name = 'PRODID';
259 var $val_type = RFC2445_TYPE_TEXT;
260 var $val_default = NULL;
262 function __construct() {
263 parent::__construct();
264 $this->val_default = '-//John Papaioannou/NONSGML Bennu '._BENNU_VERSION.'//EN';
266 $this->valid_parameters = array(
267 RFC2445_XNAME => RFC2445_OPTIONAL
272 class iCalendar_property_version extends iCalendar_property {
274 var $name = 'VERSION';
275 var $val_type = RFC2445_TYPE_TEXT;
276 var $val_default = '2.0';
278 function __construct() {
279 parent::__construct();
280 $this->valid_parameters = array(
281 RFC2445_XNAME => RFC2445_OPTIONAL
285 function is_valid_value($value) {
286 return($value === '2.0' || $value === 2.0);
291 // 4.8.1 Descriptive Component Properties
292 // --------------------------------------
294 class iCalendar_property_attach extends iCalendar_property {
296 var $name = 'ATTACH';
297 var $val_type = RFC2445_TYPE_URI;
299 function __construct() {
300 parent::__construct();
301 $this->valid_parameters = array(
302 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
303 'ENCODING' => RFC2445_OPTIONAL | RFC2445_ONCE,
304 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
305 RFC2445_XNAME => RFC2445_OPTIONAL
309 function invariant_holds() {
310 if(isset($this->parameters['ENCODING']) && !isset($this->parameters['VALUE'])) {
311 return false;
313 if(isset($this->parameters['VALUE']) && !isset($this->parameters['ENCODING'])) {
314 return false;
317 return true;
320 function is_valid_parameter($parameter, $value) {
322 $parameter = strtoupper($parameter);
324 if(!parent::is_valid_parameter($parameter, $value)) {
325 return false;
328 if($parameter === 'ENCODING' && strtoupper($value) != 'BASE64') {
329 return false;
332 if($parameter === 'VALUE' && strtoupper($value) != 'BINARY') {
333 return false;
336 return true;
340 class iCalendar_property_categories extends iCalendar_property {
342 var $name = 'CATEGORIES';
343 var $val_type = RFC2445_TYPE_TEXT;
344 var $val_multi = true;
346 function __construct() {
347 parent::__construct();
348 $this->valid_parameters = array(
349 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
350 RFC2445_XNAME => RFC2445_OPTIONAL
355 class iCalendar_property_class extends iCalendar_property {
357 var $name = 'CLASS';
358 var $val_type = RFC2445_TYPE_TEXT;
359 var $val_default = 'PUBLIC';
361 function __construct() {
362 parent::__construct();
363 $this->valid_parameters = array(
364 RFC2445_XNAME => RFC2445_OPTIONAL
368 function is_valid_value($value) {
369 // If this is not an xname, it is case-sensitive
370 return ($value === 'PUBLIC' || $value === 'PRIVATE' || $value === 'CONFIDENTIAL' || rfc2445_is_xname(strtoupper($value)));
374 class iCalendar_property_comment extends iCalendar_property {
376 var $name = 'COMMENT';
377 var $val_type = RFC2445_TYPE_TEXT;
379 function __construct() {
380 parent::__construct();
381 $this->valid_parameters = array(
382 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
383 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
384 RFC2445_XNAME => RFC2445_OPTIONAL
389 class iCalendar_property_description extends iCalendar_property {
391 var $name = 'DESCRIPTION';
392 var $val_type = RFC2445_TYPE_TEXT;
394 function __construct() {
395 parent::__construct();
396 $this->valid_parameters = array(
397 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
398 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
399 RFC2445_XNAME => RFC2445_OPTIONAL
404 class iCalendar_property_geo extends iCalendar_property {
406 var $name = 'GEO';
407 var $val_type = RFC2445_TYPE_TEXT;
409 function __construct() {
410 parent::__construct();
411 $this->valid_parameters = array(
412 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
413 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
414 RFC2445_XNAME => RFC2445_OPTIONAL
418 function is_valid_value($value) {
419 // This MUST be two floats separated by a semicolon
420 if(!is_string($value)) {
421 return false;
424 $floats = explode(';', $value);
425 if(count($floats) != 2) {
426 return false;
429 return rfc2445_is_valid_value($floats[0], RFC2445_TYPE_FLOAT) && rfc2445_is_valid_value($floats[1], RFC2445_TYPE_FLOAT);
432 function set_value($value) {
433 // Must override this, otherwise the semicolon separating
434 // the two floats would get auto-quoted, which is illegal
435 if($this->is_valid_value($value)) {
436 $this->value = $value;
437 return true;
440 return false;
445 class iCalendar_property_location extends iCalendar_property {
447 var $name = 'LOCATION';
448 var $val_type = RFC2445_TYPE_TEXT;
450 function __construct() {
451 parent::__construct();
452 $this->valid_parameters = array(
453 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
454 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
455 RFC2445_XNAME => RFC2445_OPTIONAL
460 class iCalendar_property_percent_complete extends iCalendar_property {
462 var $name = 'PERCENT-COMPLETE';
463 var $val_type = RFC2445_TYPE_INTEGER;
465 function __construct() {
466 parent::__construct();
467 $this->valid_parameters = array(
468 RFC2445_XNAME => RFC2445_OPTIONAL
472 function is_valid_value($value) {
473 // Only integers between 0 and 100 inclusive allowed
474 if(!parent::is_valid_value($value)) {
475 return false;
477 $value = intval($value);
478 return ($value >= 0 && $value <= 100);
483 class iCalendar_property_priority extends iCalendar_property {
485 var $name = 'PRIORITY';
486 var $val_type = RFC2445_TYPE_INTEGER;
488 function __construct() {
489 parent::__construct();
490 $this->valid_parameters = array(
491 RFC2445_XNAME => RFC2445_OPTIONAL
495 function is_valid_value($value) {
496 // Only integers between 0 and 9 inclusive allowed
497 if(!parent::is_valid_value($value)) {
498 return false;
501 $value = intval($value);
502 return ($value >= 0 && $value <= 9);
506 class iCalendar_property_resources extends iCalendar_property {
508 var $name = 'RESOURCES';
509 var $val_type = RFC2445_TYPE_TEXT;
510 var $val_multi = true;
512 function __construct() {
513 parent::__construct();
514 $this->valid_parameters = array(
515 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
516 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
517 RFC2445_XNAME => RFC2445_OPTIONAL
522 class iCalendar_property_status extends iCalendar_property {
524 var $name = 'STATUS';
525 var $val_type = RFC2445_TYPE_TEXT;
527 function __construct() {
528 parent::__construct();
529 $this->valid_parameters = array(
530 RFC2445_XNAME => RFC2445_OPTIONAL
534 function is_valid_value($value) {
535 // This is case-sensitive
536 switch ($this->parent_component) {
537 case 'VEVENT':
538 $allowed = array('TENTATIVE', 'CONFIRMED', 'CANCELLED');
539 break;
540 case 'VTODO':
541 $allowed = array('NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED');
542 break;
543 case 'VJOURNAL':
544 $allowed = array('DRAFT', 'FINAL', 'CANCELLED');
545 break;
547 return in_array($value, $allowed);
553 class iCalendar_property_summary extends iCalendar_property {
555 var $name = 'SUMMARY';
556 var $val_type = RFC2445_TYPE_TEXT;
558 function __construct() {
559 parent::__construct();
560 $this->valid_parameters = array(
561 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
562 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
563 RFC2445_XNAME => RFC2445_OPTIONAL
568 // 4.8.2 Date and Time Component Properties
569 // ----------------------------------------
571 class iCalendar_property_completed extends iCalendar_property {
573 var $name = 'COMPLETED';
574 var $val_type = RFC2445_TYPE_DATE_TIME;
576 function __construct() {
577 parent::__construct();
578 $this->valid_parameters = array(
579 RFC2445_XNAME => RFC2445_OPTIONAL
583 function is_valid_value($value) {
584 if(!parent::is_valid_value($value)) {
585 return false;
587 // Time MUST be in UTC format
588 return(substr($value, -1) == 'Z');
592 class iCalendar_property_dtend extends iCalendar_property {
594 var $name = 'DTEND';
595 var $val_type = RFC2445_TYPE_DATE_TIME;
597 function __construct() {
598 parent::__construct();
599 $this->valid_parameters = array(
600 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
601 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
602 RFC2445_XNAME => RFC2445_OPTIONAL
606 function is_valid_value($value) {
607 if(!parent::is_valid_value($value)) {
608 return false;
611 // If present in a FREEBUSY component, must be in UTC format
612 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
613 return false;
616 return true;
620 function is_valid_parameter($parameter, $value) {
622 $parameter = strtoupper($parameter);
624 if(!parent::is_valid_parameter($parameter, $value)) {
625 return false;
627 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
628 return false;
631 return true;
635 class iCalendar_property_due extends iCalendar_property {
637 var $name = 'DUE';
638 var $val_type = RFC2445_TYPE_DATE_TIME;
640 function __construct() {
641 parent::__construct();
642 $this->valid_parameters = array(
643 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
644 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
645 RFC2445_XNAME => RFC2445_OPTIONAL
649 function is_valid_value($value) {
650 if(!parent::is_valid_value($value)) {
651 return false;
654 // If present in a FREEBUSY component, must be in UTC format
655 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
656 return false;
659 return true;
663 function is_valid_parameter($parameter, $value) {
665 $parameter = strtoupper($parameter);
667 if(!parent::is_valid_parameter($parameter, $value)) {
668 return false;
670 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
671 return false;
674 return true;
678 class iCalendar_property_dtstart extends iCalendar_property {
680 var $name = 'DTSTART';
681 var $val_type = RFC2445_TYPE_DATE_TIME;
683 function __construct() {
684 parent::__construct();
685 $this->valid_parameters = array(
686 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
687 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
688 RFC2445_XNAME => RFC2445_OPTIONAL
692 // TODO: unimplemented stuff when parent is a VTIMEZONE component
694 function is_valid_value($value) {
695 if(!parent::is_valid_value($value)) {
696 return false;
699 // If present in a FREEBUSY component, must be in UTC format
700 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
701 return false;
704 return true;
707 function is_valid_parameter($parameter, $value) {
709 $parameter = strtoupper($parameter);
711 if(!parent::is_valid_parameter($parameter, $value)) {
712 return false;
714 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
715 return false;
718 return true;
722 class iCalendar_property_duration extends iCalendar_property {
724 var $name = 'DURATION';
725 var $val_type = RFC2445_TYPE_DURATION;
727 function __construct() {
728 parent::__construct();
729 $this->valid_parameters = array(
730 RFC2445_XNAME => RFC2445_OPTIONAL
734 function is_valid_value($value) {
735 if(!parent::is_valid_value($value)) {
736 return false;
739 // Value must be positive
740 return ($value[0] != '-');
744 class iCalendar_property_freebusy extends iCalendar_property {
746 var $name = 'FREEBUSY';
747 var $val_type = RFC2445_TYPE_PERIOD;
748 var $val_multi = true;
750 function __construct() {
751 parent::__construct();
752 $this->valid_parameters = array(
753 'FBTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
754 RFC2445_XNAME => RFC2445_OPTIONAL
758 function is_valid_value($value) {
759 if(!parent::is_valid_value($value)) {
760 return false;
763 $pos = strpos($value, '/'); // We know there's only one / in there
764 if($value[$pos - 1] != 'Z') {
765 // Start time MUST be in UTC
766 return false;
768 if($value[$pos + 1] != 'P' && substr($value, -1) != 'Z') {
769 // If the second part is not a period, it MUST be in UTC
770 return false;
773 return true;
776 // TODO: these properties SHOULD be shorted in ascending order (by start time and end time as tiebreak)
779 class iCalendar_property_transp extends iCalendar_property {
781 var $name = 'TRANSP';
782 var $val_type = RFC2445_TYPE_TEXT;
783 var $val_default = 'OPAQUE';
785 function __construct() {
786 parent::__construct();
787 $this->valid_parameters = array(
788 RFC2445_XNAME => RFC2445_OPTIONAL
792 function is_valid_value($value) {
793 return ($value === 'TRANSPARENT' || $value === 'OPAQUE');
797 // TODO: 4.8.3 timezone component properties
800 // 4.8.4 Relationship Component Properties
801 // ---------------------------------------
803 class iCalendar_property_attendee extends iCalendar_property {
805 var $name = 'ATTENDEE';
806 var $val_type = RFC2445_TYPE_CAL_ADDRESS;
808 // TODO: MUST NOT be specified when the calendar object has METHOD=PUBLISH
809 // TODO: standard has lots of detail here, make triple sure that we eventually conform
811 function __construct() {
812 parent::__construct();
813 $this->valid_parameters = array(
814 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
815 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
816 'ROLE' => RFC2445_OPTIONAL | RFC2445_ONCE,
817 'PARTSTAT' => RFC2445_OPTIONAL | RFC2445_ONCE,
818 'RSVP' => RFC2445_OPTIONAL | RFC2445_ONCE,
819 'CUTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
820 'MEMBER' => RFC2445_OPTIONAL | RFC2445_ONCE,
821 'DELEGATED-TO' => RFC2445_OPTIONAL | RFC2445_ONCE,
822 'DELEGATED-FROM' => RFC2445_OPTIONAL | RFC2445_ONCE,
823 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
824 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
825 RFC2445_XNAME => RFC2445_OPTIONAL
829 function set_parent_component($componentname) {
830 if(!parent::set_parent_component($componentname)) {
831 return false;
834 if($this->parent_component == 'VFREEBUSY' || $this->parent_component == 'VALARM') {
835 // Most parameters become invalid in this case, the full allowed set is now:
836 $this->valid_parameters = array(
837 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
838 RFC2445_XNAME => RFC2445_OPTIONAL
842 return false;
847 class iCalendar_property_contact extends iCalendar_property {
849 var $name = 'CONTACT';
850 var $val_type = RFC2445_TYPE_TEXT;
852 function __construct() {
853 parent::__construct();
854 $this->valid_parameters = array(
855 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
856 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
857 RFC2445_XNAME => RFC2445_OPTIONAL
862 class iCalendar_property_organizer extends iCalendar_property {
864 var $name = 'ORGANIZER';
865 var $val_type = RFC2445_TYPE_CAL_ADDRESS;
867 function __construct() {
868 parent::__construct();
869 $this->valid_parameters = array(
870 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
871 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
872 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
873 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
874 RFC2445_XNAME => RFC2445_OPTIONAL
878 // TODO:
880 Conformance: This property MUST be specified in an iCalendar object
881 that specifies a group scheduled calendar entity. This property MUST
882 be specified in an iCalendar object that specifies the publication of
883 a calendar user's busy time. This property MUST NOT be specified in
884 an iCalendar object that specifies only a time zone definition or
885 that defines calendar entities that are not group scheduled entities,
886 but are entities only on a single user's calendar.
891 class iCalendar_property_recurrence_id extends iCalendar_property {
893 // TODO: can only be specified when defining recurring components in the calendar
895 Conformance: This property can be specified in an iCalendar object
896 containing a recurring calendar component.
898 Description: The full range of calendar components specified by a
899 recurrence set is referenced by referring to just the "UID" property
900 value corresponding to the calendar component. The "RECURRENCE-ID"
901 property allows the reference to an individual instance within the
902 recurrence set.
905 var $name = 'RECURRENCE-ID';
906 var $val_type = RFC2445_TYPE_DATE_TIME;
908 function __construct() {
909 parent::__construct();
910 $this->valid_parameters = array(
911 'RANGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
912 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
913 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
914 RFC2445_XNAME => RFC2445_OPTIONAL
918 function is_valid_parameter($parameter, $value) {
920 $parameter = strtoupper($parameter);
922 if(!parent::is_valid_parameter($parameter, $value)) {
923 return false;
925 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
926 return false;
929 return true;
934 class iCalendar_property_related_to extends iCalendar_property {
936 var $name = 'RELATED-TO';
937 var $val_type = RFC2445_TYPE_TEXT;
939 // TODO: the value of this property must reference another component's UID
941 function __construct() {
942 parent::__construct();
943 $this->valid_parameters = array(
944 'RELTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
945 RFC2445_XNAME => RFC2445_OPTIONAL
950 class iCalendar_property_url extends iCalendar_property {
952 var $name = 'URL';
953 var $val_type = RFC2445_TYPE_URI;
955 function __construct() {
956 parent::__construct();
957 $this->valid_parameters = array(
958 RFC2445_XNAME => RFC2445_OPTIONAL
963 class iCalendar_property_uid extends iCalendar_property {
965 var $name = 'UID';
966 var $val_type = RFC2445_TYPE_TEXT;
968 function __construct() {
969 parent::__construct();
970 $this->valid_parameters = array(
971 RFC2445_XNAME => RFC2445_OPTIONAL
974 // The exception to the rule: this is not a static value, so we
975 // generate it on-the-fly here. Guaranteed to be different for
976 // each instance of this property, too. Nice.
977 $this->val_default = Bennu::generate_guid();
981 // 4.8.5 Recurrence Component Properties
982 // -------------------------------------
984 class iCalendar_property_exdate extends iCalendar_property {
986 var $name = 'EXDATE';
987 var $val_type = RFC2445_TYPE_DATE_TIME;
988 var $val_multi = true;
990 function __construct() {
991 parent::__construct();
992 $this->valid_parameters = array(
993 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
994 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
995 RFC2445_XNAME => RFC2445_OPTIONAL
999 function is_valid_parameter($parameter, $value) {
1001 $parameter = strtoupper($parameter);
1003 if(!parent::is_valid_parameter($parameter, $value)) {
1004 return false;
1006 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
1007 return false;
1010 return true;
1015 class iCalendar_property_exrule extends iCalendar_property {
1017 var $name = 'EXRULE';
1018 var $val_type = RFC2445_TYPE_RECUR;
1020 function __construct() {
1021 parent::__construct();
1022 $this->valid_parameters = array(
1023 RFC2445_XNAME => RFC2445_OPTIONAL
1028 class iCalendar_property_rdate extends iCalendar_property {
1030 var $name = 'RDATE';
1031 var $val_type = RFC2445_TYPE_DATE_TIME;
1032 var $val_multi = true;
1034 function __construct() {
1035 parent::__construct();
1036 $this->valid_parameters = array(
1037 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
1038 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1039 RFC2445_XNAME => RFC2445_OPTIONAL
1043 function is_valid_parameter($parameter, $value) {
1045 $parameter = strtoupper($parameter);
1047 if(!parent::is_valid_parameter($parameter, $value)) {
1048 return false;
1050 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME' || $value == 'PERIOD')) {
1051 return false;
1054 return true;
1059 class iCalendar_property_rrule extends iCalendar_property {
1061 var $name = 'RRULE';
1062 var $val_type = RFC2445_TYPE_RECUR;
1064 function __construct() {
1065 parent::__construct();
1066 $this->valid_parameters = array(
1067 RFC2445_XNAME => RFC2445_OPTIONAL
1072 // 4.8.6 Alarm Component Properties
1073 // -------------------------------------------
1074 class iCalendar_property_action extends iCalendar_property {
1075 var $name = 'ACTION';
1076 var $val_type = RFC2445_TYPE_TEXT;
1078 function __construct() {
1079 parent::__construct();
1080 $this->valid_parameters = array(
1081 RFC2445_XNAME => RFC2445_OPTIONAL
1085 function is_valid_value($value) {
1086 if(!parent::is_valid_value($value)) {
1087 return false;
1090 // Value must be one of the following, or an x-name.
1091 $valid_values = array('ACTION', 'DISPLAY', 'EMAIL', 'PROCEDURE');
1092 return(in_array($value, $valid_values) || rfc2445_is_xname($value));
1097 class iCalendar_property_repeat extends iCalendar_property {
1098 var $name = 'REPEAT';
1099 var $val_type = RFC2445_TYPE_INTEGER;
1101 function __construct() {
1102 parent::__construct();
1103 $this->valid_parameters = array(
1104 RFC2445_XNAME => RFC2445_OPTIONAL
1109 class iCalendar_property_trigger extends iCalendar_property {
1110 var $name = 'TRIGGER';
1111 var $val_type = RFC2445_TYPE_TEXT;
1113 function __construct() {
1114 parent::__construct();
1115 $this->valid_parameters = array(
1116 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1117 'RELATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
1118 RFC2445_XNAME => RFC2445_OPTIONAL
1122 function is_valid_value($value) {
1123 if(!parent::is_valid_value($value)) {
1124 return false;
1126 // Must either be DURATION or DATE_TIME type
1127 return(rfc2445_is_valid_value($value, RFC2445_TYPE_DURATION)
1128 || rfc2445_is_valid_value($value, RFC2445_TYPE_DATE_TIME));
1134 // 4.8.7 Change Management Component Properties
1135 // --------------------------------------------
1137 class iCalendar_property_created extends iCalendar_property {
1139 var $name = 'CREATED';
1140 var $val_type = RFC2445_TYPE_DATE_TIME;
1142 function __construct() {
1143 parent::__construct();
1144 $this->valid_parameters = array(
1145 RFC2445_XNAME => RFC2445_OPTIONAL
1149 function is_valid_value($value) {
1150 if(!parent::is_valid_value($value)) {
1151 return false;
1153 // Time MUST be in UTC format
1154 return(substr($value, -1) == 'Z');
1158 class iCalendar_property_dtstamp extends iCalendar_property {
1160 var $name = 'DTSTAMP';
1161 var $val_type = RFC2445_TYPE_DATE_TIME;
1163 function __construct() {
1164 parent::__construct();
1165 $this->valid_parameters = array(
1166 RFC2445_XNAME => RFC2445_OPTIONAL
1170 function is_valid_value($value) {
1171 if(!parent::is_valid_value($value)) {
1172 return false;
1174 // Time MUST be in UTC format
1175 return(substr($value, -1) == 'Z');
1179 class iCalendar_property_last_modified extends iCalendar_property {
1181 var $name = 'LAST-MODIFIED';
1182 var $val_type = RFC2445_TYPE_DATE_TIME;
1184 function __construct() {
1185 parent::__construct();
1186 $this->valid_parameters = array(
1187 RFC2445_XNAME => RFC2445_OPTIONAL
1191 function is_valid_value($value) {
1192 if(!parent::is_valid_value($value)) {
1193 return false;
1195 // Time MUST be in UTC format
1196 return(substr($value, -1) == 'Z');
1200 class iCalendar_property_sequence extends iCalendar_property {
1202 var $name = 'SEQUENCE';
1203 var $val_type = RFC2445_TYPE_INTEGER;
1204 var $val_default = 0;
1206 function __construct() {
1207 parent::__construct();
1208 $this->valid_parameters = array(
1209 RFC2445_XNAME => RFC2445_OPTIONAL
1213 function is_valid_value($value) {
1214 if(!parent::is_valid_value($value)) {
1215 return false;
1217 $value = intval($value);
1218 return ($value >= 0);
1222 // 4.8.8 Miscellaneous Component Properties
1223 // ----------------------------------------
1225 class iCalendar_property_x extends iCalendar_property {
1227 var $name = RFC2445_XNAME;
1228 var $val_type = NULL;
1230 function __construct() {
1231 parent::__construct();
1232 $this->valid_parameters = array(
1233 // X-ALT-DESC (Description) can have FMTTYPE declaration of text/html is a property for HTML content.
1234 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1235 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1236 RFC2445_XNAME => RFC2445_OPTIONAL
1240 function set_name($name) {
1242 $name = strtoupper($name);
1244 if(rfc2445_is_xname($name)) {
1245 $this->name = $name;
1246 return true;
1249 return false;
1253 class iCalendar_property_request_status extends iCalendar_property {
1255 // IMPORTANT NOTE: This property value includes TEXT fields
1256 // separated by semicolons. Unfortunately, auto-value-formatting
1257 // cannot be used in this case. As an exception, the value passed
1258 // to this property MUST be already escaped.
1260 var $name = 'REQUEST-STATUS';
1261 var $val_type = RFC2445_TYPE_TEXT;
1263 function __construct() {
1264 parent::__construct();
1265 $this->valid_parameters = array(
1266 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1267 RFC2445_XNAME => RFC2445_OPTIONAL
1271 function is_valid_value($value) {
1272 if(!is_string($value) || empty($value)) {
1273 return false;
1276 $len = strlen($value);
1277 $parts = array();
1278 $from = 0;
1279 $escch = false;
1281 for($i = 0; $i < $len; ++$i) {
1282 if($value[$i] == ';' && !$escch) {
1283 // Token completed
1284 $parts[] = substr($value, $from, $i - $from);
1285 $from = $i + 1;
1286 continue;
1288 $escch = ($value[$i] == '\\');
1290 // Add one last token with the remaining text; if the value
1291 // ended with a ';' it was illegal, so check that this token
1292 // is not the empty string.
1293 $parts[] = substr($value, $from);
1295 $count = count($parts);
1297 // May have 2 or 3 tokens (last one is optional)
1298 if($count != 2 && $count != 3) {
1299 return false;
1302 // REMEMBER: if ANY part is empty, we have an illegal value
1304 // First token must be hierarchical numeric status (3 levels max)
1305 if(strlen($parts[0]) == 0) {
1306 return false;
1309 if($parts[0][0] < '1' || $parts[0][0] > '4') {
1310 return false;
1313 $len = strlen($parts[0]);
1315 // Max 3 levels, and can't end with a period
1316 if($len > 5 || $parts[0][$len - 1] == '.') {
1317 return false;
1320 for($i = 1; $i < $len; ++$i) {
1321 if(($i & 1) == 1 && $parts[0][$i] != '.') {
1322 // Even-indexed chars must be periods
1323 return false;
1325 else if(($i & 1) == 0 && ($parts[0][$i] < '0' || $parts[0][$i] > '9')) {
1326 // Odd-indexed chars must be numbers
1327 return false;
1331 // Second and third tokens must be TEXT, and already escaped, so
1332 // they are not allowed to have UNESCAPED semicolons, commas, slashes,
1333 // or any newlines at all
1335 for($i = 1; $i < $count; ++$i) {
1336 if(strpos($parts[$i], "\n") !== false) {
1337 return false;
1340 $len = strlen($parts[$i]);
1341 if($len == 0) {
1342 // Cannot be empty
1343 return false;
1346 $parts[$i] .= '#'; // This guard token saves some conditionals in the loop
1348 for($j = 0; $j < $len; ++$j) {
1349 $thischar = $parts[$i][$j];
1350 $nextchar = $parts[$i][$j + 1];
1351 if($thischar == '\\') {
1352 // Next char must now be one of ";,\nN"
1353 if($nextchar != ';' && $nextchar != ',' && $nextchar != '\\' &&
1354 $nextchar != 'n' && $nextchar != 'N') {
1355 return false;
1358 // OK, this escaped sequence is correct, bypass next char
1359 ++$j;
1360 continue;
1362 if($thischar == ';' || $thischar == ',' || $thischar == '\\') {
1363 // This wasn't escaped as it should
1364 return false;
1369 return true;
1372 function set_value($value) {
1373 // Must override this, otherwise the value would be quoted again
1374 if($this->is_valid_value($value)) {
1375 $this->value = $value;
1376 return true;
1379 return false;
1384 class iCalendar_property_tzid extends iCalendar_property {
1386 var $name = 'TZID';
1387 var $val_type = RFC2445_TYPE_TEXT;
1389 function __construct() {
1390 parent::__construct();
1391 $this->valid_parameters = array(
1392 RFC2445_XNAME => RFC2445_OPTIONAL
1396 function is_valid_value($value) {
1397 if(!parent::is_valid_value($value)) {
1398 return false;
1399 } else {
1400 return true;
1405 class iCalendar_property_tzname extends iCalendar_property {
1407 var $name = 'TZNAME';
1408 var $val_type = RFC2445_TYPE_TEXT;
1410 function __construct() {
1411 parent::__construct();
1412 $this->valid_parameters = array(
1413 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1414 RFC2445_XNAME => RFC2445_OPTIONAL
1418 function is_valid_value($value) {
1419 if(!parent::is_valid_value($value)) {
1420 return false;
1421 } else {
1422 return true;
1427 class iCalendar_property_tzoffsetfrom extends iCalendar_property {
1429 var $name = 'TZOFFSETFROM';
1430 var $val_type = RFC2445_TYPE_UTC_OFFSET;
1432 function __construct() {
1433 parent::__construct();
1434 $this->valid_parameters = array(
1435 RFC2445_XNAME => RFC2445_OPTIONAL
1439 function is_valid_value($value) {
1440 if(!parent::is_valid_value($value)) {
1441 return false;
1442 } else {
1443 return true;
1448 class iCalendar_property_tzoffsetto extends iCalendar_property {
1450 var $name = 'TZOFFSETTO';
1451 var $val_type = RFC2445_TYPE_UTC_OFFSET;
1453 function __construct() {
1454 parent::__construct();
1455 $this->valid_parameters = array(
1456 RFC2445_XNAME => RFC2445_OPTIONAL
1460 function is_valid_value($value) {
1461 if(!parent::is_valid_value($value)) {
1462 return false;
1463 } else {
1464 return true;
1469 class iCalendar_property_tzurl extends iCalendar_property {
1471 var $name = 'TZURL';
1472 var $val_type = RFC2445_TYPE_URI;
1474 function __construct() {
1475 parent::__construct();
1476 $this->valid_parameters = array(
1477 RFC2445_XNAME => RFC2445_OPTIONAL
1482 #######################
1484 class iCalendar_property_class extends iCalendar_property {
1486 var $name = 'CLASS';
1487 var $val_type = RFC2445_TYPE_TEXT;
1489 function __construct() {
1490 parent::__construct();
1491 $this->valid_parameters = array(
1492 RFC2445_XNAME => RFC2445_OPTIONAL