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    public function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end)
35    {
36        list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd();
37
38        return
39            (is_null($effectiveStart) || $start < $effectiveEnd) &&
40            (is_null($effectiveEnd) || $end > $effectiveStart)
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    public 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     * A simple list of validation rules.
75     *
76     * This is simply a list of properties, and how many times they either
77     * must or must not appear.
78     *
79     * Possible values per property:
80     *   * 0 - Must not appear.
81     *   * 1 - Must appear exactly once.
82     *   * + - Must appear at least once.
83     *   * * - Can appear any number of times.
84     *   * ? - May appear, but not more than once.
85     *
86     * @var array
87     */
88    public function getValidationRules()
89    {
90        return [
91            'UID' => 1,
92            'DTSTAMP' => 1,
93
94            'BUSYTYPE' => '?',
95            'CLASS' => '?',
96            'CREATED' => '?',
97            'DESCRIPTION' => '?',
98            'DTSTART' => '?',
99            'LAST-MODIFIED' => '?',
100            'ORGANIZER' => '?',
101            'PRIORITY' => '?',
102            'SEQUENCE' => '?',
103            'SUMMARY' => '?',
104            'URL' => '?',
105            'DTEND' => '?',
106            'DURATION' => '?',
107
108            'CATEGORIES' => '*',
109            'COMMENT' => '*',
110            'CONTACT' => '*',
111        ];
112    }
113
114    /**
115     * Validates the node for correctness.
116     *
117     * The following options are supported:
118     *   Node::REPAIR - May attempt to automatically repair the problem.
119     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
120     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
121     *
122     * This method returns an array with detected problems.
123     * Every element has the following properties:
124     *
125     *  * level - problem level.
126     *  * message - A human-readable string describing the issue.
127     *  * node - A reference to the problematic node.
128     *
129     * The level means:
130     *   1 - The issue was repaired (only happens if REPAIR was turned on).
131     *   2 - A warning.
132     *   3 - An error.
133     *
134     * @param int $options
135     *
136     * @return array
137     */
138    public function validate($options = 0)
139    {
140        $result = parent::validate($options);
141
142        if (isset($this->DTEND) && isset($this->DURATION)) {
143            $result[] = [
144                'level' => 3,
145                'message' => 'DTEND and DURATION cannot both be present',
146                'node' => $this,
147            ];
148        }
149
150        return $result;
151    }
152}
153