Test Failed
Push — main ( 29a1e7...86a642 )
by Rafael
61:24
created

ICal::parse()   F

Complexity

Conditions 32
Paths 261

Size

Total Lines 116
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 32
eloc 77
nc 261
nop 3
dl 0
loc 116
rs 2.5708
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2006      Roman Ozana          <[email protected]>
4
 * Copyright (C) 2011	   Juanjo Menent		<[email protected]>
5
 * Copyright (C) 2013-2014 Laurent Destailleur	<[email protected]>
6
 * Copyright (C) 2012	   Regis Houssin		<[email protected]>
7
 * Copyright (C) 2019-2024  Frédéric France     <[email protected]>
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 */
22
23
namespace DoliModules\Agenda\Model;
24
25
/**
26
 *       \file       htdocs/comm/action/class/ical.class.php
27
 *       \ingroup    agenda
28
 *       \brief      File of class to parse ical calendars
29
 */
30
31
require_once DOL_DOCUMENT_ROOT . '/core/lib/xcal.lib.php';
32
require_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php';
33
34
35
/**
36
 *  Class to read/parse ICal calendars
37
 */
38
class ICal
39
{
40
    /**
41
     * @var string  Name of remote HTTP file to read
42
     */
43
    public $file;
44
45
    /**
46
     * @var string  Text in file
47
     */
48
    public $file_text;
49
50
    /**
51
     * @var array Array to save iCalendar parse data
52
     */
53
    public $cal;
54
55
    /**
56
     * @var int Number of Events
57
     */
58
    public $event_count;
59
60
    /**
61
     * @var int Number of Todos
62
     */
63
    public $todo_count;
64
65
    /**
66
     * @var int Number of Freebusy
67
     */
68
    public $freebusy_count;
69
70
    /**
71
     * @var string Help variable save last key (multiline string)
72
     */
73
    public $last_key;
74
75
    /**
76
     * @var string error message
77
     */
78
    public $error;
79
80
81
    /**
82
     * Constructor
83
     */
84
    public function __construct()
85
    {
86
    }
87
88
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
89
    /**
90
     *  Read text file, icalender text file
91
     *
92
     *  @param  string          $file       File
93
     *  @return string|null                 Content of remote file read or null if error
94
     */
95
    public function read_file($file)
96
    {
97
		// phpcs:enable
98
        $this->file = $file;
99
        $file_text = '';
100
101
        $tmpresult = getURLContent($file, 'GET');
102
        if ($tmpresult['http_code'] != 200) {
103
            $file_text = null;
104
            $this->error = 'Error: ' . $tmpresult['http_code'] . ' ' . $tmpresult['content'];
105
        } else {
106
            $file_text = preg_replace("/[\r\n]{1,} /", "", $tmpresult['content']);
107
        }
108
        //var_dump($tmpresult);
109
110
        return $file_text; // return all text
111
    }
112
113
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
114
    /**
115
     * Returns the number of calendar events
116
     *
117
     * @return int
118
     */
119
    public function get_event_count()
120
    {
121
		// phpcs:enable
122
        return $this->event_count;
123
    }
124
125
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
126
    /**
127
     * Returns the number of to do
128
     *
129
     * @return int
130
     */
131
    public function get_todo_count()
132
    {
133
		// phpcs:enable
134
        return $this->todo_count;
135
    }
136
137
    /**
138
     * Translate Calendar
139
     *
140
     * @param   string      $uri            Url
141
     * @param   string      $usecachefile   Full path of a cache file to use a cache file
142
     * @param   int         $delaycache     Delay in seconds for cache (by default 3600 secondes)
143
     * @return  array|string
144
     */
145
    public function parse($uri, $usecachefile = '', $delaycache = 3600)
146
    {
147
        $this->cal = array(); // new empty array
148
149
        $this->event_count = -1;
150
        $this->file_text = '';
151
152
        // Save file into a cache
153
        if ($usecachefile) {
154
            include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
155
            $datefile = dol_filemtime($usecachefile);
156
            $now = dol_now('gmt');
157
            //print $datefile.' '.$now.' ...';
158
            if ($datefile && $datefile > ($now - $delaycache)) {
159
                // We reuse the cache file
160
                $this->file_text = file_get_contents($usecachefile);
161
            }
162
        }
163
164
        // read FILE text
165
        if (is_null($this->file_text)) {
166
            $this->file_text = $this->read_file($uri);
167
168
            if ($usecachefile && !is_null($this->file_text)) {
169
                // Save the file content into cache file
170
                file_put_contents($usecachefile, $this->file_text, LOCK_EX);
171
                dolChmod($usecachefile);
172
            }
173
        }
174
175
        $file_text_array = preg_split("[\n]", $this->file_text);
176
177
        // is this text vcalendar standard text ? on line 1 is BEGIN:VCALENDAR
178
        if (!stristr($file_text_array[0], 'BEGIN:VCALENDAR')) {
179
            return 'error not VCALENDAR';
180
        }
181
182
        $insidealarm = 0;
183
        $tmpkey = '';
184
        $tmpvalue = '';
185
        $type = '';
186
        foreach ($file_text_array as $text) {
187
            $text = trim($text); // trim one line
188
            if (!empty($text)) {
189
                // get Key and Value VCALENDAR:Begin -> Key = VCALENDAR, Value = begin
190
                [$key, $value] = $this->retun_key_value($text);
191
                //var_dump($text.' -> '.$key.' - '.$value);
192
193
                switch ($text) { // search special string
194
                    case "BEGIN:VTODO":
195
                        $this->todo_count = $this->todo_count + 1; // new to do begin
196
                        $type = "VTODO";
197
                        break;
198
199
                    case "BEGIN:VEVENT":
200
                        $this->event_count = $this->event_count + 1; // new event begin
201
                        $type = "VEVENT";
202
                        break;
203
204
                    case "BEGIN:VFREEBUSY":
205
                        $this->freebusy_count = $this->freebusy_count + 1; // new event begin
206
                        $type = "VFREEBUSY";
207
                        break;
208
209
                    case "BEGIN:VCALENDAR": // all other special string
210
                    case "BEGIN:DAYLIGHT":
211
                    case "BEGIN:VTIMEZONE":
212
                    case "BEGIN:STANDARD":
213
                        $type = $value; // save array under value key
214
                        break;
215
216
                    case "END:VTODO": // end special text - goto VCALENDAR key
217
                    case "END:VEVENT":
218
                    case "END:VFREEBUSY":
219
                    case "END:VCALENDAR":
220
                    case "END:DAYLIGHT":
221
                    case "END:VTIMEZONE":
222
                    case "END:STANDARD":
223
                        $type = "VCALENDAR";
224
                        break;
225
226
                    // Manage VALARM that are inside a VEVENT to avoid fields of VALARM to overwrites fields of VEVENT
227
                    case "BEGIN:VALARM":
228
                        $insidealarm = 1;
229
                        break;
230
                    case "END:VALARM":
231
                        $insidealarm = 0;
232
                        break;
233
234
                    default: // no special string (SUMMARY, DESCRIPTION, ...)
235
                        if ($tmpvalue) {
236
                            $tmpvalue .= $text;
237
                            if (!preg_match('/=$/', $text)) {   // No more lines
238
                                $key = $tmpkey;
239
                                $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue));
240
                                $tmpkey = '';
241
                                $tmpvalue = '';
242
                            }
243
                        } elseif (preg_match('/^ENCODING=QUOTED-PRINTABLE:/i', $value)) {
244
                            if (preg_match('/=$/', $value)) {
245
                                $tmpkey = $key;
246
                                $tmpvalue = $tmpvalue . preg_replace('/=$/', "", $value); // We must wait to have next line to have complete message
247
                            } else {
248
                                $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue . $value));
249
                            }
250
                        }                       //$value=quotedPrintDecode($tmpvalue.$value);
251
                        if (!$insidealarm && !$tmpkey) {
252
                            $this->add_to_array($type, $key, $value); // add to array
253
                        }
254
                        break;
255
                }
