Completed
Push — develop ( 0fe9d6...4e1ddc )
by Seth
03:00
created

Calendar::setFeedUrl()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
3
namespace smtech\CanvasICSSync\SyncIntoCanvas;
4
5
use DateTime;
6
use vcalendar;
7
use Battis\DataUtilities;
8
9
class Calendar
10
{
11
    /**
12
     * Canvas calendar context
13
     * @var CalendarContext
14
     */
15
    protected $context;
16
17
    /**
18
     * ICS or webcal feed URL
19
     * @var string
20
     */
21
    protected $feedUrl;
22
23
    /**
24
     * Name of this calendar (extracted from feed)
25
     * @var string
26
     */
27
    protected $name;
28
29
    /**
30
     * Filter for events in this calendar
31
     * @var Filter
32
     */
33
    protected $filter;
34
35
    /**
36
     * Construct a Calendar object
37
     *
38
     * @param string $canvasUrl URL of a Canvas calendar context
39
     * @param string $feedUrl URL of a webcal or ICS calendar feed
40
     * @param boolean $enableFilter (Optional, default `false`)
41
     * @param string $include (Optional) Regular expression to select events
42
     *     for inclusion in the calendar sync
43
     * @param string $exclude (Optional) Regular expression to select events
44
     *     for exclusion from the calendar sync
45
     */
46
    public function __construct($canvasUrl, $feedUrl, $enableFilter = false, $include = null, $exclude = null)
47
    {
48
        $this->setContext(new CalendarContext($canvasUrl));
49
        $this->setFeed($feedUrl);
0 ignored issues
show
Bug introduced by
The method setFeed() does not exist on smtech\CanvasICSSync\SyncIntoCanvas\Calendar. Did you maybe mean setFeedUrl()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
50
        $this->setFilter(new Filter(
51
            $enableFilter,
52
            $include,
53
            $exclude
54
        ));
55
    }
56
57
    /**
58
     * Set the Canvas calendar context
59
     *
60
     * @param CalendarContext $context
61
     * @throws Exception If `$context` is null
62
     */
63
    public function setContext(CalendarContext $context)
64
    {
65
        if (!empty($context)) {
66
            $this->context = $context;
67
        } else {
68
            throw new Exception(
69
                'Context cannot be null'
70
            );
71
        }
72
    }
73
74
    /**
75
     * Get the Canvas calendar context
76
     *
77
     * @return CalendarContext
78
     */
79
    public function getContext()
80
    {
81
        return $this->context;
82
    }
83
84
    /**
85
     * Set the webcal or ICS feed URl for this calendar
86
     *
87
     * @param string $feedUrl
88
     * @throws Exception If `$feedUrl` is not a valid URL
89
     */
90
    public function setFeedUrl($feedUrl)
91
    {
92
        if (!empty($feedUrl)) {
93
            /* crude test to see if the feed is a valid URL */
94
            $handle = fopen($feedUrl, 'r');
95
            if ($handle !== false) {
96
                $this->feedUrl = $feedUrl;
97
            }
98
        }
99
        throw new Exception(
100
            'Feed must be a valid URL'
101
        );
102
    }
103
104
    /**
105
     * Get the feed URL for this calendar
106
     *
107
     * @return string
108
     */
109
    public function getFeedUrl()
110
    {
111
        return $this->feedUrl;
112
    }
113
114
    /**
115
     * Set the name of the calendar
116
     *
117
     * @param string $name
118
     */
119
    public function setName($name)
120
    {
121
        $this->name = (string) $name;
122
    }
123
124
    /**
125
     * Get the name of the calendar
126
     *
127
     * @return string
128
     */
129
    public function getName()
130
    {
131
        return $this->name;
132
    }
133
134
    /**
135
     * Set the regular expression filter for this calendar
136
     *
137
     * @param Filter $filter
138
     */
139
    public function setFilter(Filter $filter)
140
    {
141
        $this->filter = $filter;
142
    }
143
144
    /**
145
     * Get the regular expression filter for this calendar
146
     *
147
     * @return Filter
148
     */
149
    public function getFilter()
150
    {
151
        return $this->filter;
152
    }
153
154
    /**
155
     * Generate a unique ID to identify this particular pairing of ICS feed and
156
     * Canvas calendar
157
     **/
158
    protected function getId($algorithm = 'md5')
159
    {
160
        return hash($algorithm, $this->getContext()->getCanonicalUrl() . $this->getFeedUrl());
161
    }
162
163
    public function getContextCode()
164
    {
165
        return $this->getContext()->getContext() . '_' . $this->getContext()->getId();
166
    }
167
168
    public function save()
169
    {
170
        $db = static::getDatabase();
0 ignored issues
show
Bug introduced by
The method getDatabase() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
171
        $find = $db->prepare(
172
            "SELECT * FROM `calendars` WHERE `id` = :id"
173
        );
174
        $update = $db->prepare(
175
            "UPDATE `calendars`
176
                SET
177
                    `name` = :name,
178
                    `canvas_url` = :canvas_url,
179
                    `ics_url` = :ics_url,
180
                    `synced` = :synced,
181
                    `enable_regex_filter` = :enable_regex_filter,
182
                    `include_regexp` = :include_regexp,
183
                    `exclude_regexp` = :exclude_regexp
184
                WHERE
185
                `id` = :id"
186
        );
187
        $insert = $db->prepare(
188
            "INSERT INTO `calendars`
189
                (
190
                    `id`,
191
                    `name`,
192
                    `canvas_url`,
193
                    `ics_url`,
194
                    `synced`,
195
                    `enable_regex_filter`,
196
                    `include_regexp`,
197
                    `exclude_regexp`
198
                ) VALUES (
199
                    :id,
200
                    :name,
201
                    :canvas_url,
202
                    :ics_url,
203
                    :synced
204
                    :enable_regex_filter,
205
                    :include_regexp,
206
                    :exclude_regexp
207
                )"
208
        );
209
        $params = [
210
            'id' => $this->getPairingHash(),
0 ignored issues
show
Bug introduced by
The method getPairingHash() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
211
            'name' => $this->getName(),
212
            'canvas_url' => $this->getContext()->getCanonicalUrl(),
213
            'ics_url' => $this->getFeedUrl(),
214
            'synced' => static::getSyncTimestamp(),
0 ignored issues
show
Bug introduced by
The method getSyncTimestamp() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
215
            'enable_regex_filter' => $this->getFilter()->isEnabled(),
216
            'include_regexp' => $this->getFilter()->getIncludeExpression(),
217
            'exclude_regexp' => $this->getFilter()->getExcludeExpression()
218
        ];
219
220
        $find->execute($params);
221
        if ($find->fetch() !== false) {
222
            $update->execute($params);
223
        } else {
224
            $insert->execute($params);
225
        }
226
    }
227
228
    /**
229
     * Load a Calendar from the database
230
     *
231
     * @param int $id
232
     * @return Calendar
233
     */
234
    public static function load($id)
235
    {
236
        $find = static::getDatabase()->prepare(
0 ignored issues
show
Bug introduced by
The method getDatabase() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
237
            "SELECT * FROM `calendars` WHERE `id` = :id"
238
        );
239
        $find->execute($id);
240
        if (($calendar = $find->fetch()) !== false) {
241
            return new Calendar(
242
                $calendar['canvas_url'],
243
                $calendar['ics_url'],
244
                $calendar['enable_regex_filter'],
245
                $calendar['include_regexp'],
246
                $calendar['exclude_regexp']
247
            );
248
        }
249
        return null;
250
    }
251
252
    public function sync(Log $log)
253
    {
254
        try {
255
            $api->get($this->getContext()->getVerificationUrl());
0 ignored issues
show
Bug introduced by
The variable $api 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...
256
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class smtech\CanvasICSSync\SyncIntoCanvas\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
257
            $this->logThrow(new Exception("Cannot sync calendars without a valid Canvas context"), $log);
258
        }
259
260
        if (!DataUtilities::URLexists($this>getFeedUrl())) {
0 ignored issues
show
Documentation introduced by
$this > getFeedUrl() is of type boolean, but the function expects a 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...
261
            $this->logThrow(new Exception("Cannot sync calendars with a valid calendar feed"), $log);
262
        }
263
264
        $this->log(static::getTimestamp() . ' sync started', $log);
0 ignored issues
show
Bug introduced by
The method getTimestamp() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
265
266
        $this->save();
267
268
        $ics = new vcalendar([
269
            'unique_id' => __FILE__,
270
            'url' => $this->getFeedUrl()
271
        ]);
272
        $ics->parse();
273
274
        /*
275
         * TODO: would it be worth the performance improvement to just process
276
         *     things from today's date forward? (i.e. ignore old items, even
277
         *     if they've changed...)
278
         */
279
        /*
280
         * TODO:0 the best window for syncing would be the term of the course
281
         *     in question, right? issue:12
282
         */
283
        /*
284
         * TODO:0 Arbitrarily selecting events in for a year on either side of
285
         *     today's date, probably a better system? issue:12
286
         */
287
        foreach ($ics->selectComponents(
0 ignored issues
show
Bug introduced by
The expression $ics->selectComponents(d...nt', false, true, true) of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
288
            date('Y')-1, // startYear
289
            date('m'), // startMonth
290
            date('d'), // startDay
291
            date('Y')+1, // endYEar
292
            date('m'), // endMonth
293
            date('d'), // endDay
294
            'vevent', // cType
295
            false, // flat
296
            true, // any
297
            true // split
298
        ) as $year) {
299
            foreach ($year as $month => $days) {
300
                foreach ($days as $day => $events) {
301
                    foreach ($events as $i => $_event) {
302
                        try {
303
                            $event = new Event($_event, $this);
304
                            if ($this->getFilter()->filter($event)) {
305
                                $event->save();
306
                            } else {
307
                                $event->delete();
308
                            }
309
                        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class smtech\CanvasICSSync\SyncIntoCanvas\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
310
                            $this->logThrow($e, $log);
311
                        }
312
                    }
313
                }
314
            }
315
        }
316
317
        try {
318
            Event::purgeUnmatched(static::getTimestamp(), $this);
0 ignored issues
show
Bug introduced by
The method getTimestamp() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
319
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class smtech\CanvasICSSync\SyncIntoCanvas\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
320
            $this->logThrow($e, $log);
321
        }
322
323
        $this->log(static::getTimestamp() . ' sync finished', $log);
0 ignored issues
show
Bug introduced by
The method getTimestamp() does not seem to exist on object<smtech\CanvasICSS...yncIntoCanvas\Calendar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
324
    }
325
326
    private function log($message, Log $log, $flag = PEAR_LOG_INFO)
327
    {
328
        if ($log) {
329
            $log->log($message, $flag);
330
        }
331
    }
332
333
    private function logThrow(
334
        Exception $exception,
0 ignored issues
show
Unused Code introduced by
The parameter $exception is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
335
        Log $log,
336
        $flag = PEAR_LOG_ERR
337
    ) {
338
        if ($log) {
339
            $log->log($e->getMessage() . ': ' . $e->getTraceAsString(), $flag);
0 ignored issues
show
Bug introduced by
The variable $e 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...
340
        } else {
341
            throw $e;
342
        }
343
    }
344
}
345