]> git.mxchange.org Git - friendica-addons.git/blob - dav/sabre-vobject/README.md
Merge remote-tracking branch 'upstream/master' into 1601-gcontact
[friendica-addons.git] / dav / sabre-vobject / README.md
1 # SabreTooth VObject library
2
3 [![Build Status](https://secure.travis-ci.org/evert/sabre-vobject.png?branch=master)](http://travis-ci.org/evert/sabre-vobject)
4
5 The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545)
6 and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP.
7 The goal of the VObject library is to create a very complete library, with an easy to use API.
8
9 This project is a spin-off from [SabreDAV](http://code.google.com/p/sabredav/), where it has
10 been used for several years. The VObject library has 100% unittest coverage.
11
12 # Installation
13
14 VObject requires PHP 5.3, and should be installed using composer.
15 The general composer instructions can be found on the [composer website](http://getcomposer.org/doc/00-intro.md composer website).
16
17 After that, just declare the vobject dependency as follows:
18
19 ```
20 "require" : {
21     "sabre/vobject" : "dev-master"
22 }
23 ```
24
25 Then, run `composer.phar update` and you should be good. As soon as the first release is out, you should switch `dev-master` to `2.0.*` though.
26
27 # Usage
28
29 ## Parsing
30
31 For our example, we will be using the following vcard:
32
33 ```
34 BEGIN:VCARD
35 VERSION:3.0
36 PRODID:-//Sabre//Sabre VObject 2.0//EN
37 N:Planck;Max;;;
38 FN:Max Planck
39 EMAIL;TYPE=WORK:mplanck@example.org
40 item1.TEL;TYPE=CELL:(+49)3144435678
41 item1.X-ABLabel:Private cell
42 item2.TEL;TYPE=WORK:(+49)5554564744
43 item2.X-ABLabel:Work
44 END:VCARD
45 ```
46
47
48 If we want to just print out Max' full name, you can just use property access:
49
50
51 ```php
52
53 use Sabre\VObject;
54
55 $card = VObject\Reader::read($data);
56 echo $card->FN;
57
58 ```
59
60 ## Changing properties
61
62 Creating properties is pretty similar. If we like to add his birthday, we just
63 set the property:
64
65 ```php
66
67 $card->BDAY = '1858-04-23';
68
69 ```
70
71 Note that in the previous example, we're actually updating any existing BDAY that
72 may already exist. If we want to add a new property, without overwriting the previous
73 we can do this with the `add` method. 
74
75 ```php
76
77 $card->add('EMAIL','max@example.org');
78
79 ```
80
81 ## Parameters
82
83 If we want to also specify that this is max' home email addresses, we can do this with
84 a third parameter:
85
86 ```
87
88 $card->add('EMAIL', 'max@example'org', array('type' => 'HOME'));
89
90 ```
91
92 If we want to read out all the email addresses and their type, this would look something
93 like this:
94
95 ```
96 foreach($card->EMAIL as $email) {
97
98     echo $email['TYPE'], ' - ', $email;
99
100 }
101 ```
102
103 ## Groups
104
105 In our example, you can see that the TEL properties are prefixed. These are 'groups' and
106 allow you to group multiple related properties together. The group can be any user-defined
107 name.
108
109 This particular example as generated by the OS X addressbook, which uses the `X-ABLabel`
110 to allow the user to specify custom labels for properties. OS X addressbook uses groups
111 to tie the label to the property.
112
113 The VObject library simply ignores the group if you don't specify it, so this will work:
114
115 ```php
116
117 foreach($card->TEL as $tel) {
118     echo $tel, "\n";
119 }
120 ```
121
122 But if you would like to target a specific group + property, this is possible too:
123
124 ```php
125
126 echo $card->{'ITEM1.TEL'};
127
128 ```
129
130 So if you would like to show all the phone numbers, along with their custom label, the
131 following syntax is used:
132
133 ```
134 foreach($card->TEL as $tel) {
135
136     echo $card->{$tel->group . '.X-ABLABEL'}, ": ";
137     echo $tel, "\n";
138
139 }
140 ```
141
142 ## Serializing / Saving
143
144 If you want to generate your updated VObject, you can simply call the serialize() method.
145
146 ```
147
148 echo $card->serialize();
149
150 ```
151
152 ## Components
153
154 iCalendar, unlike vCards always have sub-components. Where vCards are often just a flat
155 list, iCalendar objects tend to have a tree-like structure. For the following paragraphs,
156 we will use the following object as the example:
157
158 ```
159 BEGIN:VCALENDAR
160 VERSION:2.0
161 PRODID:-//Sabre//Sabre VObject 2.0//EN
162 BEGIN:VEVENT
163 SUMMARY:Curiosity landing
164 DTSTART:20120806T051439Z
165 LOCATION:Mars
166 END:VEVENT
167 END:VCALENDAR
168 ```
169
170 Since events, tasks and journals are always in a sub component, this is also how we
171 access them.
172
173 ```php
174
175 use Sabre\VObject;
176
177 $calendar = VObject\Reader::read($data);
178 echo $calendar->VEVENT->SUMMARY;
179
180 ```
181
182 Adding components to a calendar is done with a factory method:
183
184 ```php
185
186 $event = VObject\Component::create('VEVENT');
187 $calendar->add($event);
188
189 $event->SUMMARY = 'Curiosity launch';
190 $event->DTSTART = '20111126T150202Z';
191 $event->LOCATION = 'Cape Carnival';
192
193 ```
194
195 By the way.. cloning also works as expected, as the entire structure is cloned along with it:
196
197 ```php
198
199 $clonedEvent = clone $calendar->VEVENT[0];
200 $calendar->add($clonedEvent);
201
202 ```
203
204 ## Date and time handling
205
206 If you ever had to deal with iCalendar timezones, you know it can be complicated.
207 The way timezones are specified is flawed, which is something I may write an essay about some
208 day. VObject does its best to determine the correct timezone. Many standard formats
209 have been tested and verified, and special code has been implemented for special-casing
210 microsoft generated timezone information, and others.
211
212 To get a real php `DateTime` object, you can request this as follows:
213
214 ```
215 $event = $calendar->VEVENT;
216 $start = $event->DTSTART->getDateTime();
217 echo $start->format(\DateTime::W3C);
218 ```
219
220 To set the property with a DateTime object, you can use the following syntax:
221
222 ```
223 $dateTime = new \DateTime('2012-08-07 23:53:00', new DateTimeZone('Europe/Amsterdam'));
224 $event->DTSTART->setDateTime($dateTime, VObject\Property\DateTime::DATE);
225 ```
226
227 The second argument specifies the type of date you're setting. The following three
228 options exist:
229
230 1. `LOCAL` This is a floating time, with no timezone information. This basically specifies that the event happens in whatever the timezone's currently in. This would be encoded as `DTSTART;VALUE=DATE-TIME:20120807235300`
231 2. `UTC` This specifies that the time should be encoded as a UTC time. This is encoded as `DTSTART;VALUE=DATE-TIME:20120807205300Z`. Note the extra Z and the fact that it's two hours 'earlier'.
232 3. `LOCALTZ` specifies that it's supposed to be encoded in its local timezone. For example `DTSTART;VALUE=DATE-TIME;TZID=Europe/Amsterdam:20120807235300`.
233 4. `DATE` This is a date-only, and does not contain the time. In this case this would be encoded as `DTSTART;VALUE=DATE:20120807`.
234
235 A few important notes:
236
237 * When a `TZID` is specified, there should also be a matching `VTIMEZONE` object with all the timezone information. VObject cannot currently automatically embed this. However, in reality other clients seem to do fine without this information. Yet, for completeness, this will be added in the future.
238 * As mentioned, the timezone-determination process may sometimes fail. Report any issues you find, and I'll be quick to add workarounds!
239
240 ## Recurrence rules
241
242 Recurrence rules allow events to recur, for example for a weekly meeting, or an anniversary.
243 This is done with the `RRULE` property. The `RRULE` property allows for a LOT of different
244 rules. VObject only implements the ones that actually appear in calendar software.
245
246 To read more about `RRULE` and all the options, check out [RFC5545](https://tools.ietf.org/html/rfc5545#section-3.8.5).
247 VObject supports the following options:
248
249 1. `UNTIL` for an end date.
250 2. `INTERVAL` for for example "every 2 days".
251 3. `COUNT` to stop recurring after x items.
252 4. `FREQ=DAILY` to recur every day, and `BYDAY` to limit it to certain days.
253 5. `FREQ=WEEKLY` to recur every week, `BYDAY` to expand this to multiple weekdays in every week and `WKST` to specify on which day the week starts.
254 6. `FREQ=MONTHLY` to recur every month, `BYMONTHDAY` to expand this to certain days in a month, `BYDAY` to expand it to certain weekdays occuring in a month, and `BYSETPOS` to limit the last two expansions.
255 7. `FREQ=YEARLY` to recur every year, `BYMONTH` to expand that to certain months in a year, and `BYDAY` and `BYWEEKDAY` to expand the `BYMONTH` rule even further.
256
257 VObject supports the `EXDATE` property for exclusions, but not yet the `RDATE` and `EXRULE` 
258 properties. If you're interested in this, please file a github issue, as this will put it
259 on my radar.
260
261 This is a bit of a complex subject to go in excruciating detail. The
262 [RFC](https://tools.ietf.org/html/rfc5545#section-3.8.5) has a lot of examples though.
263
264 The hard part is not to write the RRULE, it is to expand them. The most complex and
265 hard-to-read code is hidden in this component. Dragons be here.
266
267 So, if we have a meeting every 2nd monday of the month, this would be specified as such:
268
269 ```
270 BEGIN:VCALENDAR
271   VERSION:2.0
272   BEGIN:VEVENT
273     UID:1102c450-e0d7-11e1-9b23-0800200c9a66
274     DTSTART:20120109T140000Z
275     RRULE:FREQ=MONTHLY;BYDAY=MO;BYSETPOS=2
276   END:VEVENT
277 END:VCALENDAR
278 ```
279
280 Note that normally it's not allowed to indent the object like this, but it does make
281 it easier to read. This is also the first time I added in a UID, which is required
282 for all VEVENT, VTODO and VJOURNAL objects!
283
284 To figure out all the meetings for this year, we can use the following syntax:
285
286 ```php
287 use Sabre\VObject;
288
289 $calendar = VObject\Reader::read($data);
290 $calendar->expand(new DateTime('2012-01-01'), new DateTime('2012-12-31'));
291 ```
292
293 What the expand method does, is look at its inner events, and expand the recurring
294 rule. Our calendar now contains 12 events. The first will have its RRULE stripped,
295 and every subsequent VEVENT has the correct meeting date and a `RECURRENCE-ID` set.
296
297 This results in something like this:
298
299 ```
300 BEGIN:VCALENDAR
301   VERSION:2.0
302   BEGIN:VEVENT
303     UID:1102c450-e0d7-11e1-9b23-0800200c9a66
304     DTSTART:20120109T140000Z
305   END:VEVENT
306   BEGIN:VEVENT
307     UID:1102c450-e0d7-11e1-9b23-0800200c9a66
308     RECURRENCE-ID:20120213T140000Z
309     DTSTART:20120213T140000Z
310   END:VEVENT
311   BEGIN:VEVENT
312     UID:1102c450-e0d7-11e1-9b23-0800200c9a66
313     RECURRENCE-ID:20120312T140000Z
314     DTSTART:20120312T140000Z
315   END:VEVENT
316   ..etc..
317 END:VCALENDAR
318 ```
319
320 To show the list of dates, we would do this as such:
321
322 ```
323 foreach($calendar->VEVENT as $event) {
324     echo $event->DTSTART->getDateTime()->format(\DateTime::ATOM);
325 }
326 ```
327
328 In a recurring event, single instances can also be overriden. VObject also takes these
329 into consideration. The reason we needed to specify a start and end-date, is because
330 some recurrence rules can be 'never ending'.
331
332 You should make sure you pick a sane date-range. Because if you pick a 50 year
333 time-range, for a daily recurring event; this would result in over 18K objects.
334
335 # Free-busy report generation
336
337 Some calendaring software can make use of FREEBUSY reports to show when people are
338 available.
339
340 You can automatically generate these reports from calendars using the `FreeBusyGenerator`.
341
342 Example based on our last event:
343
344 ```
345
346 // We're giving it the calendar object. It's also possible to specify multiple objects,
347 // by setting them as an array.
348 //
349 // We must also specify a start and end date, because recurring events are expanded.
350 $fbGenerator = new VObject\FreeBusyGenerator(
351     new DateTime('2012-01-01'),
352     new DateTime('2012-12-31'),
353     $calendar
354 );
355
356 // Grabbing the report
357 $freebusy = $fbGenerator->result();
358
359 // The freebusy report is another VCALENDAR object, so we can serialize it as usual:
360 echo $freebusy->serialize();
361 ```
362
363 The output of this script will look like this:
364
365 ```
366 BEGIN:VCALENDAR
367 VERSION:2.0
368 PRODID:-//Sabre//Sabre VObject 2.0//EN
369 CALSCALE:GREGORIAN
370 BEGIN:VFREEBUSY
371 DTSTART;VALUE=DATE-TIME:20111231T230000Z
372 DTEND;VALUE=DATE-TIME:20111231T230000Z
373 DTSTAMP;VALUE=DATE-TIME:20120808T131628Z
374 FREEBUSY;FBTYPE=BUSY:20120109T140000Z/20120109T140000Z
375 FREEBUSY;FBTYPE=BUSY:20120213T140000Z/20120213T140000Z
376 FREEBUSY;FBTYPE=BUSY:20120312T140000Z/20120312T140000Z
377 FREEBUSY;FBTYPE=BUSY:20120409T140000Z/20120409T140000Z
378 FREEBUSY;FBTYPE=BUSY:20120514T140000Z/20120514T140000Z
379 FREEBUSY;FBTYPE=BUSY:20120611T140000Z/20120611T140000Z
380 FREEBUSY;FBTYPE=BUSY:20120709T140000Z/20120709T140000Z
381 FREEBUSY;FBTYPE=BUSY:20120813T140000Z/20120813T140000Z
382 FREEBUSY;FBTYPE=BUSY:20120910T140000Z/20120910T140000Z
383 FREEBUSY;FBTYPE=BUSY:20121008T140000Z/20121008T140000Z
384 FREEBUSY;FBTYPE=BUSY:20121112T140000Z/20121112T140000Z
385 FREEBUSY;FBTYPE=BUSY:20121210T140000Z/20121210T140000Z
386 END:VFREEBUSY
387 END:VCALENDAR
388 ```