Completed
Push — develop ( 3d0dce...676918 )
by Seth
02:56
created

Event::delete()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 41
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 41
rs 8.5806
cc 4
eloc 21
nc 5
nop 0
1
<?php
2
3
namespace smtech\CanvasICSSync\SyncIntoCanvas;
4
5
use DateTime;
6
use Exception;
7
use vevent;
8
use iCalUtilityFunctions;
9
use Michelf\Markdown;
10
use Pest_Unauthorized;
11
12
class Event extends Syncable
13
{
14
    /*
15
     * TODO:0 Can we detect the timezone for the Canvas instance and use it? issue:18
16
     */
17
    const LOCAL_TIMEZONE = 'US/Eastern';
18
    const CANVAS_TIMESTAMP_FORMAT = 'Y-m-d\TH:iP';
19
    const FIELD_MAP = [
20
        'calendar_event[title]' => 'SUMMARY',
21
        'calendar_event[description]' => 'DESCRIPTION',
22
        'calendar_event[start_at]' => [
23
            0 => 'X-CURRENT-DTSTART',
24
            1 => 'DTSTART'
25
        ],
26
        'calendar_event[end_at]' => [
27
            0 => 'X-CURRENT-DTEND',
28
            1 => 'DTEND'
29
        ],
30
        'calendar_event[location_name]' => 'LOCATION'
31
    ];
32
33
    /**
34
     * VEVENT
35
     * @var vevent
36
     */
37
    protected $vevent;
38
39
    /**
40
     * Calendar
41
     * @var Calendar
42
     */
43
    protected $calendar;
44
45
    /**
46
     * Unique hash identifying this version of this event
47
     * @var string
48
     */
49
    protected $hash;
50
51
    protected $canvasId;
52
53
    public function __construct($veventOrHash, Calendar $calendar)
54
    {
55
        $fail = false;
56
        if (empty($veventOrHash)) {
57
            $fail = true;
58
        } elseif (is_string($veventOrHash)) {
59
            $this->hash = $veventOrHash;
60
        } elseif (is_a($veventOrHash, vevent::class)) {
61
            $this->vevent = $veventOrHash;
62
        } else {
63
            $fail = true;
64
        }
65
        if ($fail) {
66
            throw new Exception(
67
                'Valid VEVENT or hash required'
68
            );
69
        }
70
71
        if (empty($calendar)) {
72
            throw new Exception(
73
                'Valid Calendar required'
74
            );
75
        }
76
        $this->calendar = $calendar;
77
78
        $this->getHash();
79
    }
80
81
    public function getProperty($p1 = false, $p2 = false, $p3 = false, $p4 = false)
82
    {
83
        return (empty($this->vevent) ? false : $this->vevent->getProperty($p1, $p2, $p3, $p4));
0 ignored issues
show
Documentation introduced by
$p1 is of type boolean, but the function expects a false|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$p2 is of type boolean, but the function expects a false|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
84
    }
85
86
    public function getCalendar()
87
    {
88
        return $this->calendar;
89
    }
90
91
    /**
92
     * Generate a hash of this version of an event to cache in the database
93
     **/
94
    public function getHash($algorithm = 'md5')
95
    {
96
        if (empty($this->hash)) {
97
            if (empty($this->vevent)) {
98
                throw new Exception(
99
                    'Neither hash or VEVENT to compute hash available'
100
                );
101
            }
102
            $blob = '';
103
            foreach (static::$FIELD_MAP as $field) {
104
                if (is_array($field)) {
105
                    foreach ($field as $option) {
106
                        if (!empty($property = $this->getProperty($option))) {
107
                            $blob .= serialize($property);
108
                            break;
109
                        }
110
                    }
111
                } else {
112
                    if (!empty($property = $this->getProperty($field))) {
113
                        $blob .= serialize($property);
114
                    }
115
                }
116
            }
117
            $this->hash = hash($algorithm, $blob);
118
        }
119
        return $this->hash;
120
    }
121
122
    public function save()
123
    {
124
        $db = static::getDatabase();
125
        $api = static::getApi();
126
127
        $select = $db->prepare(
128
            "SELECT *
129
                FROM `events`
130
                WHERE
131
                    `event_hash` = :event_hash AND
132
                    `calendar` = :calendar"
133
        );
134
        $update = $db->prepare(
135
            "UPDATE `events`
136
                SET
137
                    `synced` = :synced
138
                WHERE
139
                    `event_hash` = :event_hash AND
140
                    `calendar` = :calendar"
141
        );
142
        $insert = $db->prepare(
143
            "INSERT INTO `events`
144
                (
145
                    `calendar`,
146
                    `calendar_event[id]`,
147
                    `event_hash`,
148
                    `synced`
149
                ) VALUES (
150
                    :calendar,
151
                    :calendar_event_id,
152
                    :event_hash,
153
                    :synced
154
                )"
155
        );
156
157
        $params = [
158
            'calendar' => $this->getCalendar()->getId(),
159
            'event_hash' => $this->getHash(),
160
            'synced' => Syncable::getTimestamp()
161
        ];
162
163
        $select->execute($params);
164
        if ($select->fetch() !== false) {
165
            $update->execute($params);
166
        } else {
167
            /*
168
             * FIXME: how sure are we of this? issue:14
169
             */
170
            /*
171
             * multi-day event instance start times need to be changed to
172
             * _this_ date
173
             */
174
            $start = new DateTime(
175
                iCalUtilityFunctions::_date2strdate(
176
                    $this->getProperty('DTSTART')
0 ignored issues
show
Documentation introduced by
'DTSTART' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
177
                )
178
            );
179
            $end = new DateTime(
180
                iCalUtilityFunctions::_date2strdate(
181
                    $this->getProperty('DTEND')
0 ignored issues
show
Documentation introduced by
'DTEND' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
182
                )
183
            );
184
            if ($this->getProperty('X-RECURRENCE')) {
0 ignored issues
show
Documentation introduced by
'X-RECURRENCE' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
185
                $start = new DateTime($this->getProperty('X-CURRENT-DTSTART')[1]);
0 ignored issues
show
Documentation introduced by
'X-CURRENT-DTSTART' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
186
                $end = new DateTime($this->getProperty('X-CURRENT-DTEND')[1]);
0 ignored issues
show
Documentation introduced by
'X-CURRENT-DTEND' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
187
            }
188
            $start->setTimeZone(new DateTimeZone(self::LOCAL_TIMEZONE));
189
            $end->setTimeZone(new DateTimeZone(self::LOCAL_TIMEZONE));
190
191
            $calendarEvent = $api->post(
192
                "/calendar_events",
193
                [
0 ignored issues
show
Documentation introduced by
array('calendar_event' =...tProperty('LOCATION'))) is of type array<string,array<strin...ation_name\":\"*\"}>"}>, but the function expects a string|array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
194
                    'calendar_event' => [
195
                        'context_code' => $this->getCalendar()->getContextCode(),
196
                        /*
197
                         * TODO this should be configurable issue:5
198
                         */
199
                        /* removing trailing [TAGS] from event title */
200
                        'title' => preg_replace(
201
                            '%^([^\]]+)(\s*\[[^\]]+\]\s*)+$%',
202
                            '\\1',
203
                            strip_tags($this->getProperty('SUMMARY'))
0 ignored issues
show
Documentation introduced by
'SUMMARY' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
204
                        ),
205
                        'description' => Markdown::defaultTransform(
206
                            str_replace(
207
                                '\n',
208
                                "\n\n",
209
                                $this->getProperty('DESCRIPTION', 1)
0 ignored issues
show
Documentation introduced by
'DESCRIPTION' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
1 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
210
                            )
211
                        ),
212
                        'start_at' => $start->format(self::CANVAS_TIMESTAMP_FORMAT),
213
                        'end_at' => $end->format(self::CANVAS_TIMESTAMP_FORMAT),
214
                        'location_name' => $this->getProperty('LOCATION')
0 ignored issues
show
Documentation introduced by
'LOCATION' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
215
                    ]
216
                ]
217
            );
218
            $params['calendar_event_id'] = $calendarEvent['id'];
219
            $insert->execute($params);
220
        }