256
            }
257
        }
258
259
        //var_dump($this->cal);
260
        return $this->cal;
261
    }
262
263
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
264
    /**
265
     * Add to $this->ical array one value and key.
266
     *
267
     * @param   string  $type       Type ('VTODO', 'VEVENT', 'VFREEBUSY', 'VCALENDAR'...)
268
     * @param   string  $key        Key ('DTSTART', ...). Note: Field is never 'DTSTART;TZID=...' because ';...' was before removed and added as another property
269
     * @param   string  $value      Value
270
     * @return  void
271
     */
272
    public function add_to_array($type, $key, $value)
273
    {
274
		// phpcs:enable
275
276
        //print 'type='.$type.' key='.$key.' value='.$value.'<br>'."\n";
277
278
        if (empty($key)) {
279
            $key = $this->last_key;
280
            switch ($type) {
281
                case 'VEVENT':
282
                    $value = $this->cal[$type][$this->event_count][$key] . $value;
283
                    break;
284
                case 'VFREEBUSY':
285
                    $value = $this->cal[$type][$this->freebusy_count][$key] . $value;
286
                    break;
287
                case 'VTODO':
288
                    $value = $this->cal[$type][$this->todo_count][$key] . $value;
289
                    break;
290
            }
291
        }
292
293
        if (($key == "DTSTAMP") || ($key == "LAST-MODIFIED") || ($key == "CREATED")) {
294
            $value = $this->ical_date_to_unix($value);
295
        }
296
        //if ($key == "RRULE" ) $value = $this->ical_rrule($value);
297
298
        if (stristr($key, "DTSTART") || stristr($key, "DTEND") || stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
299
            if (stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
300
                [$key, $value] = array($key, $value);
301
            } else {
302
                [$key, $value] = $this->ical_dt_date($key, $value);
303
            }
304
        }
305
306
        switch ($type) {
307
            case "VTODO":
308
                $this->cal[$type][$this->todo_count][$key] = $value;
309
                break;
310
311
            case "VEVENT":
312
                $this->cal[$type][$this->event_count][$key] = $value;
313
                break;
314
315
            case "VFREEBUSY":
316
                $this->cal[$type][$this->freebusy_count][$key] = $value;
317
                break;
318
319
            default:
320
                $this->cal[$type][$key] = $value;
321
                break;
322
        }
323
        $this->last_key = $key;
324
    }
