MDL-10386 Refactored the grader report file (index.php), and extracted the grade_repo...
[moodle-pu.git] / lib / bennu / iCalendar_properties.php
blob3f3120e54efa9a21af059df99a910cb1827e08f6
1 <?php // $Id$
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 iCalendar_property() {
29 $this->construct();
32 function construct() {
33 $this->parameters = array();
36 // If some property needs extra care with its parameters, override this
37 // IMPORTANT: the parameter name MUST BE CAPITALIZED!
38 function is_valid_parameter($parameter, $value) {
40 if(is_array($value)) {
41 if(!iCalendar_parameter::multiple_values_allowed($parameter)) {
42 return false;
44 foreach($value as $item) {
45 if(!iCalendar_parameter::is_valid_value($this, $parameter, $item)) {
46 return false;
49 return true;
52 return iCalendar_parameter::is_valid_value($this, $parameter, $value);
55 function invariant_holds() {
56 return true;
59 // If some property is very picky about its values, it should do the work itself
60 // Only data type validation is done here
61 function is_valid_value($value) {
62 if(is_array($value)) {
63 if(!$this->val_multi) {
64 return false;
66 else {
67 foreach($value as $oneval) {
68 if(!rfc2445_is_valid_value($oneval, $this->val_type)) {
69 return false;
73 return true;
75 return rfc2445_is_valid_value($value, $this->val_type);
78 function default_value() {
79 return $this->val_default;
82 function set_parent_component($componentname) {
83 if(class_exists('iCalendar_'.strtolower(substr($componentname, 1)))) {
84 $this->parent_component = strtoupper($componentname);
85 return true;
88 return false;
91 function set_value($value) {
92 if($this->is_valid_value($value)) {
93 // This transparently formats any value type according to the iCalendar specs
94 if(is_array($value)) {
95 foreach($value as $key => $item) {
96 $value[$key] = rfc2445_do_value_formatting($item, $this->val_type);
98 $this->value = implode(',', $value);
100 else {
101 $this->value = rfc2445_do_value_formatting($value, $this->val_type);
104 return true;
106 return false;
109 function get_value() {
110 // First of all, assume that we have multiple values
111 $valarray = explode('\\,', $this->value);
113 // Undo transparent formatting
114 $replace_function = create_function('$a', 'return rfc2445_undo_value_formatting($a, '.$this->val_type.');');
115 $valarray = array_map($replace_function, $valarray);
117 // Now, if this property cannot have multiple values, don't return as an array
118 if(!$this->val_multi) {
119 return $valarray[0];
122 // Otherwise return an array even if it has one element, for uniformity
123 return $valarray;
127 function set_parameter($name, $value) {
129 // Uppercase
130 $name = strtoupper($name);
132 // Are we trying to add a valid parameter?
133 $xname = false;
134 if(!isset($this->valid_parameters[$name])) {
135 // If not, is it an x-name as per RFC 2445?
136 if(!rfc2445_is_xname($name)) {
137 return false;
139 // No more checks -- all components are supposed to allow x-name parameters
140 $xname = true;
143 if(!$this->is_valid_parameter($name, $value)) {
144 return false;
147 if(is_array($value)) {
148 foreach($value as $key => $element) {
149 $value[$key] = iCalendar_parameter::do_value_formatting($name, $element);
152 else {
153 $value = iCalendar_parameter::do_value_formatting($name, $value);
156 $this->parameters[$name] = $value;
158 // Special case: if we just changed the VALUE parameter, reflect this
159 // in the object's status so that it only accepts correct type values
160 if($name == 'VALUE') {
161 // TODO: what if this invalidates an already-set value?
162 $this->val_type = constant('RFC2445_TYPE_'.str_replace('-', '_', $value));
165 return true;
169 function get_parameter($name) {
171 // Uppercase
172 $name = strtoupper($name);
174 if(isset($this->parameters[$name])) {
175 // If there are any double quotes in the value, invisibly strip them
176 if(is_array($this->parameters[$name])) {
177 foreach($this->parameters[$name] as $key => $value) {
178 if(substr($value, 0, 1) == '"') {
179 $this->parameters[$name][$key] = substr($value, 1, strlen($value) - 2);
182 return $this->parameters[$name];
185 else {
186 if(substr($this->parameters[$name], 0, 1) == '"') {
187 return substr($this->parameters[$name], 1, strlen($this->parameters[$name]) - 2);
192 return NULL;
195 function serialize() {
196 $string = $this->name;
198 if(!empty($this->parameters)) {
199 foreach($this->parameters as $name => $value) {
200 $string .= ';'.$name.'=';
201 if(is_array($value)) {
202 $string .= implode(',', $value);
204 else {
205 $string .= $value;
210 $string .= ':'.$this->value;
212 return rfc2445_fold($string) . RFC2445_CRLF;
216 // 4.7 Calendar Properties
217 // -----------------------
219 class iCalendar_property_calscale extends iCalendar_property {
221 var $name = 'CALSCALE';
222 var $val_type = RFC2445_TYPE_TEXT;
224 function 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 $this->valid_parameters = array(
243 RFC2445_XNAME => RFC2445_OPTIONAL
247 function is_valid_value($value) {
248 // This is case-sensitive
249 // Methods from RFC 2446
250 $methods = array('PUBLISH', 'REQUEST', 'REPLY', 'ADD', 'CANCEL', 'REFRESH', 'COUNTER', 'DECLINECOUNTER');
251 return in_array($value, $methods);
255 class iCalendar_property_prodid extends iCalendar_property {
257 var $name = 'PRODID';
258 var $val_type = RFC2445_TYPE_TEXT;
259 var $val_default = NULL;
261 function construct() {
262 $this->val_default = '-//John Papaioannou/NONSGML Bennu '._BENNU_VERSION.'//EN';
264 $this->valid_parameters = array(
265 RFC2445_XNAME => RFC2445_OPTIONAL
270 class iCalendar_property_version extends iCalendar_property {
272 var $name = 'VERSION';
273 var $val_type = RFC2445_TYPE_TEXT;
274 var $val_default = '2.0';
276 function 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 $this->valid_parameters = array(
298 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
299 'ENCODING' => RFC2445_OPTIONAL | RFC2445_ONCE,
300 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
301 RFC2445_XNAME => RFC2445_OPTIONAL
305 function invariant_holds() {
306 if(isset($this->parameters['ENCODING']) && !isset($this->parameters['VALUE'])) {
307 return false;
309 if(isset($this->parameters['VALUE']) && !isset($this->parameters['ENCODING'])) {
310 return false;
313 return true;
316 function is_valid_parameter($parameter, $value) {
318 $parameter = strtoupper($parameter);
320 if(!parent::is_valid_parameter($parameter, $value)) {
321 return false;
324 if($parameter === 'ENCODING' && strtoupper($value) != 'BASE64') {
325 return false;
328 if($parameter === 'VALUE' && strtoupper($value) != 'BINARY') {
329 return false;
332 return true;
336 class iCalendar_property_categories extends iCalendar_property {
338 var $name = 'CATEGORIES';
339 var $val_type = RFC2445_TYPE_TEXT;
340 var $val_multi = true;
342 function construct() {
343 $this->valid_parameters = array(
344 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
345 RFC2445_XNAME => RFC2445_OPTIONAL
350 class iCalendar_property_class extends iCalendar_property {
352 var $name = 'CLASS';
353 var $val_type = RFC2445_TYPE_TEXT;
354 var $val_default = 'PUBLIC';
356 function construct() {
357 $this->valid_parameters = array(
358 RFC2445_XNAME => RFC2445_OPTIONAL
362 function is_valid_value($value) {
363 // If this is not an xname, it is case-sensitive
364 return ($value === 'PUBLIC' || $value === 'PRIVATE' || $value === 'CONFIDENTIAL' || rfc2445_is_xname(strtoupper($value)));
368 class iCalendar_property_comment extends iCalendar_property {
370 var $name = 'COMMENT';
371 var $val_type = RFC2445_TYPE_TEXT;
373 function construct() {
374 $this->valid_parameters = array(
375 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
376 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
377 RFC2445_XNAME => RFC2445_OPTIONAL
382 class iCalendar_property_description extends iCalendar_property {
384 var $name = 'DESCRIPTION';
385 var $val_type = RFC2445_TYPE_TEXT;
387 function construct() {
388 $this->valid_parameters = array(
389 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
390 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
391 RFC2445_XNAME => RFC2445_OPTIONAL
396 class iCalendar_property_geo extends iCalendar_property {
398 var $name = 'GEO';
399 var $val_type = RFC2445_TYPE_TEXT;
401 function construct() {
402 $this->valid_parameters = array(
403 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
404 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
405 RFC2445_XNAME => RFC2445_OPTIONAL
409 function is_valid_value($value) {
410 // This MUST be two floats separated by a semicolon
411 if(!is_string($value)) {
412 return false;
415 $floats = explode(';', $value);
416 if(count($floats) != 2) {
417 return false;
420 return rfc2445_is_valid_value($floats[0], RFC2445_TYPE_FLOAT) && rfc2445_is_valid_value($floats[1], RFC2445_TYPE_FLOAT);
423 function set_value($value) {
424 // Must override this, otherwise the semicolon separating
425 // the two floats would get auto-quoted, which is illegal
426 if($this->is_valid_value($value)) {
427 $this->value = $value;
428 return true;
431 return false;
436 class iCalendar_property_location extends iCalendar_property {
438 var $name = 'LOCATION';
439 var $val_type = RFC2445_TYPE_TEXT;
441 function construct() {
442 $this->valid_parameters = array(
443 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
444 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
445 RFC2445_XNAME => RFC2445_OPTIONAL
450 class iCalendar_property_percent_complete extends iCalendar_property {
452 var $name = 'PERCENT-COMPLETE';
453 var $val_type = RFC2445_TYPE_INTEGER;
455 function construct() {
456 $this->valid_parameters = array(
457 RFC2445_XNAME => RFC2445_OPTIONAL
461 function is_valid_value($value) {
462 // Only integers between 0 and 100 inclusive allowed
463 if(!parent::is_valid_value($value)) {
464 return false;
466 $value = intval($value);
467 return ($value >= 0 && $value <= 100);
472 class iCalendar_property_priority extends iCalendar_property {
474 var $name = 'PRIORITY';
475 var $val_type = RFC2445_TYPE_INTEGER;
477 function construct() {
478 $this->valid_parameters = array(
479 RFC2445_XNAME => RFC2445_OPTIONAL
483 function is_valid_value($value) {
484 // Only integers between 0 and 9 inclusive allowed
485 if(!parent::is_valid_value($value)) {
486 return false;
489 $value = intval($value);
490 return ($value >= 0 && $value <= 9);
494 class iCalendar_property_resources extends iCalendar_property {
496 var $name = 'RESOURCES';
497 var $val_type = RFC2445_TYPE_TEXT;
498 var $val_multi = true;
500 function construct() {
501 $this->valid_parameters = array(
502 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
503 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
504 RFC2445_XNAME => RFC2445_OPTIONAL
509 class iCalendar_property_status extends iCalendar_property {
511 var $name = 'STATUS';
512 var $val_type = RFC2445_TYPE_TEXT;
514 function construct() {
515 $this->valid_parameters = array(
516 RFC2445_XNAME => RFC2445_OPTIONAL
520 function is_valid_value($value) {
521 // This is case-sensitive
522 switch ($this->parent_component) {
523 case 'VEVENT':
524 $allowed = array('TENTATIVE', 'CONFIRMED', 'CANCELLED');
525 break;
526 case 'VTODO':
527 $allowed = array('NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED');
528 break;
529 case 'VJOURNAL':
530 $allowed = array('DRAFT', 'FINAL', 'CANCELLED');
531 break;
533 return in_array($value, $allowed);
539 class iCalendar_property_summary extends iCalendar_property {
541 var $name = 'SUMMARY';
542 var $val_type = RFC2445_TYPE_TEXT;
544 function construct() {
545 $this->valid_parameters = array(
546 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
547 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
548 RFC2445_XNAME => RFC2445_OPTIONAL
553 // 4.8.2 Date and Time Component Properties
554 // ----------------------------------------
556 class iCalendar_property_completed extends iCalendar_property {
558 var $name = 'COMPLETED';
559 var $val_type = RFC2445_TYPE_DATE_TIME;
561 function construct() {
562 $this->valid_parameters = array(
563 RFC2445_XNAME => RFC2445_OPTIONAL
567 function is_valid_value($value) {
568 if(!parent::is_valid_value($value)) {
569 return false;
571 // Time MUST be in UTC format
572 return(substr($value, -1) == 'Z');
576 class iCalendar_property_dtend extends iCalendar_property {
578 var $name = 'DTEND';
579 var $val_type = RFC2445_TYPE_DATE_TIME;
581 function construct() {
582 $this->valid_parameters = array(
583 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
584 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
585 RFC2445_XNAME => RFC2445_OPTIONAL
589 function is_valid_value($value) {
590 if(!parent::is_valid_value($value)) {
591 return false;
594 // If present in a FREEBUSY component, must be in UTC format
595 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
596 return false;
599 return true;
603 function is_valid_parameter($parameter, $value) {
605 $parameter = strtoupper($parameter);
607 if(!parent::is_valid_parameter($parameter, $value)) {
608 return false;
610 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
611 return false;
614 return true;
618 class iCalendar_property_due extends iCalendar_property {
620 var $name = 'DUE';
621 var $val_type = RFC2445_TYPE_DATE_TIME;
623 function construct() {
624 $this->valid_parameters = array(
625 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
626 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
627 RFC2445_XNAME => RFC2445_OPTIONAL
631 function is_valid_value($value) {
632 if(!parent::is_valid_value($value)) {
633 return false;
636 // If present in a FREEBUSY component, must be in UTC format
637 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
638 return false;
641 return true;
645 function is_valid_parameter($parameter, $value) {
647 $parameter = strtoupper($parameter);
649 if(!parent::is_valid_parameter($parameter, $value)) {
650 return false;
652 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
653 return false;
656 return true;
660 class iCalendar_property_dtstart extends iCalendar_property {
662 var $name = 'DTSTART';
663 var $val_type = RFC2445_TYPE_DATE_TIME;
665 function construct() {
666 $this->valid_parameters = array(
667 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
668 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
669 RFC2445_XNAME => RFC2445_OPTIONAL
673 // TODO: unimplemented stuff when parent is a VTIMEZONE component
675 function is_valid_value($value) {
676 if(!parent::is_valid_value($value)) {
677 return false;
680 // If present in a FREEBUSY component, must be in UTC format
681 if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
682 return false;
685 return true;
688 function is_valid_parameter($parameter, $value) {
690 $parameter = strtoupper($parameter);
692 if(!parent::is_valid_parameter($parameter, $value)) {
693 return false;
695 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
696 return false;
699 return true;
703 class iCalendar_property_duration extends iCalendar_property {
705 var $name = 'DURATION';
706 var $val_type = RFC2445_TYPE_DURATION;
708 function construct() {
709 $this->valid_parameters = array(
710 RFC2445_XNAME => RFC2445_OPTIONAL
714 function is_valid_value($value) {
715 if(!parent::is_valid_value($value)) {
716 return false;
719 // Value must be positive
720 return ($value{0} != '-');
724 class iCalendar_property_freebusy extends iCalendar_property {
726 var $name = 'FREEBUSY';
727 var $val_type = RFC2445_TYPE_PERIOD;
728 var $val_multi = true;
730 function construct() {
731 $this->valid_parameters = array(
732 'FBTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
733 RFC2445_XNAME => RFC2445_OPTIONAL
737 function is_valid_value($value) {
738 if(!parent::is_valid_value($value)) {
739 return false;
742 $pos = strpos($value, '/'); // We know there's only one / in there
743 if($value{$pos - 1} != 'Z') {
744 // Start time MUST be in UTC
745 return false;
747 if($value{$pos + 1} != 'P' && $substr($value, -1) != 'Z') {
748 // If the second part is not a period, it MUST be in UTC
749 return false;
752 return true;
755 // TODO: these properties SHOULD be shorted in ascending order (by start time and end time as tiebreak)
758 class iCalendar_property_transp extends iCalendar_property {
760 var $name = 'TRANSP';
761 var $val_type = RFC2445_TYPE_TEXT;
762 var $val_default = 'OPAQUE';
764 function construct() {
765 $this->valid_parameters = array(
766 RFC2445_XNAME => RFC2445_OPTIONAL
770 function is_valid_value($value) {
771 return ($value === 'TRANSPARENT' || $value === 'OPAQUE');
775 // TODO: 4.8.3 timezone component properties
778 // 4.8.4 Relationship Component Properties
779 // ---------------------------------------
781 class iCalendar_property_attendee extends iCalendar_property {
783 var $name = 'ATTENDEE';
784 var $val_type = RFC2445_TYPE_CAL_ADDRESS;
786 // TODO: MUST NOT be specified when the calendar object has METHOD=PUBLISH
787 // TODO: standard has lots of detail here, make triple sure that we eventually conform
789 function construct() {
790 $this->valid_parameters = array(
791 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
792 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
793 'ROLE' => RFC2445_OPTIONAL | RFC2445_ONCE,
794 'PARTSTAT' => RFC2445_OPTIONAL | RFC2445_ONCE,
795 'RSVP' => RFC2445_OPTIONAL | RFC2445_ONCE,
796 'CUTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
797 'MEMBER' => RFC2445_OPTIONAL | RFC2445_ONCE,
798 'DELEGATED-TO' => RFC2445_OPTIONAL | RFC2445_ONCE,
799 'DELEGATED-FROM' => RFC2445_OPTIONAL | RFC2445_ONCE,
800 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
801 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
802 RFC2445_XNAME => RFC2445_OPTIONAL
806 function set_parent_component($componentname) {
807 if(!parent::set_parent_component($componentname)) {
808 return false;
811 if($this->parent_component == 'VFREEBUSY' || $this->parent_component == 'VALARM') {
812 // Most parameters become invalid in this case, the full allowed set is now:
813 $this->valid_parameters = array(
814 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
815 RFC2445_XNAME => RFC2445_OPTIONAL
819 return false;
824 class iCalendar_property_contact extends iCalendar_property {
826 var $name = 'CONTACT';
827 var $val_type = RFC2445_TYPE_TEXT;
829 function construct() {
830 $this->valid_parameters = array(
831 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
832 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
833 RFC2445_XNAME => RFC2445_OPTIONAL
838 class iCalendar_property_organizer extends iCalendar_property {
840 var $name = 'ORGANIZER';
841 var $val_type = RFC2445_TYPE_CAL_ADDRESS;
843 function construct() {
844 $this->valid_parameters = array(
845 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
846 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
847 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
848 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
849 RFC2445_XNAME => RFC2445_OPTIONAL
853 // TODO:
855 Conformance: This property MUST be specified in an iCalendar object
856 that specifies a group scheduled calendar entity. This property MUST
857 be specified in an iCalendar object that specifies the publication of
858 a calendar user's busy time. This property MUST NOT be specified in
859 an iCalendar object that specifies only a time zone definition or
860 that defines calendar entities that are not group scheduled entities,
861 but are entities only on a single user's calendar.
866 class iCalendar_property_recurrence_id extends iCalendar_property {
868 // TODO: can only be specified when defining recurring components in the calendar
870 Conformance: This property can be specified in an iCalendar object
871 containing a recurring calendar component.
873 Description: The full range of calendar components specified by a
874 recurrence set is referenced by referring to just the "UID" property
875 value corresponding to the calendar component. The "RECURRENCE-ID"
876 property allows the reference to an individual instance within the
877 recurrence set.
880 var $name = 'RECURRENCE-ID';
881 var $val_type = RFC2445_TYPE_DATE_TIME;
883 function construct() {
884 $this->valid_parameters = array(
885 'RANGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
886 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
887 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
888 RFC2445_XNAME => RFC2445_OPTIONAL
892 function is_valid_parameter($parameter, $value) {
894 $parameter = strtoupper($parameter);
896 if(!parent::is_valid_parameter($parameter, $value)) {
897 return false;
899 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
900 return false;
903 return true;
908 class iCalendar_property_related_to extends iCalendar_property {
910 var $name = 'RELATED-TO';
911 var $val_type = RFC2445_TYPE_TEXT;
913 // TODO: the value of this property must reference another component's UID
915 function construct() {
916 $this->valid_parameters = array(
917 'RELTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
918 RFC2445_XNAME => RFC2445_OPTIONAL
923 class iCalendar_property_url extends iCalendar_property {
925 var $name = 'URL';
926 var $val_type = RFC2445_TYPE_URI;
928 function construct() {
929 $this->valid_parameters = array(
930 RFC2445_XNAME => RFC2445_OPTIONAL
935 class iCalendar_property_uid extends iCalendar_property {
937 var $name = 'UID';
938 var $val_type = RFC2445_TYPE_TEXT;
940 function construct() {
941 $this->valid_parameters = array(
942 RFC2445_XNAME => RFC2445_OPTIONAL
945 // The exception to the rule: this is not a static value, so we
946 // generate it on-the-fly here. Guaranteed to be different for
947 // each instance of this property, too. Nice.
948 $this->val_default = Bennu::generate_guid();
952 // 4.8.5 Recurrence Component Properties
953 // -------------------------------------
955 class iCalendar_property_exdate extends iCalendar_property {
957 var $name = 'EXDATE';
958 var $val_type = RFC2445_TYPE_DATE_TIME;
959 var $val_multi = true;
961 function construct() {
962 $this->valid_parameters = array(
963 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
964 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
965 RFC2445_XNAME => RFC2445_OPTIONAL
969 function is_valid_parameter($parameter, $value) {
971 $parameter = strtoupper($parameter);
973 if(!parent::is_valid_parameter($parameter, $value)) {
974 return false;
976 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
977 return false;
980 return true;
985 class iCalendar_property_exrule extends iCalendar_property {
987 var $name = 'EXRULE';
988 var $val_type = RFC2445_TYPE_RECUR;
990 function construct() {
991 $this->valid_parameters = array(
992 RFC2445_XNAME => RFC2445_OPTIONAL
997 class iCalendar_property_rdate extends iCalendar_property {
999 var $name = 'RDATE';
1000 var $val_type = RFC2445_TYPE_DATE_TIME;
1001 var $val_multi = true;
1003 function construct() {
1004 $this->valid_parameters = array(
1005 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
1006 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1007 RFC2445_XNAME => RFC2445_OPTIONAL
1011 function is_valid_parameter($parameter, $value) {
1013 $parameter = strtoupper($parameter);
1015 if(!parent::is_valid_parameter($parameter, $value)) {
1016 return false;
1018 if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME' || $value == 'PERIOD')) {
1019 return false;
1022 return true;
1027 class iCalendar_property_rrule extends iCalendar_property {
1029 var $name = 'RRULE';
1030 var $val_type = RFC2445_TYPE_RECUR;
1032 function construct() {
1033 $this->valid_parameters = array(
1034 RFC2445_XNAME => RFC2445_OPTIONAL
1039 // TODO: 4.8.6 Alarm Component Properties
1041 // 4.8.7 Change Management Component Properties
1042 // --------------------------------------------
1044 class iCalendar_property_created extends iCalendar_property {
1046 var $name = 'CREATED';
1047 var $val_type = RFC2445_TYPE_DATE_TIME;
1049 function construct() {
1050 $this->valid_parameters = array(
1051 RFC2445_XNAME => RFC2445_OPTIONAL
1055 function is_valid_value($value) {
1056 if(!parent::is_valid_value($value)) {
1057 return false;
1059 // Time MUST be in UTC format
1060 return(substr($value, -1) == 'Z');
1064 class iCalendar_property_dtstamp extends iCalendar_property {
1066 var $name = 'DTSTAMP';
1067 var $val_type = RFC2445_TYPE_DATE_TIME;
1069 function construct() {
1070 $this->valid_parameters = array(
1071 RFC2445_XNAME => RFC2445_OPTIONAL
1075 function is_valid_value($value) {
1076 if(!parent::is_valid_value($value)) {
1077 return false;
1079 // Time MUST be in UTC format
1080 return(substr($value, -1) == 'Z');
1084 class iCalendar_property_last_modified extends iCalendar_property {
1086 var $name = 'LAST-MODIFIED';
1087 var $val_type = RFC2445_TYPE_DATE_TIME;
1089 function construct() {
1090 $this->valid_parameters = array(
1091 RFC2445_XNAME => RFC2445_OPTIONAL
1095 function is_valid_value($value) {
1096 if(!parent::is_valid_value($value)) {
1097 return false;
1099 // Time MUST be in UTC format
1100 return(substr($value, -1) == 'Z');
1104 class iCalendar_property_sequence extends iCalendar_property {
1106 var $name = 'SEQUENCE';
1107 var $val_type = RFC2445_TYPE_INTEGER;
1108 var $val_default = 0;
1110 function construct() {
1111 $this->valid_parameters = array(
1112 RFC2445_XNAME => RFC2445_OPTIONAL
1116 function is_valid_value($value) {
1117 if(!parent::is_valid_value($value)) {
1118 return false;
1120 $value = intval($value);
1121 return ($value >= 0);
1125 // 4.8.8 Miscellaneous Component Properties
1126 // ----------------------------------------
1128 class iCalendar_property_x extends iCalendar_property {
1130 var $name = RFC2445_XNAME;
1131 var $val_type = NULL;
1133 function construct() {
1134 $this->valid_parameters = array(
1135 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1136 RFC2445_XNAME => RFC2445_OPTIONAL
1140 function set_name($name) {
1142 $name = strtoupper($name);
1144 if(rfc2445_is_xname($name)) {
1145 $this->name = $name;
1146 return true;
1149 return false;
1153 class iCalendar_property_request_status extends iCalendar_property {
1155 // IMPORTANT NOTE: This property value includes TEXT fields
1156 // separated by semicolons. Unfortunately, auto-value-formatting
1157 // cannot be used in this case. As an exception, the value passed
1158 // to this property MUST be already escaped.
1160 var $name = 'REQUEST-STATUS';
1161 var $val_type = RFC2445_TYPE_TEXT;
1163 function construct() {
1164 $this->valid_parameters = array(
1165 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
1166 RFC2445_XNAME => RFC2445_OPTIONAL
1170 function is_valid_value($value) {
1171 if(!is_string($value) || empty($value)) {
1172 return false;
1175 $len = strlen($value);
1176 $parts = array();
1177 $from = 0;
1178 $escch = false;
1180 for($i = 0; $i < $len; ++$i) {
1181 if($value{$i} == ';' && !$escch) {
1182 // Token completed
1183 $parts[] = substr($value, $from, $i - $from);
1184 $from = $i + 1;
1185 continue;
1187 $escch = ($value{$i} == '\\');
1189 // Add one last token with the remaining text; if the value
1190 // ended with a ';' it was illegal, so check that this token
1191 // is not the empty string.
1192 $parts[] = substr($value, $from);
1194 $count = count($parts);
1196 // May have 2 or 3 tokens (last one is optional)
1197 if($count != 2 && $count != 3) {
1198 return false;
1201 // REMEMBER: if ANY part is empty, we have an illegal value
1203 // First token must be hierarchical numeric status (3 levels max)
1204 if(strlen($parts[0]) == 0) {
1205 return false;
1208 if($parts[0]{0} < '1' || $parts[0]{0} > '4') {
1209 return false;
1212 $len = strlen($parts[0]);
1214 // Max 3 levels, and can't end with a period
1215 if($len > 5 || $parts[0]{$len - 1} == '.') {
1216 return false;
1219 for($i = 1; $i < $len; ++$i) {
1220 if(($i & 1) == 1 && $parts[0]{$i} != '.') {
1221 // Even-indexed chars must be periods
1222 return false;
1224 else if(($i & 1) == 0 && ($parts[0]{$i} < '0' || $parts[0]{$i} > '9')) {
1225 // Odd-indexed chars must be numbers
1226 return false;
1230 // Second and third tokens must be TEXT, and already escaped, so
1231 // they are not allowed to have UNESCAPED semicolons, commas, slashes,
1232 // or any newlines at all
1234 for($i = 1; $i < $count; ++$i) {
1235 if(strpos($parts[$i], "\n") !== false) {
1236 return false;
1239 $len = strlen($parts[$i]);
1240 if($len == 0) {
1241 // Cannot be empty
1242 return false;
1245 $parts[$i] .= '#'; // This guard token saves some conditionals in the loop
1247 for($j = 0; $j < $len; ++$j) {
1248 $thischar = $parts[$i]{$j};
1249 $nextchar = $parts[$i]{$j + 1};
1250 if($thischar == '\\') {
1251 // Next char must now be one of ";,\nN"
1252 if($nextchar != ';' && $nextchar != ',' && $nextchar != '\\' &&
1253 $nextchar != 'n' && $nextchar != 'N') {
1254 return false;
1257 // OK, this escaped sequence is correct, bypass next char
1258 ++$j;
1259 continue;
1261 if($thischar == ';' || $thischar == ',' || $thischar == '\\') {
1262 // This wasn't escaped as it should
1263 return false;
1268 return true;
1271 function set_value($value) {
1272 // Must override this, otherwise the value would be quoted again
1273 if($this->is_valid_value($value)) {
1274 $this->value = $value;
1275 return true;
1278 return false;
1284 #######################
1286 class iCalendar_property_class extends iCalendar_property {
1288 var $name = 'CLASS';
1289 var $val_type = RFC2445_TYPE_TEXT;
1291 function construct() {
1292 $this->valid_parameters = array(
1293 RFC2445_XNAME => RFC2445_OPTIONAL