~ubuntu-branches/ubuntu/vivid/php-horde-icalendar/vivid-proposed

« back to all changes in this revision

Viewing changes to Horde_Icalendar-2.0.6/lib/Horde/Icalendar/Vfreebusy.php

  • Committer: Package Import Robot
  • Author(s): Mathieu Parent
  • Date: 2013-06-24 10:54:25 UTC
  • mfrom: (1.1.5)
  • Revision ID: package-import@ubuntu.com-20130624105425-adwwv5oiqnng8xew
Tags: 2.0.6-1
New upstream version 2.0.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Class representing vFreebusy components.
 
4
 *
 
5
 * Copyright 2003-2013 Horde LLC (http://www.horde.org/)
 
6
 *
 
7
 * See the enclosed file COPYING for license information (LGPL). If you
 
8
 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 
9
 *
 
10
 * @todo Don't use timestamps
 
11
 *
 
12
 * @author   Mike Cochrane <mike@graftonhall.co.nz>
 
13
 * @category Horde
 
14
 * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 
15
 * @package  Icalendar
 
16
 */
 
17
class Horde_Icalendar_Vfreebusy extends Horde_Icalendar
 
18
{
 
19
    /**
 
20
     * The component type of this class.
 
21
     *
 
22
     * @var string
 
23
     */
 
24
    public $type = 'vFreebusy';
 
25
 
 
26
    /**
 
27
     * TODO
 
28
     *
 
29
     * @var array
 
30
     */
 
31
    protected $_busyPeriods = array();
 
32
 
 
33
    /**
 
34
     * TODO
 
35
     *
 
36
     * @var array
 
37
     */
 
38
    protected $_extraParams = array();
 
39
 
 
40
    /**
 
41
     * Parses a string containing vFreebusy data.
 
42
     *
 
43
     * @param string $data     The data to parse.
 
44
     * @param $type TODO
 
45
     * @param $charset TODO
 
46
     */
 
47
    public function parsevCalendar($data, $type = null, $charset = null)
 
48
    {
 
49
        parent::parsevCalendar($data, 'VFREEBUSY', $charset);
 
50
 
 
51
        // Do something with all the busy periods.
 
52
        foreach ($this->_attributes as $key => $attribute) {
 
53
            if ($attribute['name'] != 'FREEBUSY') {
 
54
                continue;
 
55
            }
 
56
            foreach ($attribute['values'] as $value) {
 
57
                $params = isset($attribute['params'])
 
58
                    ? $attribute['params']
 
59
                    : array();
 
60
                if (isset($value['duration'])) {
 
61
                    $this->addBusyPeriod('BUSY', $value['start'], null,
 
62
                                         $value['duration'], $params);
 
63
                } else {
 
64
                    $this->addBusyPeriod('BUSY', $value['start'],
 
65
                                         $value['end'], null, $params);
 
66
                }
 
67
            }
 
68
            unset($this->_attributes[$key]);
 
69
        }
 
70
    }
 
71
 
 
72
    /**
 
73
     * Returns the component exported as string.
 
74
     *
 
75
     * @return string  The exported vFreeBusy information according to the
 
76
     *                 iCalendar format specification.
 
77
     */
 
78
    public function exportvCalendar()
 
79
    {
 
80
        foreach ($this->_busyPeriods as $start => $end) {
 
81
            $periods = array(array('start' => $start, 'end' => $end));
 
82
            $this->setAttribute('FREEBUSY', $periods,
 
83
                                isset($this->_extraParams[$start])
 
84
                                ? $this->_extraParams[$start] : array());
 
85
        }
 
86
 
 
87
        $res = $this->_exportvData('VFREEBUSY');
 
88
 
 
89
        foreach ($this->_attributes as $key => $attribute) {
 
90
            if ($attribute['name'] == 'FREEBUSY') {
 
91
                unset($this->_attributes[$key]);
 
92
            }
 
93
        }
 
94
 
 
95
        return $res;
 
96
    }
 
97
 
 
98
    /**
 
99
     * Returns a display name for this object.
 
100
     *
 
101
     * @return string  A clear text name for displaying this object.
 
102
     */
 
103
    public function getName()
 
104
    {
 
105
        $name = '';
 
106
 
 
107
        try {
 
108
            $method = !empty($this->_container)
 
109
                ? $this->_container->getAttribute('METHOD')
 
110
                : 'PUBLISH';
 
111
            if ($method == 'PUBLISH') {
 
112
                $attr = 'ORGANIZER';
 
113
            } elseif ($method == 'REPLY') {
 
114
                $attr = 'ATTENDEE';
 
115
            }
 
116
        } catch (Horde_Icalendar_Exception $e) {
 
117
            $attr = 'ORGANIZER';
 
118
        }
 
119
 
 
120
        try {
 
121
            $name = $this->getAttribute($attr, true);
 
122
            if (isset($name[0]['CN'])) {
 
123
                return $name[0]['CN'];
 
124
            }
 
125
        } catch (Horde_Icalendar_Exception $e) {}
 
126
 
 
127
        try {
 
128
            $name = parse_url($this->getAttribute($attr));
 
129
            return $name['path'];
 
130
        } catch (Horde_Icalendar_Exception $e) {
 
131
            return '';
 
132
        }
 
133
    }
 
134
 
 
135
    /**
 
136
     * Returns the email address for this object.
 
137
     *
 
138
     * @return string  The email address of this object's owner.
 
139
     */
 
140
    public function getEmail()
 
141
    {
 
142
        $name = '';
 
143
 
 
144
        try {
 
145
            $method = !empty($this->_container)
 
146
                ? $this->_container->getAttribute('METHOD')
 
147
                : 'PUBLISH';
 
148
            if ($method == 'PUBLISH') {
 
149
                $attr = 'ORGANIZER';
 
150
            } elseif ($method == 'REPLY') {
 
151
                $attr = 'ATTENDEE';
 
152
            }
 
153
        } catch (Horde_Icalendar_Exception $e) {
 
154
            $attr = 'ORGANIZER';
 
155
        }
 
156
 
 
157
        try {
 
158
            $name = parse_url($this->getAttribute($attr));
 
159
            return $name['path'];
 
160
        } catch (Horde_Icalendar_Exception $e) {
 
161
            return '';
 
162
        }
 
163
    }
 
164
 
 
165
    /**
 
166
     * Returns the busy periods.
 
167
     *
 
168
     * @return array  All busy periods.
 
169
     */
 
170
    public function getBusyPeriods()
 
171
    {
 
172
        return $this->_busyPeriods;
 
173
    }
 
174
 
 
175
    /**
 
176
     * Returns any additional freebusy parameters.
 
177
     *
 
178
     * @return array  Additional parameters of the freebusy periods.
 
179
     */
 
180
    public function getExtraParams()
 
181
    {
 
182
        return $this->_extraParams;
 
183
    }
 
184
 
 
185
    /**
 
186
     * Returns all the free periods of time in a given period.
 
187
     *
 
188
     * @param integer $startStamp  The start timestamp.
 
189
     * @param integer $endStamp    The end timestamp.
 
190
     *
 
191
     * @return array  A hash with free time periods, the start times as the
 
192
     *                keys and the end times as the values.
 
193
     */
 
194
    public function getFreePeriods($startStamp, $endStamp)
 
195
    {
 
196
        $this->simplify();
 
197
        $periods = array();
 
198
 
 
199
        // Check that we have data for some part of this period.
 
200
        if ($this->getEnd() < $startStamp || $this->getStart() > $endStamp) {
 
201
            return $periods;
 
202
        }
 
203
 
 
204
        // Locate the first time in the requested period we have data for.
 
205
        $nextstart = max($startStamp, $this->getStart());
 
206
 
 
207
        // Check each busy period and add free periods in between.
 
208
        foreach ($this->_busyPeriods as $start => $end) {
 
209
            if ($start <= $endStamp && $end >= $nextstart) {
 
210
                if ($nextstart <= $start) {
 
211
                    $periods[$nextstart] = min($start, $endStamp);
 
212
                }
 
213
                $nextstart = min($end, $endStamp);
 
214
            }
 
215
        }
 
216
 
 
217
        // If we didn't read the end of the requested period but still have
 
218
        // data then mark as free to the end of the period or available data.
 
219
        if ($nextstart < $endStamp && $nextstart < $this->getEnd()) {
 
220
            $periods[$nextstart] = min($this->getEnd(), $endStamp);
 
221
        }
 
222
 
 
223
        return $periods;
 
224
    }
 
225
 
 
226
    /**
 
227
     * Adds a busy period to the info.
 
228
     *
 
229
     * This function may throw away data in case you add a period with a start
 
230
     * date that already exists. The longer of the two periods will be chosen
 
231
     * (and all information associated with the shorter one will be removed).
 
232
     *
 
233
     * @param string $type       The type of the period. Either 'FREE' or
 
234
     *                           'BUSY'; only 'BUSY' supported at the moment.
 
235
     * @param integer $start     The start timestamp of the period.
 
236
     * @param integer $end       The end timestamp of the period.
 
237
     * @param integer $duration  The duration of the period. If specified, the
 
238
     *                           $end parameter will be ignored.
 
239
     * @param array   $extra     Additional parameters for this busy period.
 
240
     */
 
241
    public function addBusyPeriod($type, $start, $end = null, $duration = null,
 
242
                                  $extra = array())
 
243
    {
 
244
        if ($type == 'FREE') {
 
245
            // Make sure this period is not marked as busy.
 
246
            return false;
 
247
        }
 
248
 
 
249
        // Calculate the end time if duration was specified.
 
250
        $tempEnd = is_null($duration) ? $end : $start + $duration;
 
251
 
 
252
        // Make sure the period length is always positive.
 
253
        $end = max($start, $tempEnd);
 
254
        $start = min($start, $tempEnd);
 
255
 
 
256
        if (isset($this->_busyPeriods[$start])) {
 
257
            // Already a period starting at this time. Change the current
 
258
            // period only if the new one is longer. This might be a problem
 
259
            // if the callee assumes that there is no simplification going
 
260
            // on. But since the periods are stored using the start time of
 
261
            // the busy periods we have to throw away data here.
 
262
            if ($end > $this->_busyPeriods[$start]) {
 
263
                $this->_busyPeriods[$start] = $end;
 
264
                $this->_extraParams[$start] = $extra;
 
265
            }
 
266
        } else {
 
267
            // Add a new busy period.
 
268
            $this->_busyPeriods[$start] = $end;
 
269
            $this->_extraParams[$start] = $extra;
 
270
        }
 
271
 
 
272
        return true;
 
273
    }
 
274
 
 
275
    /**
 
276
     * Returns the timestamp of the start of the time period this free busy
 
277
     * information covers.
 
278
     *
 
279
     * @return integer  A timestamp.
 
280
     */
 
281
    public function getStart()
 
282
    {
 
283
        try {
 
284
            return $this->getAttribute('DTSTART');
 
285
        } catch (Horde_Icalendar_Exception $e) {
 
286
            return count($this->_busyPeriods)
 
287
                ? min(array_keys($this->_busyPeriods))
 
288
                : false;
 
289
        }
 
290
    }
 
291
 
 
292
    /**
 
293
     * Returns the timestamp of the end of the time period this free busy
 
294
     * information covers.
 
295
     *
 
296
     * @return integer  A timestamp.
 
297
     */
 
298
    public function getEnd()
 
299
    {
 
300
        try {
 
301
            return $this->getAttribute('DTEND');
 
302
        } catch (Horde_Icalendar_Exception $e) {
 
303
            return count($this->_busyPeriods)
 
304
                ? max(array_values($this->_busyPeriods))
 
305
                : false;
 
306
        }
 
307
    }
 
308
 
 
309
    /**
 
310
     * Merges the busy periods of another Horde_Icalendar_Vfreebusy object
 
311
     * into this one.
 
312
     *
 
313
     * This might lead to simplification no matter what you specify for the
 
314
     * "simplify" flag since periods with the same start date will lead to the
 
315
     * shorter period being removed (see addBusyPeriod).
 
316
     *
 
317
     * @param Horde_Icalendar_Vfreebusy $freebusy  A freebusy object.
 
318
     * @param boolean $simplify                    If true, simplify() will
 
319
     *                                             called after the merge.
 
320
     */
 
321
    public function merge(Horde_Icalendar_Vfreebusy $freebusy,
 
322
                          $simplify = true)
 
323
    {
 
324
        $extra = $freebusy->getExtraParams();
 
325
        foreach ($freebusy->getBusyPeriods() as $start => $end) {
 
326
            // This might simplify the busy periods without taking the
 
327
            // "simplify" flag into account.
 
328
            $this->addBusyPeriod('BUSY', $start, $end, null,
 
329
                                 isset($extra[$start])
 
330
                                 ? $extra[$start] : array());
 
331
        }
 
332
 
 
333
        foreach (array('DTSTART', 'DTEND') as $val) {
 
334
            try {
 
335
                $thisattr = $this->getAttribute($val);
 
336
            } catch (Horde_Icalendar_Exception $e) {
 
337
                $thisattr = null;
 
338
            }
 
339
 
 
340
            try {
 
341
                $thatattr = $freebusy->getAttribute($val);
 
342
            } catch (Horde_Icalendar_Exception $e) {
 
343
                $thatattr = null;
 
344
            }
 
345
 
 
346
            if (is_null($thisattr) && !is_null($thatattr)) {
 
347
                $this->setAttribute($val, $thatattr, array(), false);
 
348
            } elseif (!is_null($thatattr)) {
 
349
                switch ($val) {
 
350
                case 'DTSTART':
 
351
                    $set = ($thatattr < $thisattr);
 
352
                    break;
 
353
 
 
354
                case 'DTEND':
 
355
                    $set = ($thatattr > $thisattr);
 
356
                    break;
 
357
                }
 
358
 
 
359
                if ($set) {
 
360
                    $this->setAttribute($val, $thatattr, array(), false);
 
361
                }
 
362
            }
 
363
        }
 
364
 
 
365
        if ($simplify) {
 
366
            $this->simplify();
 
367
        }
 
368
 
 
369
        return true;
 
370
    }
 
371
 
 
372
    /**
 
373
     * Removes all overlaps and simplifies the busy periods array as much as
 
374
     * possible.
 
375
     */
 
376
    public function simplify()
 
377
    {
 
378
        $clean = false;
 
379
        $busy  = array($this->_busyPeriods, $this->_extraParams);
 
380
        while (!$clean) {
 
381
            $result = $this->_simplify($busy[0], $busy[1]);
 
382
            $clean = $result === $busy;
 
383
            $busy = $result;
 
384
        }
 
385
 
 
386
        ksort($result[1], SORT_NUMERIC);
 
387
        $this->_extraParams = $result[1];
 
388
 
 
389
        ksort($result[0], SORT_NUMERIC);
 
390
        $this->_busyPeriods = $result[0];
 
391
    }
 
392
 
 
393
    /**
 
394
     * TODO
 
395
     *
 
396
     * @param $busyPeriods TODO
 
397
     * @param array $extraParams TODO
 
398
     *
 
399
     * @return array TODO
 
400
     */
 
401
    protected function _simplify($busyPeriods, $extraParams = array())
 
402
    {
 
403
        $checked = $checkedExtra = array();
 
404
        $checkedEmpty = true;
 
405
 
 
406
        foreach ($busyPeriods as $start => $end) {
 
407
            if ($checkedEmpty) {
 
408
                $checked[$start] = $end;
 
409
                $checkedExtra[$start] = isset($extraParams[$start])
 
410
                    ? $extraParams[$start]
 
411
                    : array();
 
412
                $checkedEmpty = false;
 
413
            } else {
 
414
                $added = false;
 
415
                foreach ($checked as $testStart => $testEnd) {
 
416
                    // Replace old period if the new period lies around the
 
417
                    // old period.
 
418
                    if ($start <= $testStart && $end >= $testEnd) {
 
419
                        // Remove old period entry.
 
420
                        unset($checked[$testStart]);
 
421
                        unset($checkedExtra[$testStart]);
 
422
                        // Add replacing entry.
 
423
                        $checked[$start] = $end;
 
424
                        $checkedExtra[$start] = isset($extraParams[$start])
 
425
                            ? $extraParams[$start]
 
426
                            : array();
 
427
                        $added = true;
 
428
                    } elseif ($start >= $testStart && $end <= $testEnd) {
 
429
                        // The new period lies fully within the old
 
430
                        // period. Just forget about it.
 
431
                        $added = true;
 
432
                    } elseif (($end <= $testEnd && $end >= $testStart) ||
 
433
                              ($start >= $testStart && $start <= $testEnd)) {
 
434
                        // Now we are in trouble: Overlapping time periods. If
 
435
                        // we allow for additional parameters we cannot simply
 
436
                        // choose one of the two parameter sets. It's better
 
437
                        // to leave two separated time periods.
 
438
                        $extra = isset($extraParams[$start])
 
439
                            ? $extraParams[$start]
 
440
                            : array();
 
441
                        $testExtra = isset($checkedExtra[$testStart])
 
442
                            ? $checkedExtra[$testStart]
 
443
                            : array();
 
444
                        // Remove old period entry.
 
445
                        unset($checked[$testStart]);
 
446
                        unset($checkedExtra[$testStart]);
 
447
                        // We have two periods overlapping. Are their
 
448
                        // additional parameters the same or different?
 
449
                        $newStart = min($start, $testStart);
 
450
                        $newEnd = max($end, $testEnd);
 
451
                        if ($extra === $testExtra) {
 
452
                            // Both periods have the same information. So we
 
453
                            // can just merge.
 
454
                            $checked[$newStart] = $newEnd;
 
455
                            $checkedExtra[$newStart] = $extra;
 
456
                        } else {
 
457
                            // Extra parameters are different. Create one
 
458
                            // period at the beginning with the params of the
 
459
                            // first period and create a trailing period with
 
460
                            // the params of the second period. The break
 
461
                            // point will be the end of the first period.
 
462
                            $break = min($end, $testEnd);
 
463
                            $checked[$newStart] = $break;
 
464
                            $checkedExtra[$newStart] =
 
465
                                isset($extraParams[$newStart])
 
466
                                ? $extraParams[$newStart]
 
467
                                : array();
 
468
                            $checked[$break] = $newEnd;
 
469
                            $highStart = max($start, $testStart);
 
470
                            $checkedExtra[$break] =
 
471
                                isset($extraParams[$highStart])
 
472
                                ? $extraParams[$highStart]
 
473
                                : array();
 
474
 
 
475
                            // Ensure we also have the extra data in the
 
476
                            // extraParams.
 
477
                            $extraParams[$break] =
 
478
                                isset($extraParams[$highStart])
 
479
                                ? $extraParams[$highStart]
 
480
                                : array();
 
481
                        }
 
482
                        $added = true;
 
483
                    }
 
484
 
 
485
                    if ($added) {
 
486
                        break;
 
487
                    }
 
488
                }
 
489
 
 
490
                if (!$added) {
 
491
                    $checked[$start] = $end;
 
492
                    $checkedExtra[$start] = isset($extraParams[$start])
 
493
                        ? $extraParams[$start]
 
494
                        : array();
 
495
                }
 
496
            }
 
497
        }
 
498
 
 
499
        return array($checked, $checkedExtra);
 
500
    }
 
501
 
 
502
}