325
326
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
327
    /**
328
     * Parse text "XXXX:value text some with : " and return array($key = "XXXX", $value="value");
329
     *
330
     * @param   string  $text   Text
331
     * @return  array
332
     */
333
    public function retun_key_value($text)
334
    {
335
		// phpcs:enable
336
        /*
337
        preg_match("/([^:]+)[:]([\w\W]+)/", $text, $matches);
338
339
        if (empty($matches))
340
        {
341
            return array(false,$text);
342
        }
343
        else
344
        {
345
            $matches = array_splice($matches, 1, 2);
346
            return $matches;
347
        }*/
348
        return explode(':', $text, 2);
349
    }
350
351
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
352
    /**
353
     * Parse RRULE  return array
354
     *
355
     * @param   string  $value  string
356
     * @return  array
357
     */
358
    public function ical_rrule($value)
359
    {
360
		// phpcs:enable
361
        $result = array();
362
        $rrule = explode(';', $value);
363
        foreach ($rrule as $line) {
364
            $rcontent = explode('=', $line);
365
            $result[$rcontent[0]] = $rcontent[1];
366
        }
367
        return $result;
368
    }
369
370
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
371
    /**
372
     * Return Unix time from ical date time format (YYYYMMDD[T]HHMMSS[Z] or YYYYMMDD[T]HHMMSS)
373
     *
374
     * @param   string      $ical_date      String date
375
     * @return  int
376
     */
