MDL-55707 grades: Stop infinite loop when regrading.
[moodle.git] / lib / bennu / iCalendar_properties.php
blob260c42a4a9f5be8d9d951b888a5c51127025e5e6
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 $replace_function = create_function('$a', 'return rfc2445_undo_value_formatting($a, '.$this->val_type.');');
111 $valarray = array_map($replace_function, $valarray);
113 // Now, if this property cannot have multiple values, don't return as an array
114 if(!$this->val_multi) {
115 return $valarray[0];
118 // Otherwise return an array even if it has one element, for uniformity
119 return $valarray;
123 function set_parameter($name, $value) {
125 // Uppercase
126 $name = strtoupper($name);
128 // Are we trying to add a valid parameter?
129 $xname = false;
130 if(!isset($this->valid_parameters[$name])) {
131 // If not, is it an x-name as per RFC 2445?
132 if(!rfc2445_is_xname($name)) {
133 return false;
135 // No more checks -- all components are supposed to allow x-name parameters
136 $xname = true;
139 if(!$this->is_valid_parameter($name, $value)) {
140 return false;
143 if(is_array($value)) {
144 foreach($value as $key => $element) {
145 $value[$key] = iCalendar_parameter::do_value_formatting($name, $element);
148 else {
149 $value = iCalendar_parameter::do_value_formatting($name, $value);
152 $this->parameters[$name] = $value;
154 // Special case: if we just changed the VALUE parameter, reflect this
155 // in the object's status so that it only accepts correct type values
156 if($name == 'VALUE') {
157 // TODO: what if this invalidates an already-set value?
158 $this->val_type = constant('RFC2445_TYPE_'.str_replace('-', '_', $value));
161 return true;
165 function get_parameter($name) {
167 // Uppercase
168 $name = strtoupper($name);
170 if(isset($this->parameters[$name])) {
171 // If there are any double quotes in the value, invisibly strip them
172 if(is_array($this->parameters[$name])) {
173 foreach($this->parameters[$name] as $key => $value) {
174 if(substr($value, 0, 1) == '"') {
175 $this->parameters[$name][$key] = substr($value, 1, strlen($value) - 2);
178 return $this->parameters[$name];
181 else {
182 if(substr($this->parameters[$name], 0, 1) == '"') {
183 return substr($this->parameters[$name], 1, strlen($this->parameters[$name]) - 2);
188 return NULL;
191 function serialize() {
192 $string = $this->name;
194 if(!empty($this->parameters)) {
195 foreach($this->parameters as $name => $value) {
196 $string .= ';'.$name.'=';
197 if(is_array($value)) {
198 $string .= implode(',', $value);
200 else {
201 $string .= $value;
206 $string .= ':'.$this->value;
208 return rfc2445_fold($string) . RFC2445_CRLF;
212 // 4.7 Calendar Properties
213 // -----------------------
215 class iCalendar_property_calscale extends iCalendar_property {
217 var $name = 'CALSCALE';
218 var $val_type = RFC2445_TYPE_TEXT;
220 function __construct() {
221 parent::__construct();
222 $this->valid_parameters = array(
223 RFC2445_XNAME => RFC2445_OPTIONAL
227 function is_valid_value($value) {
228 // This is case-sensitive
229 return ($value === 'GREGORIAN');
233 class iCalendar_property_method extends iCalendar_property {
235 var $name = 'METHOD';
236 var $val_type = RFC2445_TYPE_TEXT;
238 function __construct() {
239 parent::__construct();
240 $this->valid_parameters = array(
241 RFC2445_XNAME => RFC2445_OPTIONAL
245 function is_valid_value($value) {
246 // This is case-sensitive
247 // Methods from RFC 2446
248 $methods = array('PUBLISH', 'REQUEST', 'REPLY', 'ADD', 'CANCEL', 'REFRESH', 'COUNTER', 'DECLINECOUNTER');
249 return in_array($value, $methods);
253 class iCalendar_property_prodid extends iCalendar_property {
255 var $name = 'PRODID';
256 var $val_type = RFC2445_TYPE_TEXT;
257 var $val_default = NULL;
259 function __construct() {
260 parent::__construct();
261 $this->val_default = '-//John Papaioannou/NONSGML Bennu '._BENNU_VERSION.'//EN';
263 $this->valid_parameters = array(
264 RFC2445_XNAME => RFC2445_OPTIONAL
269 class iCalendar_property_version extends iCalendar_property {
271 var $name = 'VERSION';
272 var $val_type = RFC2445_TYPE_TEXT;
273 var $val_default = '2.0';
275 function __construct() {
276 parent::__construct();
277 $this->valid_parameters = array(
278 RFC2445_XNAME => RFC2445_OPTIONAL
282 function is_valid_value($value) {
283 return($value === '2.0' || $value === 2.0);
288 // 4.8.1 Descriptive Component Properties
289 // --------------------------------------
291 class iCalendar_property_attach extends iCalendar_property {
293 var $name = 'ATTACH';
294 var $val_type = RFC2445_TYPE_URI;
296 function __construct() {
297 parent::__construct();
298 $this->valid_parameters = array(
299 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
300 'ENCODING' => RFC2445_OPTIONAL | RFC2445_ONCE,
301 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
302 RFC2445_XNAME => RFC2445_OPTIONAL
306 function invariant_holds() {
307 if(isset($this->parameters['ENCODING']) && !isset($this->parameters['VALUE'])) {
308 return false;
310 if(isset($this->parameters['VALUE']) && !isset($this->parameters['ENCODING'])) {
311 return false;
314 return true;
317 function is_valid_parameter($parameter, $value) {
319 $parameter = strtoupper($parameter);
321 if(!parent::is_valid_parameter($parameter, $value)) {
322 return false;
325 if($parameter === 'ENCODING' && strtoupper($value) != 'BASE64') {
326 return false;
329 if($parameter === 'VALUE' && strtoupper($value) != 'BINARY') {
330 return false;
333 return true;
337 class iCalendar_property_categories extends iCalendar_property {
339 var $name = 'CATEGORIES';
340 var $val_type = RFC2445_TYPE_TEXT;
341 var $val_multi = true;
343 function __construct() {
344 parent::__construct();
345 $this->valid_parameters = array(
346 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
347 RFC2445_XNAME => RFC2445_OPTIONAL
352 class iCalendar_property_class extends iCalendar_property {
354 var $name = 'CLASS';
355 var $val_type = RFC2445_TYPE_TEXT;
356 var $val_default = 'PUBLIC';
358 function __construct() {
359 parent::__construct();
360 $this->valid_parameters = array(
361 RFC2445_XNAME => RFC2445_OPTIONAL
365 function is_valid_value($value) {
366 // If this is not an xname, it is case-sensitive
367 return ($value === 'PUBLIC' || $value === 'PRIVATE' || $value === 'CONFIDENTIAL' || rfc2445_is_xname(strtoupper($value)));
371 class iCalendar_property_comment extends iCalendar_property {
373 var $name = 'COMMENT';
374 var $val_type = RFC2445_TYPE_TEXT;
376 function __construct() {
377 parent::__construct();
378 $this->valid_parameters = array(
379 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
380 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
381 RFC2445_XNAME => RFC2445_OPTIONAL
386 class iCalendar_property_description extends iCalendar_property {
388 var $name = 'DESCRIPTION';
389 var $val_type = RFC2445_TYPE_TEXT;
391 function __construct() {
392 parent::__construct();
393 $this->valid_parameters = array(
394 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
395 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
396 RFC2445_XNAME => RFC2445_OPTIONAL
401 class iCalendar_property_geo extends iCalendar_property {
403 var $name = 'GEO';
404 var $val_type = RFC2445_TYPE_TEXT;
406 function __construct() {
407 parent::__construct();
408 $this->valid_parameters = array(
409 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
410 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
411 RFC2445_XNAME => RFC2445_OPTIONAL
415 function is_valid_value($value) {
416 // This MUST be two floats separated by a semicolon
417 if(!is_string($value)) {
418 return false;
421 $floats = explode(';', $value);
422 if(count($floats) != 2) {
423 return false;
426 return rfc2445_is_valid_value($floats[0], RFC2445_TYPE_FLOAT) && rfc2445_is_valid_value($floats[1], RFC2445_TYPE_FLOAT);
429 function set_value($value) {
430 // Must override this, otherwise the semicolon separating
431 // the two floats would get auto-quoted, which is illegal
432 if($this->is_valid_value($value)) {
433 $this->value = $value;
434 return true;
437 return false;
442 class iCalendar_property_location extends iCalendar_property {
444 var $name = 'LOCATION';
445 var $val_type = RFC2445_TYPE_TEXT;
447 function __construct() {
448 parent::__construct();
449 $this->valid_parameters = array(
450 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
451 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
452 RFC2445_XNAME => RFC2445_OPTIONAL
457 class iCalendar_property_percent_complete extends iCalendar_property {
459 var $name = 'PERCENT-COMPLETE';
460 var $val_type = RFC2445_TYPE_INTEGER;
462 function __construct() {
463 parent::__construct();
464 $this->valid_parameters = array(
465 RFC2445_XNAME => RFC2445_OPTIONAL
469 function is_valid_value($value) {
470 // Only integers between 0 and 100 inclusive allowed
471 if(!parent::is_valid_value($value)) {
472 return false;
474 $value = intval($value);
475 return ($value >= 0 && $value <= 100);
480 class iCalendar_property_priority extends iCalendar_property {
482 var $name = 'PRIORITY';
483 var $val_type = RFC2445_TYPE_INTEGER;
485 function __construct() {
486 parent::__construct();
487 $this->valid_parameters = array(
488 RFC2445_XNAME => RFC2445_OPTIONAL
492 function is_valid_value($value) {
493 // Only integers between 0 and 9 inclusive allowed
494 if(!parent::is_valid_value($value)) {
495 return false;
498 $value = intval($value);
499 return ($value >= 0 && $value <= 9);
503 class iCalendar_property_resources extends iCalendar_property {
505 var $name = 'RESOURCES';
506 var $val_type = RFC2445_TYPE_TEXT;
507 var $val_multi = true;
509 function __construct() {
510 parent::__construct();
511 $this->valid_parameters = array(
512 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
513 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
514 RFC2445_XNAME => RFC2445_OPTIONAL
519 class iCalendar_property_status extends iCalendar_property {
521 var $name = 'STATUS';
522 var $val_type = RFC2445_TYPE_TEXT;
524 function __construct() {
525 parent::__construct();
526 $this->valid_parameters = array(
527 RFC2445_XNAME => RFC2445_OPTIONAL
531 function is_valid_value($value) {
532 // This is case-sensitive
533 switch ($this->parent_component) {
534 case 'VEVENT':
535 $allowed = array('TENTATIVE', 'CONFIRMED', 'CANCELLED');
536 break;
537 case 'VTODO':
538 $allowed = array('NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED');
539 break;
540 case 'VJOURNAL':
541 $allowed = array('DRAFT', 'FINAL', 'CANCELLED');
542 break;
544 return in_array($value, $allowed);
550 class iCalendar_property_summary extends iCalendar_property {
552 var $name = 'SUMMARY';
553 var $val_type = RFC2445_TYPE_TEXT;
555 function __construct() {
556 parent::__construct();
557 $this->valid_parameters = array(
558 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
559 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
560 RFC2445_XNAME => RFC2445_OPTIONAL
565 // 4.8.2 Date and Time Component Properties
566 // ----------------------------------------
568 class iCalendar_property_completed extends iCalendar_property {
570 var $name = 'COMPLETED';
571 var $val_type = RFC2445_TYPE_DATE_TIME;
573 function __construct() {
574 parent::__construct();
575 $this->valid_parameters = array(
576 RFC2445_XNAME => RFC2445_OPTIONAL
580 function is_valid_value($value) {
581 if(!parent::is_valid_value($value)) {
582 return false;
584 // Time MUST be in UTC format
585 return(substr($value, -1) == 'Z');
589 class iCalendar_property_dtend extends iCalendar_property {
591 var $name = 'DTEND';
592 var $val_type = RFC2445_TYPE_DATE_TIME;
594 function __construct() {
595 parent::__construct();
596 $this->valid_parameters = array(
597 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
598 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
599 RFC2445_XNAME => RFC2445_OPTIONAL
603 function is_valid_value($value) {
604 if(!parent::is_valid_value($value)) {
605 return false;
608 // If present in a FREEBUSY component, must be in UTC format
609 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
610 return false;
613 return true;
617 function is_valid_parameter($parameter, $value) {
619 $parameter = strtoupper($parameter);
621 if(!parent::is_valid_parameter($parameter, $value)) {
622 return false;
624 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
625 return false;
628 return true;
632 class iCalendar_property_due extends iCalendar_property {
634 var $name = 'DUE';
635 var $val_type = RFC2445_TYPE_DATE_TIME;
637 function __construct() {
638 parent::__construct();
639 $this->valid_parameters = array(
640 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
641 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
642 RFC2445_XNAME => RFC2445_OPTIONAL
646 function is_valid_value($value) {
647 if(!parent::is_valid_value($value)) {
648 return false;
651 // If present in a FREEBUSY component, must be in UTC format
652 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
653 return false;
656 return true;
660 function is_valid_parameter($parameter, $value) {
662 $parameter = strtoupper($parameter);
664 if(!parent::is_valid_parameter($parameter, $value)) {
665 return false;
667 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
668 return false;
671 return true;
675 class iCalendar_property_dtstart extends iCalendar_property {
677 var $name = 'DTSTART';
678 var $val_type = RFC2445_TYPE_DATE_TIME;
680 function __construct() {
681 parent::__construct();
682 $this->valid_parameters = array(
683 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
684 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
685 RFC2445_XNAME => RFC2445_OPTIONAL
689 // TODO: unimplemented stuff when parent is a VTIMEZONE component
691 function is_valid_value($value) {
692 if(!parent::is_valid_value($value)) {
693 return false;
696 // If present in a FREEBUSY component, must be in UTC format
697 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
698 return false;
701 return true;
704 function is_valid_parameter($parameter, $value) {
706 $parameter = strtoupper($parameter);
708 if(!parent::is_valid_parameter($parameter, $value)) {
709 return false;
711 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
712 return false;
715 return true;
719 class iCalendar_property_duration extends iCalendar_property {
721 var $name = 'DURATION';
722 var $val_type = RFC2445_TYPE_DURATION;
724 function __construct() {
725 parent::__construct();
726 $this->valid_parameters = array(
727 RFC2445_XNAME => RFC2445_OPTIONAL
731 function is_valid_value($value) {
732 if(!parent::is_valid_value($value)) {
733 return false;
736 // Value must be positive
737 return ($value{0} != '-');
741 class iCalendar_property_freebusy extends iCalendar_property {
743 var $name = 'FREEBUSY';
744 var $val_type = RFC2445_TYPE_PERIOD;
745 var $val_multi = true;
747 function __construct() {
748 parent::__construct();
749 $this->valid_parameters = array(
750 'FBTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
751 RFC2445_XNAME => RFC2445_OPTIONAL
755 function is_valid_value($value) {
756 if(!parent::is_valid_value($value)) {
757 return false;
760 $pos = strpos($value, '/'); // We know there's only one / in there
761 if($value{$pos - 1} != 'Z') {
762 // Start time MUST be in UTC
763 return false;
765 if($value{$pos + 1} != 'P' && substr($value, -1) != 'Z') {
766 // If the second part is not a period, it MUST be in UTC
767 return false;
770 return true;
773 // TODO: these properties SHOULD be shorted in ascending order (by start time and end time as tiebreak)
776 class iCalendar_property_transp extends iCalendar_property {
778 var $name = 'TRANSP';
779 var $val_type = RFC2445_TYPE_TEXT;
780 var $val_default = 'OPAQUE';
782 function __construct() {
783 parent::__construct();
784 $this->valid_parameters = array(
785 RFC2445_XNAME => RFC2445_OPTIONAL
789 function is_valid_value($value) {
790 return ($value === 'TRANSPARENT' || $value === 'OPAQUE');
794 // TODO: 4.8.3 timezone component properties
797 // 4.8.4 Relationship Component Properties
798 // ---------------------------------------
800 class iCalendar_property_attendee extends iCalendar_property {
802 var $name = 'ATTENDEE';
803 var $val_type = RFC2445_TYPE_CAL_ADDRESS;
805 // TODO: MUST NOT be specified when the calendar object has METHOD=PUBLISH
806 // TODO: standard has lots of detail here, make triple sure that we eventually conform
808 function __construct() {
809 parent::__construct();
810 $this->valid_parameters = array(
811 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
812 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
813 'ROLE' => RFC2445_OPTIONAL | RFC2445_ONCE,
814 'PARTSTAT' => RFC2445_OPTIONAL | RFC2445_ONCE,
815 'RSVP' => RFC2445_OPTIONAL | RFC2445_ONCE,
816 'CUTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
817 'MEMBER' => RFC2445_OPTIONAL | RFC2445_ONCE,
818 'DELEGATED-TO' => RFC2445_OPTIONAL | RFC2445_ONCE,
819 'DELEGATED-FROM' => RFC2445_OPTIONAL | RFC2445_ONCE,
820 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
821 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
822 RFC2445_XNAME => RFC2445_OPTIONAL
826 function set_parent_component($componentname) {
827 if(!parent::set_parent_component($componentname)) {
828 return false;
831 if($this->parent_component == 'VFREEBUSY' || $this->parent_component == 'VALARM') {
832 // Most parameters become invalid in this case, the full allowed set is now:
833 $this->valid_parameters = array(
834 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
835 RFC2445_XNAME => RFC2445_OPTIONAL
839 return false;
844 class iCalendar_property_contact extends iCalendar_property {
846 var $name = 'CONTACT';
847 var $val_type = RFC2445_TYPE_TEXT;
849 function __construct() {
850 parent::__construct();
851 $this->valid_parameters = array(
852 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
853 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
854 RFC2445_XNAME => RFC2445_OPTIONAL
859 class iCalendar_property_organizer extends iCalendar_property {
861 var $name = 'ORGANIZER';
862 var $val_type = RFC2445_TYPE_CAL_ADDRESS;
864 function __construct() {
865 parent::__construct();
866 $this->valid_parameters = array(
867 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
868 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
869 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
870 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
871 RFC2445_XNAME => RFC2445_OPTIONAL
875 // TODO:
877 Conformance: This property MUST be specified in an iCalendar object
878 that specifies a group scheduled calendar entity. This property MUST
879 be specified in an iCalendar object that specifies the publication of
880 a calendar user's busy time. This property MUST NOT be specified in
881 an iCalendar object that specifies only a time zone definition or
882 that defines calendar entities that are not group scheduled entities,
883 but are entities only on a single user's calendar.
888 class iCalendar_property_recurrence_id extends iCalendar_property {
890 // TODO: can only be specified when defining recurring components in the calendar
892 Conformance: This property can be specified in an iCalendar object
893 containing a recurring calendar component.
895 Description: The full range of calendar components specified by a
896 recurrence set is referenced by referring to just the "UID" property
897 value corresponding to the calendar component. The "RECURRENCE-ID"
898 property allows the reference to an individual instance within the
899 recurrence set.
902 var $name = 'RECURRENCE-ID';
903 var $val_type = RFC2445_TYPE_DATE_TIME;
905 function __construct() {
906 parent::__construct();
907 $this->valid_parameters = array(
908 'RANGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
909 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
910 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
911 RFC2445_XNAME => RFC2445_OPTIONAL
915 function is_valid_parameter($parameter, $value) {
917 $parameter = strtoupper($parameter);
919 if(!parent::is_valid_parameter($parameter, $value)) {
920 return false;
922 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
923 return false;
926 return true;
931 class iCalendar_property_related_to extends iCalendar_property {
933 var $name = 'RELATED-TO';
934 var $val_type = RFC2445_TYPE_TEXT;
936 // TODO: the value of this property must reference another component's UID
938 function __construct() {
939 parent::__construct();
940 $this->valid_parameters = array(
941 'RELTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
942 RFC2445_XNAME => RFC2445_OPTIONAL
947 class iCalendar_property_url extends iCalendar_property {
949 var $name = 'URL';
950 var $val_type = RFC2445_TYPE_URI;
952 function __construct() {
953 parent::__construct();
954 $this->valid_parameters = array(
955 RFC2445_XNAME => RFC2445_OPTIONAL
960 class iCalendar_property_uid extends iCalendar_property {
962 var $name = 'UID';
963 var $val_type = RFC2445_TYPE_TEXT;
965 function __construct() {
966 parent::__construct();
967 $this->valid_parameters = array(
968 RFC2445_XNAME => RFC2445_OPTIONAL
971 // The exception to the rule: this is not a static value, so we
972 // generate it on-the-fly here. Guaranteed to be different for
973 // each instance of this property, too. Nice.
974 $this->val_default = Bennu::generate_guid();
978 // 4.8.5 Recurrence Component Properties
979 // -------------------------------------
981 class iCalendar_property_exdate extends iCalendar_property {
983 var $name = 'EXDATE';
984 var $val_type = RFC2445_TYPE_DATE_TIME;
985 var $val_multi = true;
987 function __construct() {
988 parent::__construct();
989 $this->valid_parameters = array(
990 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
991 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
992 RFC2445_XNAME => RFC2445_OPTIONAL
996 function is_valid_parameter($parameter, $value) {
998 $parameter = strtoupper($parameter);
1000 if(!parent::is_valid_parameter($parameter, $value)) {
1001 return false;
1003 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
1004 return false;
1007 return true;
1012 class iCalendar_property_exrule extends iCalendar_property {
1014 var $name = 'EXRULE';
1015 var $val_type = RFC2445_TYPE_RECUR;
1017 function __construct() {
1018 parent::__construct();
1019 $this->valid_parameters = array(
1020 RFC2445_XNAME => RFC2445_OPTIONAL
1025 class iCalendar_property_rdate extends iCalendar_property {
1027 var $name = 'RDATE';
1028 var $val_type = RFC2445_TYPE_DATE_TIME;
1029 var $val_multi = true;
1031 function __construct() {
1032 parent::__construct();
1033 $this->valid_parameters = array(
1034 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
1035 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1036 RFC2445_XNAME => RFC2445_OPTIONAL
1040 function is_valid_parameter($parameter, $value) {
1042 $parameter = strtoupper($parameter);
1044 if(!parent::is_valid_parameter($parameter, $value)) {
1045 return false;
1047 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME' || $value == 'PERIOD')) {
1048 return false;
1051 return true;
1056 class iCalendar_property_rrule extends iCalendar_property {
1058 var $name = 'RRULE';
1059 var $val_type = RFC2445_TYPE_RECUR;
1061 function __construct() {
1062 parent::__construct();
1063 $this->valid_parameters = array(
1064 RFC2445_XNAME => RFC2445_OPTIONAL
1069 // 4.8.6 Alarm Component Properties
1070 // -------------------------------------------
1071 class iCalendar_property_action extends iCalendar_property {
1072 var $name = 'ACTION';
1073 var $val_type = RFC2445_TYPE_TEXT;
1075 function __construct() {
1076 parent::__construct();
1077 $this->valid_parameters = array(
1078 RFC2445_XNAME => RFC2445_OPTIONAL
1082 function is_valid_value($value) {
1083 if(!parent::is_valid_value($value)) {
1084 return false;
1087 // Value must be one of the following, or an x-name.
1088 $valid_values = array('ACTION', 'DISPLAY', 'EMAIL', 'PROCEDURE');
1089 return(in_array($value, $valid_values) || rfc2445_is_xname($value));
1094 class iCalendar_property_repeat extends iCalendar_property {
1095 var $name = 'REPEAT';
1096 var $val_type = RFC2445_TYPE_INTEGER;
1098 function __construct() {
1099 parent::__construct();
1100 $this->valid_parameters = array(
1101 RFC2445_XNAME => RFC2445_OPTIONAL
1106 class iCalendar_property_trigger extends iCalendar_property {
1107 var $name = 'TRIGGER';
1108 var $val_type = RFC2445_TYPE_TEXT;
1110 function __construct() {
1111 parent::__construct();
1112 $this->valid_parameters = array(
1113 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1114 'RELATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
1115 RFC2445_XNAME => RFC2445_OPTIONAL
1119 function is_valid_value($value) {
1120 if(!parent::is_valid_value($value)) {
1121 return false;
1123 // Must either be DURATION or DATE_TIME type
1124 return(rfc2445_is_valid_value($value, RFC2445_TYPE_DURATION)
1125 || rfc2445_is_valid_value($value, RFC2445_TYPE_DATE_TIME));
1131 // 4.8.7 Change Management Component Properties
1132 // --------------------------------------------
1134 class iCalendar_property_created extends iCalendar_property {
1136 var $name = 'CREATED';
1137 var $val_type = RFC2445_TYPE_DATE_TIME;
1139 function __construct() {
1140 parent::__construct();
1141 $this->valid_parameters = array(
1142 RFC2445_XNAME => RFC2445_OPTIONAL
1146 function is_valid_value($value) {
1147 if(!parent::is_valid_value($value)) {
1148 return false;
1150 // Time MUST be in UTC format
1151 return(substr($value, -1) == 'Z');
1155 class iCalendar_property_dtstamp extends iCalendar_property {
1157 var $name = 'DTSTAMP';
1158 var $val_type = RFC2445_TYPE_DATE_TIME;
1160 function __construct() {
1161 parent::__construct();
1162 $this->valid_parameters = array(
1163 RFC2445_XNAME => RFC2445_OPTIONAL
1167 function is_valid_value($value) {
1168 if(!parent::is_valid_value($value)) {
1169 return false;
1171 // Time MUST be in UTC format
1172 return(substr($value, -1) == 'Z');
1176 class iCalendar_property_last_modified extends iCalendar_property {
1178 var $name = 'LAST-MODIFIED';
1179 var $val_type = RFC2445_TYPE_DATE_TIME;
1181 function __construct() {
1182 parent::__construct();
1183 $this->valid_parameters = array(
1184 RFC2445_XNAME => RFC2445_OPTIONAL
1188 function is_valid_value($value) {
1189 if(!parent::is_valid_value($value)) {
1190 return false;
1192 // Time MUST be in UTC format
1193 return(substr($value, -1) == 'Z');
1197 class iCalendar_property_sequence extends iCalendar_property {
1199 var $name = 'SEQUENCE';
1200 var $val_type = RFC2445_TYPE_INTEGER;
1201 var $val_default = 0;
1203 function __construct() {
1204 parent::__construct();
1205 $this->valid_parameters = array(
1206 RFC2445_XNAME => RFC2445_OPTIONAL
1210 function is_valid_value($value) {
1211 if(!parent::is_valid_value($value)) {
1212 return false;
1214 $value = intval($value);
1215 return ($value >= 0);
1219 // 4.8.8 Miscellaneous Component Properties
1220 // ----------------------------------------
1222 class iCalendar_property_x extends iCalendar_property {
1224 var $name = RFC2445_XNAME;
1225 var $val_type = NULL;
1227 function __construct() {
1228 parent::__construct();
1229 $this->valid_parameters = array(
1230 // X-ALT-DESC (Description) can have FMTTYPE declaration of text/html is a property for HTML content.
1231 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1232 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1233 RFC2445_XNAME => RFC2445_OPTIONAL
1237 function set_name($name) {
1239 $name = strtoupper($name);
1241 if(rfc2445_is_xname($name)) {
1242 $this->name = $name;
1243 return true;
1246 return false;
1250 class iCalendar_property_request_status extends iCalendar_property {
1252 // IMPORTANT NOTE: This property value includes TEXT fields
1253 // separated by semicolons. Unfortunately, auto-value-formatting
1254 // cannot be used in this case. As an exception, the value passed
1255 // to this property MUST be already escaped.
1257 var $name = 'REQUEST-STATUS';
1258 var $val_type = RFC2445_TYPE_TEXT;
1260 function __construct() {
1261 parent::__construct();
1262 $this->valid_parameters = array(
1263 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1264 RFC2445_XNAME => RFC2445_OPTIONAL
1268 function is_valid_value($value) {
1269 if(!is_string($value) || empty($value)) {
1270 return false;
1273 $len = strlen($value);
1274 $parts = array();
1275 $from = 0;
1276 $escch = false;
1278 for($i = 0; $i < $len; ++$i) {
1279 if($value{$i} == ';' && !$escch) {
1280 // Token completed
1281 $parts[] = substr($value, $from, $i - $from);
1282 $from = $i + 1;
1283 continue;
1285 $escch = ($value{$i} == '\\');
1287 // Add one last token with the remaining text; if the value
1288 // ended with a ';' it was illegal, so check that this token
1289 // is not the empty string.
1290 $parts[] = substr($value, $from);
1292 $count = count($parts);
1294 // May have 2 or 3 tokens (last one is optional)
1295 if($count != 2 && $count != 3) {
1296 return false;
1299 // REMEMBER: if ANY part is empty, we have an illegal value
1301 // First token must be hierarchical numeric status (3 levels max)
1302 if(strlen($parts[0]) == 0) {
1303 return false;
1306 if($parts[0]{0} < '1' || $parts[0]{0} > '4') {
1307 return false;
1310 $len = strlen($parts[0]);
1312 // Max 3 levels, and can't end with a period
1313 if($len > 5 || $parts[0]{$len - 1} == '.') {
1314 return false;
1317 for($i = 1; $i < $len; ++$i) {
1318 if(($i & 1) == 1 && $parts[0]{$i} != '.') {
1319 // Even-indexed chars must be periods
1320 return false;
1322 else if(($i & 1) == 0 && ($parts[0]{$i} < '0' || $parts[0]{$i} > '9')) {
1323 // Odd-indexed chars must be numbers
1324 return false;
1328 // Second and third tokens must be TEXT, and already escaped, so
1329 // they are not allowed to have UNESCAPED semicolons, commas, slashes,
1330 // or any newlines at all
1332 for($i = 1; $i < $count; ++$i) {
1333 if(strpos($parts[$i], "\n") !== false) {
1334 return false;
1337 $len = strlen($parts[$i]);
1338 if($len == 0) {
1339 // Cannot be empty
1340 return false;
1343 $parts[$i] .= '#'; // This guard token saves some conditionals in the loop
1345 for($j = 0; $j < $len; ++$j) {
1346 $thischar = $parts[$i]{$j};
1347 $nextchar = $parts[$i]{$j + 1};
1348 if($thischar == '\\') {
1349 // Next char must now be one of ";,\nN"
1350 if($nextchar != ';' && $nextchar != ',' && $nextchar != '\\' &&
1351 $nextchar != 'n' && $nextchar != 'N') {
1352 return false;
1355 // OK, this escaped sequence is correct, bypass next char
1356 ++$j;
1357 continue;
1359 if($thischar == ';' || $thischar == ',' || $thischar == '\\') {
1360 // This wasn't escaped as it should
1361 return false;
1366 return true;
1369 function set_value($value) {
1370 // Must override this, otherwise the value would be quoted again
1371 if($this->is_valid_value($value)) {
1372 $this->value = $value;
1373 return true;
1376 return false;
1381 class iCalendar_property_tzid extends iCalendar_property {
1383 var $name = 'TZID';
1384 var $val_type = RFC2445_TYPE_TEXT;
1386 function __construct() {
1387 parent::__construct();
1388 $this->valid_parameters = array(
1389 RFC2445_XNAME => RFC2445_OPTIONAL
1393 function is_valid_value($value) {
1394 if(!parent::is_valid_value($value)) {
1395 return false;
1396 } else {
1397 return true;
1402 class iCalendar_property_tzname extends iCalendar_property {
1404 var $name = 'TZNAME';
1405 var $val_type = RFC2445_TYPE_TEXT;
1407 function __construct() {
1408 parent::__construct();
1409 $this->valid_parameters = array(
1410 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1411 RFC2445_XNAME => RFC2445_OPTIONAL
1415 function is_valid_value($value) {
1416 if(!parent::is_valid_value($value)) {
1417 return false;
1418 } else {
1419 return true;
1424 class iCalendar_property_tzoffsetfrom extends iCalendar_property {
1426 var $name = 'TZOFFSETFROM';
1427 var $val_type = RFC2445_TYPE_UTC_OFFSET;
1429 function __construct() {
1430 parent::__construct();
1431 $this->valid_parameters = array(
1432 RFC2445_XNAME => RFC2445_OPTIONAL
1436 function is_valid_value($value) {
1437 if(!parent::is_valid_value($value)) {
1438 return false;
1439 } else {
1440 return true;
1445 class iCalendar_property_tzoffsetto extends iCalendar_property {
1447 var $name = 'TZOFFSETTO';
1448 var $val_type = RFC2445_TYPE_UTC_OFFSET;
1450 function __construct() {
1451 parent::__construct();
1452 $this->valid_parameters = array(
1453 RFC2445_XNAME => RFC2445_OPTIONAL
1457 function is_valid_value($value) {
1458 if(!parent::is_valid_value($value)) {
1459 return false;
1460 } else {
1461 return true;
1468 #######################
1470 class iCalendar_property_class extends iCalendar_property {
1472 var $name = 'CLASS';
1473 var $val_type = RFC2445_TYPE_TEXT;
1475 function __construct() {
1476 parent::__construct();
1477 $this->valid_parameters = array(
1478 RFC2445_XNAME => RFC2445_OPTIONAL