|
4
|
1 <?php
|
|
|
2
|
|
|
3 namespace Sabre\VObject\Component;
|
|
|
4
|
|
|
5 use Sabre\VObject;
|
|
|
6
|
|
|
7 /**
|
|
|
8 * The VCalendar component
|
|
|
9 *
|
|
|
10 * This component adds functionality to a component, specific for a VCALENDAR.
|
|
|
11 *
|
|
|
12 * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
|
|
13 * @author Evert Pot (http://evertpot.com/)
|
|
|
14 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
|
15 */
|
|
|
16 class VCalendar extends VObject\Document {
|
|
|
17
|
|
|
18 static $defaultName = 'VCALENDAR';
|
|
|
19
|
|
|
20 /**
|
|
|
21 * Returns a list of all 'base components'. For instance, if an Event has
|
|
|
22 * a recurrence rule, and one instance is overridden, the overridden event
|
|
|
23 * will have the same UID, but will be excluded from this list.
|
|
|
24 *
|
|
|
25 * VTIMEZONE components will always be excluded.
|
|
|
26 *
|
|
|
27 * @param string $componentName filter by component name
|
|
|
28 * @return array
|
|
|
29 */
|
|
|
30 public function getBaseComponents($componentName = null) {
|
|
|
31
|
|
|
32 $components = array();
|
|
|
33 foreach($this->children as $component) {
|
|
|
34
|
|
|
35 if (!$component instanceof VObject\Component)
|
|
|
36 continue;
|
|
|
37
|
|
|
38 if (isset($component->{'RECURRENCE-ID'}))
|
|
|
39 continue;
|
|
|
40
|
|
|
41 if ($componentName && $component->name !== strtoupper($componentName))
|
|
|
42 continue;
|
|
|
43
|
|
|
44 if ($component->name === 'VTIMEZONE')
|
|
|
45 continue;
|
|
|
46
|
|
|
47 $components[] = $component;
|
|
|
48
|
|
|
49 }
|
|
|
50
|
|
|
51 return $components;
|
|
|
52
|
|
|
53 }
|
|
|
54
|
|
|
55 /**
|
|
|
56 * If this calendar object, has events with recurrence rules, this method
|
|
|
57 * can be used to expand the event into multiple sub-events.
|
|
|
58 *
|
|
|
59 * Each event will be stripped from it's recurrence information, and only
|
|
|
60 * the instances of the event in the specified timerange will be left
|
|
|
61 * alone.
|
|
|
62 *
|
|
|
63 * In addition, this method will cause timezone information to be stripped,
|
|
|
64 * and normalized to UTC.
|
|
|
65 *
|
|
|
66 * This method will alter the VCalendar. This cannot be reversed.
|
|
|
67 *
|
|
|
68 * This functionality is specifically used by the CalDAV standard. It is
|
|
|
69 * possible for clients to request expand events, if they are rather simple
|
|
|
70 * clients and do not have the possibility to calculate recurrences.
|
|
|
71 *
|
|
|
72 * @param DateTime $start
|
|
|
73 * @param DateTime $end
|
|
|
74 * @return void
|
|
|
75 */
|
|
|
76 public function expand(\DateTime $start, \DateTime $end) {
|
|
|
77
|
|
|
78 $newEvents = array();
|
|
|
79
|
|
|
80 foreach($this->select('VEVENT') as $key=>$vevent) {
|
|
|
81
|
|
|
82 if (isset($vevent->{'RECURRENCE-ID'})) {
|
|
|
83 unset($this->children[$key]);
|
|
|
84 continue;
|
|
|
85 }
|
|
|
86
|
|
|
87
|
|
|
88 if (!$vevent->rrule) {
|
|
|
89 unset($this->children[$key]);
|
|
|
90 if ($vevent->isInTimeRange($start, $end)) {
|
|
|
91 $newEvents[] = $vevent;
|
|
|
92 }
|
|
|
93 continue;
|
|
|
94 }
|
|
|
95
|
|
|
96 $uid = (string)$vevent->uid;
|
|
|
97 if (!$uid) {
|
|
|
98 throw new \LogicException('Event did not have a UID!');
|
|
|
99 }
|
|
|
100
|
|
|
101 $it = new VObject\RecurrenceIterator($this, $vevent->uid);
|
|
|
102 $it->fastForward($start);
|
|
|
103
|
|
|
104 while($it->valid() && $it->getDTStart() < $end) {
|
|
|
105
|
|
|
106 if ($it->getDTEnd() > $start) {
|
|
|
107
|
|
|
108 $newEvents[] = $it->getEventObject();
|
|
|
109
|
|
|
110 }
|
|
|
111 $it->next();
|
|
|
112
|
|
|
113 }
|
|
|
114 unset($this->children[$key]);
|
|
|
115
|
|
|
116 }
|
|
|
117
|
|
|
118 foreach($newEvents as $newEvent) {
|
|
|
119
|
|
|
120 foreach($newEvent->children as $child) {
|
|
|
121 if ($child instanceof VObject\Property\DateTime &&
|
|
|
122 $child->getDateType() == VObject\Property\DateTime::LOCALTZ) {
|
|
|
123 $child->setDateTime($child->getDateTime(),VObject\Property\DateTime::UTC);
|
|
|
124 }
|
|
|
125 }
|
|
|
126
|
|
|
127 $this->add($newEvent);
|
|
|
128
|
|
|
129 }
|
|
|
130
|
|
|
131 // Removing all VTIMEZONE components
|
|
|
132 unset($this->VTIMEZONE);
|
|
|
133
|
|
|
134 }
|
|
|
135
|
|
|
136 /**
|
|
|
137 * Validates the node for correctness.
|
|
|
138 * An array is returned with warnings.
|
|
|
139 *
|
|
|
140 * Every item in the array has the following properties:
|
|
|
141 * * level - (number between 1 and 3 with severity information)
|
|
|
142 * * message - (human readable message)
|
|
|
143 * * node - (reference to the offending node)
|
|
|
144 *
|
|
|
145 * @return array
|
|
|
146 */
|
|
|
147 /*
|
|
|
148 public function validate() {
|
|
|
149
|
|
|
150 $warnings = array();
|
|
|
151
|
|
|
152 $version = $this->select('VERSION');
|
|
|
153 if (count($version)!==1) {
|
|
|
154 $warnings[] = array(
|
|
|
155 'level' => 1,
|
|
|
156 'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time',
|
|
|
157 'node' => $this,
|
|
|
158 );
|
|
|
159 } else {
|
|
|
160 if ((string)$this->VERSION !== '2.0') {
|
|
|
161 $warnings[] = array(
|
|
|
162 'level' => 1,
|
|
|
163 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
|
|
|
164 'node' => $this,
|
|
|
165 );
|
|
|
166 }
|
|
|
167 }
|
|
|
168 $version = $this->select('PRODID');
|
|
|
169 if (count($version)!==1) {
|
|
|
170 $warnings[] = array(
|
|
|
171 'level' => 2,
|
|
|
172 'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time',
|
|
|
173 'node' => $this,
|
|
|
174 );
|
|
|
175 }
|
|
|
176 if (count($this->CALSCALE) > 1) {
|
|
|
177 $warnings[] = array(
|
|
|
178 'level' => 2,
|
|
|
179 'message' => 'The CALSCALE property must not be specified more than once.',
|
|
|
180 'node' => $this,
|
|
|
181 );
|
|
|
182 }
|
|
|
183 if (count($this->METHOD) > 1) {
|
|
|
184 $warnings[] = array(
|
|
|
185 'level' => 2,
|
|
|
186 'message' => 'The METHOD property must not be specified more than once.',
|
|
|
187 'node' => $this,
|
|
|
188 );
|
|
|
189 }
|
|
|
190
|
|
|
191 $allowedComponents = array(
|
|
|
192 'VEVENT',
|
|
|
193 'VTODO',
|
|
|
194 'VJOURNAL',
|
|
|
195 'VFREEBUSY',
|
|
|
196 'VTIMEZONE',
|
|
|
197 );
|
|
|
198 $allowedProperties = array(
|
|
|
199 'PRODID',
|
|
|
200 'VERSION',
|
|
|
201 'CALSCALE',
|
|
|
202 'METHOD',
|
|
|
203 );
|
|
|
204 $componentsFound = 0;
|
|
|
205 foreach($this->children as $child) {
|
|
|
206 if($child instanceof Component) {
|
|
|
207 $componentsFound++;
|
|
|
208 if (!in_array($child->name, $allowedComponents)) {
|
|
|
209 $warnings[] = array(
|
|
|
210 'level' => 1,
|
|
|
211 'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component",
|
|
|
212 'node' => $this,
|
|
|
213 );
|
|
|
214 }
|
|
|
215 }
|
|
|
216 if ($child instanceof Property) {
|
|
|
217 if (!in_array($child->name, $allowedProperties)) {
|
|
|
218 $warnings[] = array(
|
|
|
219 'level' => 2,
|
|
|
220 'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component",
|
|
|
221 'node' => $this,
|
|
|
222 );
|
|
|
223 }
|
|
|
224 }
|
|
|
225 }
|
|
|
226
|
|
|
227 if ($componentsFound===0) {
|
|
|
228 $warnings[] = array(
|
|
|
229 'level' => 1,
|
|
|
230 'message' => 'An iCalendar object must have at least 1 component.',
|
|
|
231 'node' => $this,
|
|
|
232 );
|
|
|
233 }
|
|
|
234
|
|
|
235 return array_merge(
|
|
|
236 $warnings,
|
|
|
237 parent::validate()
|
|
|
238 );
|
|
|
239
|
|
|
240 }
|
|
|
241 */
|
|
|
242
|
|
|
243 }
|
|
|
244
|