377
    public function ical_date_to_unix($ical_date)
378
    {
379
		// phpcs:enable
380
        $ical_date = str_replace('T', '', $ical_date);
381
        $ical_date = str_replace('Z', '', $ical_date);
382
383
        $ntime = 0;
384
        // TIME LIMITED EVENT
385
        if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})([0-9]{0,2})/', $ical_date, $date)) {
386
            $ntime = dol_mktime($date[4], $date[5], $date[6], $date[2], $date[3], $date[1], true);
387
        }
388
389
        //if (empty($date[4])) print 'Error bad date: '.$ical_date.' - date1='.$date[1];
390
        //print dol_print_date($ntime,'dayhour');exit;
391
        return $ntime; // ntime is a GTM time
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ntime also could return the type string which is incompatible with the documented return type integer.
Loading history...
392
    }
393
394
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
395
    /**
396
     * Return unix date from iCal date format
397
     *
398
     * @param   string      $key            Key
399
     * @param   string      $value          Value
400
     * @return  array
401
     */
402
    public function ical_dt_date($key, $value)
403
    {
404
		// phpcs:enable
405
        $return_value = array();
406
        $value = $this->ical_date_to_unix($value);
407
408
        // Analyse TZID
409
        $temp = explode(";", $key);
410
411
        if (empty($temp[1])) { // not TZID
412
            $value = str_replace('T', '', $value);
413
            return array($key, $value);
414
        }
415
416
        $key = $temp[0];
417
        $temp = explode("=", $temp[1]);
418
        $return_value[$temp[0]] = $temp[1];
419
        $return_value['unixtime'] = $value;
420
421
        return array($key, $return_value);
422
    }
423
424
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
425
    /**
426
     * Return sorted eventlist as array or false if calendar is empty
427
     *
428
     * @return array|false
429
     */
430
    public function get_sort_event_list()
431
    {
432
		// phpcs:enable
433
        $temp = $this->get_event_list();
434
        if (!empty($temp)) {
435
            usort($temp, array(&$this, "ical_dtstart_compare"));
436
            return $temp;
437
        } else {
438
            return false;
439
        }
440
    }
441
442
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
443
    /**
444
     * Compare two unix timestamp
445
     *
446
     * @param   array   $a      Operand a
447
     * @param   array   $b      Operand b
448
     * @return  integer
449
     */
450
    public function ical_dtstart_compare($a, $b)
451
    {
452
		// phpcs:enable
453
        return strnatcasecmp($a['DTSTART']['unixtime'], $b['DTSTART']['unixtime']);
454
    }
455
456
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
457
    /**
458
     * Return eventlist array (not sorted eventlist array)
459
     *
460
     * @return array
461
     */
462
    public function get_event_list()
463
    {
464
		// phpcs:enable
465
        return (empty($this->cal['VEVENT']) ? array() : $this->cal['VEVENT']);
466
    }
467
468
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
469
    /**
470
     * Return freebusy array (not sort eventlist array)
471
     *
472
     * @return array
473
     */
474
    public function get_freebusy_list()
475
    {
476
		// phpcs:enable
477
        return (empty($this->cal['VFREEBUSY']) ? array() : $this->cal['VFREEBUSY']);
478
    }
479
480
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
481
    /**
482
     * Return to do array (not sorted todo array)
483
     *
484
     * @return array
485
     */
486
    public function get_todo_list()
487
    {
488
		// phpcs:enable
489
        return $this->cal['VTODO'];
490
    }
491
492
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
493
    /**
494
     * Return base calendar data
495
     *
496
     * @return array
497
     */
498
    public function get_calender_data()
499
    {
500
		// phpcs:enable
501
        return $this->cal['VCALENDAR'];
502
    }
503
504
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
505
    /**
506
     * Return array with all data
507
     *
508
     * @return array
509
     */
510
    public function get_all_data()
511
    {
512
		// phpcs:enable
513
        return $this->cal;
514
    }
515
}
516