221
    }
222
223
    public static function purgeUnmatched($timestamp, $calendar)
224
    {
225
        $findDeletedEvents = static::getDatabase()->prepare(
226
            "SELECT *
227
                FROM `events`
228
                WHERE
229
                    `calendar` = :calendar AND
230
                    `synced` != :synced"
231
        );
232
233
        $findDeletedEvents->execute([
234
            'calendar' => $calendar->getId(),
235
            'synced' => $timestamp
236
        ]);
237
        if (($deletedEvents = $findDeletedEvents->fetchAll()) !== false) {
238
            foreach ($deletedEvents as $_event) {
239
                if (($event = Event::load($_event['id'], $this->getCalendar())) !== null) {
0 ignored issues
show
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
240
                    $event->delete();
241
                }
242
            }
243
        }
244
    }
245
246
    public function delete()
247
    {
248
        $db = static::getDatabase();
249
        $api = static::getApi();
250
251
        $select = $db->prepare(
252
            "SELECT * FROM `events`
253
                WHERE
254
                    `event_hash` = :event_hash AND
255
                    `calendar` = :calendar"
256
        );
257
        $delete = $db->prepare(
258
            "DELETE FROM `events`
259
                WHERE
260
                    `id` = :id"
261
        );
262
        $select->execute([
263
            'event_hash' => $this->getHash(),
264
            'calendar' => $this->getCalendar()->getId()
265
        ]);
266
        if (($event = $select->fetch()) !== false) {
267
            $params = [];
268
            $params['cancel_reason'] = $timestamp;
0 ignored issues
show
Bug introduced by
The variable $timestamp does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
269
            if ($calendar->getContext()->getContext() == 'user') {
270
                $params['as_user_id'] = $calendar->getContext()->getId();
0 ignored issues
show
Bug introduced by
The variable $calendar does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
271
            }
272
            try {
273
                $api->delete(
274
                    "/calendar_events/{$event['calendar_event[id]']}",
275
                    $params
276
                );
277
            } catch (Pest_Unauthorized $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class Pest_Unauthorized does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
278
                /*
279
                 * Do nothing: an event was cached that had been deleted
280
                 * from Canvas -- deleting the cached event is
281
                 * inconsequential
282
                 */
283
            }
284
            $delete->execute($event['id']);
285
        }
286
    }
287
288
    public static function load($id, $calendar)
289
    {
290
        $select = static::getDatabase()->prepare(
291
            "SELECT * FROM `events` WHERE `id` = :id AND `calendar` = :calendar"
292
        );
293
        $select->execute([
294
            'id' => $id,
295
            'calendar' => $calendar->getId()
296
        ]);
297
        if (($event = $select->fetch()) !== false) {
298
            return new Event($event['event_hash'], $calendar);
299
        }
300
        return null;
301
    }
302
}
303