1<?php
2
3namespace Sabre\VObject\Component;
4
5use DateTimeImmutable;
6use DateTimeInterface;
7use Sabre\VObject;
8use Sabre\VObject\InvalidDataException;
9
10/**
11 * VAlarm component.
12 *
13 * This component contains some additional functionality specific for VALARMs.
14 *
15 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
16 * @author Evert Pot (http://evertpot.com/)
17 * @license http://sabre.io/license/ Modified BSD License
18 */
19class VAlarm extends VObject\Component {
20
21    /**
22     * Returns a DateTime object when this alarm is going to trigger.
23     *
24     * This ignores repeated alarm, only the first trigger is returned.
25     *
26     * @return DateTimeImmutable
27     */
28    function getEffectiveTriggerTime() {
29
30        $trigger = $this->TRIGGER;
31        if (!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
32            $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER);
33            $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
34
35            $parentComponent = $this->parent;
36            if ($related === 'START') {
37
38                if ($parentComponent->name === 'VTODO') {
39                    $propName = 'DUE';
40                } else {
41                    $propName = 'DTSTART';
42                }
43
44                $effectiveTrigger = $parentComponent->$propName->getDateTime();
45                $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
46            } else {
47                if ($parentComponent->name === 'VTODO') {
48                    $endProp = 'DUE';
49                } elseif ($parentComponent->name === 'VEVENT') {
50                    $endProp = 'DTEND';
51                } else {
52                    throw new InvalidDataException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
53                }
54
55                if (isset($parentComponent->$endProp)) {
56                    $effectiveTrigger = $parentComponent->$endProp->getDateTime();
57                    $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
58                } elseif (isset($parentComponent->DURATION)) {
59                    $effectiveTrigger = $parentComponent->DTSTART->getDateTime();
60                    $duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION);
61                    $effectiveTrigger = $effectiveTrigger->add($duration);
62                    $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
63                } else {
64                    $effectiveTrigger = $parentComponent->DTSTART->getDateTime();
65                    $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
66                }
67            }
68        } else {
69            $effectiveTrigger = $trigger->getDateTime();
70        }
71        return $effectiveTrigger;
72
73    }
74
75    /**
76     * Returns true or false depending on if the event falls in the specified
77     * time-range. This is used for filtering purposes.
78     *
79     * The rules used to determine if an event falls within the specified
80     * time-range is based on the CalDAV specification.
81     *
82     * @param DateTime $start
83     * @param DateTime $end
84     *
85     * @return bool
86     */
87    function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
88
89        $effectiveTrigger = $this->getEffectiveTriggerTime();
90
91        if (isset($this->DURATION)) {
92            $duration = VObject\DateTimeParser::parseDuration($this->DURATION);
93            $repeat = (string)$this->REPEAT;
94            if (!$repeat) {
95                $repeat = 1;
96            }
97
98            $period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat);
99
100            foreach ($period as $occurrence) {
101
102                if ($start <= $occurrence && $end > $occurrence) {
103                    return true;
104                }
105            }
106            return false;
107        } else {
108            return ($start <= $effectiveTrigger && $end > $effectiveTrigger);
109        }
110
111    }
112
113    /**
114     * A simple list of validation rules.
115     *
116     * This is simply a list of properties, and how many times they either
117     * must or must not appear.
118     *
119     * Possible values per property:
120     *   * 0 - Must not appear.
121     *   * 1 - Must appear exactly once.
122     *   * + - Must appear at least once.
123     *   * * - Can appear any number of times.
124     *   * ? - May appear, but not more than once.
125     *
126     * @var array
127     */
128    function getValidationRules() {
129
130        return [
131            'ACTION'  => 1,
132            'TRIGGER' => 1,
133
134            'DURATION' => '?',
135            'REPEAT'   => '?',
136
137            'ATTACH' => '?',
138        ];
139
140    }
141
142}
143