dolibarr 21.0.0-alpha
ical.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2006 Roman Ozana <ozana@omdesign.cz>
3 * Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
4 * Copyright (C) 2013-2014 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
6 * Copyright (C) 2019-2024 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
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
28require_once DOL_DOCUMENT_ROOT.'/core/lib/xcal.lib.php';
29require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
30
31
35class ICal
36{
40 public $file;
41
45 public $file_text;
46
50 public $cal;
51
55 public $event_count;
56
60 public $todo_count;
61
65 public $freebusy_count;
66
70 public $last_key;
71
75 public $error;
76
77
81 public function __construct()
82 {
83 }
84
85 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
92 public function read_file($file)
93 {
94 // phpcs:enable
95 $this->file = $file;
96 $file_text = '';
97
98 //$tmpresult = getURLContent($file, 'GET', '', 1, [], ['http', 'https'], 2, 0); // To test with any URL
99 $tmpresult = getURLContent($file, 'GET');
100 if ($tmpresult['http_code'] != 200) {
101 $file_text = null;
102 $this->error = 'Error: '.$tmpresult['http_code'].' '.$tmpresult['content'];
103 } else {
104 $file_text = preg_replace("/[\r\n]{1,} /", "", $tmpresult['content']);
105 }
106 //var_dump($tmpresult);
107
108 return $file_text; // return all text
109 }
110
111 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
117 public function get_event_count()
118 {
119 // phpcs:enable
120 return $this->event_count;
121 }
122
123 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
129 public function get_todo_count()
130 {
131 // phpcs:enable
132 return $this->todo_count;
133 }
134
143 public function parse($uri, $usecachefile = '', $delaycache = 3600)
144 {
145 $this->cal = array(); // new empty array
146
147 $this->event_count = -1;
148 $this->file_text = '';
149
150 // Save file into a cache
151 if ($usecachefile) {
152 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
153 $datefile = dol_filemtime($usecachefile);
154 $now = dol_now('gmt');
155 //print $datefile.' '.$now.' ...';
156 if ($datefile && $datefile > ($now - $delaycache)) {
157 // We reuse the cache file
158 $this->file_text = file_get_contents($usecachefile);
159 }
160 }
161
162 // read FILE text
163 if (empty($this->file_text)) {
164 $this->file_text = $this->read_file($uri);
165
166 if ($usecachefile && !is_null($this->file_text)) {
167 // Save the file content into cache file
168 file_put_contents($usecachefile, $this->file_text, LOCK_EX);
169 dolChmod($usecachefile);
170 }
171 }
172
173 $file_text_array = preg_split("[\n]", $this->file_text);
174
175 // is this text vcalendar standard text ? on line 1 is BEGIN:VCALENDAR
176 if (!stristr($file_text_array[0], 'BEGIN:VCALENDAR')) {
177 return 'error not VCALENDAR';
178 }
179
180 $insidealarm = 0;
181 $tmpkey = '';
182 $tmpvalue = '';
183 $type = '';
184 foreach ($file_text_array as $text) {
185 $text = trim($text); // trim one line
186 if (!empty($text)) {
187 // get Key and Value VCALENDAR:Begin -> Key = VCALENDAR, Value = begin
188 list($key, $value) = $this->retun_key_value($text);
189 //var_dump($text.' -> '.$key.' - '.$value);
190
191 switch ($text) { // search special string
192 case "BEGIN:VTODO":
193 $this->todo_count += 1; // new to do begin
194 $type = "VTODO";
195 break;
196
197 case "BEGIN:VEVENT":
198 $this->event_count += 1; // new event begin
199 $type = "VEVENT";
200 break;
201
202 case "BEGIN:VFREEBUSY":
203 $this->freebusy_count += 1; // new event begin
204 $type = "VFREEBUSY";
205 break;
206
207 case "BEGIN:VCALENDAR": // all other special string
208 case "BEGIN:DAYLIGHT":
209 case "BEGIN:VTIMEZONE":
210 case "BEGIN:STANDARD":
211 $type = $value; // save array under value key
212 break;
213
214 case "END:VTODO": // end special text - goto VCALENDAR key
215 case "END:VEVENT":
216 case "END:VFREEBUSY":
217 case "END:VCALENDAR":
218 case "END:DAYLIGHT":
219 case "END:VTIMEZONE":
220 case "END:STANDARD":
221 $type = "VCALENDAR";
222 break;
223
224 // Manage VALARM that are inside a VEVENT to avoid fields of VALARM to overwrites fields of VEVENT
225 case "BEGIN:VALARM":
226 $insidealarm = 1;
227 break;
228 case "END:VALARM":
229 $insidealarm = 0;
230 break;
231
232 default: // no special string (SUMMARY, DESCRIPTION, ...)
233 if ($tmpvalue) {
234 $tmpvalue .= $text;
235 if (!preg_match('/=$/', $text)) { // No more lines
236 $key = $tmpkey;
237 $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue));
238 $tmpkey = '';
239 $tmpvalue = '';
240 }
241 } elseif (preg_match('/^ENCODING=QUOTED-PRINTABLE:/i', $value)) {
242 if (preg_match('/=$/', $value)) {
243 $tmpkey = $key;
244 $tmpvalue .= preg_replace('/=$/', "", $value); // We must wait to have next line to have complete message
245 } else {
246 $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue.$value));
247 }
248 } //$value=quotedPrintDecode($tmpvalue.$value);
249 if (!$insidealarm && !$tmpkey) {
250 $this->add_to_array($type, $key, $value); // add to array
251 }
252 break;
253 }
254 }
255 }
256
257 //var_dump($this->cal);
258 return $this->cal;
259 }
260
261 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
270 public function add_to_array($type, $key, $value)
271 {
272 // phpcs:enable
273
274 //print 'type='.$type.' key='.$key.' value='.$value.'<br>'."\n";
275
276 if (empty($key)) {
277 $key = $this->last_key;
278 switch ($type) {
279 case 'VEVENT':
280 // @phan-suppress-next-line PhanTypeMismatchDimFetch
281 $value = $this->cal[$type][$this->event_count][$key].$value;
282 break;
283 case 'VFREEBUSY':
284 // @phan-suppress-next-line PhanTypeMismatchDimFetch
285 $value = $this->cal[$type][$this->freebusy_count][$key].$value;
286 break;
287 case 'VTODO':
288 // @phan-suppress-next-line PhanTypeMismatchDimFetch
289 $value = $this->cal[$type][$this->todo_count][$key].$value;
290 break;
291 }
292 }
293
294 if (($key == "DTSTAMP") || ($key == "LAST-MODIFIED") || ($key == "CREATED")) {
295 $value = $this->ical_date_to_unix($value);
296 }
297 //if ($key == "RRULE" ) $value = $this->ical_rrule($value);
298
299 if (stristr($key, "DTSTART") || stristr($key, "DTEND") || stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
300 if (stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
301 list($key, $value) = array($key, $value);
302 } else {
303 list($key, $value) = $this->ical_dt_date($key, $value);
304 }
305 }
306
307 switch ($type) {
308 case "VTODO":
309 // @phan-suppress-next-line PhanTypeMismatchReturn
310 $this->cal[$type][$this->todo_count][$key] = $value;
311 break;
312
313 case "VEVENT":
314 // @phan-suppress-next-line PhanTypeMismatchReturn
315 $this->cal[$type][$this->event_count][$key] = $value;
316 break;
317
318 case "VFREEBUSY":
319 // @phan-suppress-next-line PhanTypeMismatchReturn
320 $this->cal[$type][$this->freebusy_count][$key] = $value;
321 break;
322
323 default:
324 // @phan-suppress-next-line PhanTypeMismatchProperty
325 $this->cal[$type][$key] = $value;
326 break;
327 }
328 $this->last_key = $key;
329 }
330
331 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
338 public function retun_key_value($text)
339 {
340 // phpcs:enable
341 /*
342 preg_match("/([^:]+)[:]([\w\W]+)/", $text, $matches);
343
344 if (empty($matches))
345 {
346 return array(false,$text);
347 }
348 else
349 {
350 $matches = array_splice($matches, 1, 2);
351 return $matches;
352 }*/
353 return explode(':', $text, 2);
354 }
355
356 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
363 public function ical_rrule($value)
364 {
365 // phpcs:enable
366 $result = array();
367 $rrule = explode(';', $value);
368 foreach ($rrule as $line) {
369 $rcontent = explode('=', $line);
370 $result[$rcontent[0]] = $rcontent[1];
371 }
372 return $result;
373 }
374
375 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
382 public function ical_date_to_unix($ical_date)
383 {
384 // phpcs:enable
385 $ical_date = str_replace('T', '', $ical_date);
386 $ical_date = str_replace('Z', '', $ical_date);
387
388 $ntime = 0;
389 // TIME LIMITED EVENT
390 $date = array();
391 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)) {
392 $ntime = dol_mktime((int) $date[4], (int) $date[5], (int) $date[6], (int) $date[2], (int) $date[3], (int) $date[1], true);
393 }
394
395 //if (empty($date[4])) print 'Error bad date: '.$ical_date.' - date1='.$date[1];
396 //print dol_print_date($ntime,'dayhour');exit;
397 return $ntime; // ntime is a GTM time
398 }
399
400 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
408 public function ical_dt_date($key, $value)
409 {
410 // phpcs:enable
411 $return_value = array();
412
413 // Analyse TZID
414 $temp = explode(";", $key);
415
416 if (empty($temp[1])) { // not TZID in key
417 $value = $this->ical_date_to_unix($value);
418 return array($key, $value);
419 }
420
421 $value = str_replace('T', '', $value);
422
423 $key = $temp[0];
424 $temp = explode("=", $temp[1]);
425 $return_value[$temp[0]] = $temp[1];
426 $return_value['unixtime'] = $value;
427
428 return array($key, $return_value);
429 }
430
431 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
437 public function get_sort_event_list()
438 {
439 // phpcs:enable
440 $temp = $this->get_event_list();
441 if (!empty($temp)) {
442 usort($temp, array($this, "ical_dtstart_compare")); // false-positive @phpstan-ignore-line
443 return $temp;
444 } else {
445 return false;
446 }
447 }
448
449 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
457 public function ical_dtstart_compare($a, $b)
458 {
459 // phpcs:enable
460 return strnatcasecmp($a['DTSTART']['unixtime'], $b['DTSTART']['unixtime']);
461 }
462
463 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
469 public function get_event_list()
470 {
471 // phpcs:enable
472 return (empty($this->cal['VEVENT']) ? array() : $this->cal['VEVENT']);
473 }
474
475 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
481 public function get_freebusy_list()
482 {
483 // phpcs:enable
484 return (empty($this->cal['VFREEBUSY']) ? array() : $this->cal['VFREEBUSY']);
485 }
486
487 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
493 public function get_todo_list()
494 {
495 // phpcs:enable
496 return $this->cal['VTODO'];
497 }
498
499 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
505 public function get_calender_data()
506 {
507 // phpcs:enable
508 return $this->cal['VCALENDAR'];
509 }
510
511 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
517 public function get_all_data()
518 {
519 // phpcs:enable
520 return $this->cal;
521 }
522}
Class to read/parse ICal calendars.
get_all_data()
Return array with all data.
get_event_list()
Return eventlist array (not sorted eventlist array)
parse($uri, $usecachefile='', $delaycache=3600)
Translate Calendar.
ical_dt_date($key, $value)
Return unix date from iCal date format.
ical_rrule($value)
Parse RRULE return array.
retun_key_value($text)
Parse text "XXXX:value text some with : " and return array($key = "XXXX", $value="value");.
get_todo_count()
Returns the number of to do.
ical_dtstart_compare($a, $b)
Compare two unix timestamp.
get_event_count()
Returns the number of calendar events.
__construct()
Constructor.
add_to_array($type, $key, $value)
Add to $this->ical array one value and key.
read_file($file)
Read text file, icalender text file.
get_sort_event_list()
Return sorted eventlist as array or false if calendar is empty.
get_freebusy_list()
Return freebusy array (not sort eventlist array)
get_todo_list()
Return to do array (not sorted todo array)
get_calender_data()
Return base calendar data.
ical_date_to_unix($ical_date)
Return Unix time from ical date time format (YYYYMMDD[T]HHMMSS[Z] or YYYYMMDD[T]HHMMSS)
dol_filemtime($pathoffile)
Return time of a file.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1)
Function to get a content from an URL (use proxy if proxy defined).
quotedPrintDecode($str)
Decode vcal format.
Definition xcal.lib.php:630