1<?php
2
3namespace Sabre\VObject\Component;
4
5use DateTimeInterface;
6use Sabre\VObject;
7
8/**
9 * The VAvailability component.
10 *
11 * This component adds functionality to a component, specific for VAVAILABILITY
12 * components.
13 *
14 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
15 * @author Ivan Enderlin
16 * @license http://sabre.io/license/ Modified BSD License
17 */
18class VAvailability extends VObject\Component {
19
20    /**
21     * Returns true or false depending on if the event falls in the specified
22     * time-range. This is used for filtering purposes.
23     *
24     * The rules used to determine if an event falls within the specified
25     * time-range is based on:
26     *
27     * https://tools.ietf.org/html/draft-daboo-calendar-availability-05#section-3.1
28     *
29     * @param DateTimeInterface $start
30     * @param DateTimeInterface $end
31     *
32     * @return bool
33     */
34    function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
35
36        list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd();
37        return (
38            (is_null($effectiveStart) || $start < $effectiveEnd) &&
39            (is_null($effectiveEnd) || $end > $effectiveStart)
40        );
41
42    }
43
44    /**
45     * Returns the 'effective start' and 'effective end' of this VAVAILABILITY
46     * component.
47     *
48     * We use the DTSTART and DTEND or DURATION to determine this.
49     *
50     * The returned value is an array containing DateTimeImmutable instances.
51     * If either the start or end is 'unbounded' its value will be null
52     * instead.
53     *
54     * @return array
55     */
56    function getEffectiveStartEnd() {
57
58        $effectiveStart = null;
59        $effectiveEnd = null;
60
61        if (isset($this->DTSTART)) {
62            $effectiveStart = $this->DTSTART->getDateTime();
63        }
64        if (isset($this->DTEND)) {
65            $effectiveEnd = $this->DTEND->getDateTime();
66        } elseif ($effectiveStart && isset($this->DURATION)) {
67            $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
68        }
69
70        return [$effectiveStart, $effectiveEnd];
71
72    }
73
74
75    /**
76     * A simple list of validation rules.
77     *
78     * This is simply a list of properties, and how many times they either
79     * must or must not appear.
80     *
81     * Possible values per property:
82     *   * 0 - Must not appear.
83     *   * 1 - Must appear exactly once.
84     *   * + - Must appear at least once.
85     *   * * - Can appear any number of times.
86     *   * ? - May appear, but not more than once.
87     *
88     * @var array
89     */
90    function getValidationRules() {
91
92        return [
93            'UID'     => 1,
94            'DTSTAMP' => 1,
95
96            'BUSYTYPE'      => '?',
97            'CLASS'         => '?',
98            'CREATED'       => '?',
99            'DESCRIPTION'   => '?',
100            'DTSTART'       => '?',
101            'LAST-MODIFIED' => '?',
102            'ORGANIZER'     => '?',
103            'PRIORITY'      => '?',
104            'SEQUENCE'      => '?',
105            'SUMMARY'       => '?',
106            'URL'           => '?',
107            'DTEND'         => '?',
108            'DURATION'      => '?',
109
110            'CATEGORIES' => '*',
111            'COMMENT'    => '*',
112            'CONTACT'    => '*',
113        ];
114
115    }
116
117    /**
118     * Validates the node for correctness.
119     *
120     * The following options are supported:
121     *   Node::REPAIR - May attempt to automatically repair the problem.
122     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
123     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
124     *
125     * This method returns an array with detected problems.
126     * Every element has the following properties:
127     *
128     *  * level - problem level.
129     *  * message - A human-readable string describing the issue.
130     *  * node - A reference to the problematic node.
131     *
132     * The level means:
133     *   1 - The issue was repaired (only happens if REPAIR was turned on).
134     *   2 - A warning.
135     *   3 - An error.
136     *
137     * @param int $options
138     *
139     * @return array
140     */
141    function validate($options = 0) {
142
143        $result = parent::validate($options);
144
145        if (isset($this->DTEND) && isset($this->DURATION)) {
146            $result[] = [
147                'level'   => 3,
148                'message' => 'DTEND and DURATION cannot both be present',
149                'node'    => $this
150            ];
151        }
152
153        return $result;
154
155    }
156}
157