dolibarr  20.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 
28 require_once DOL_DOCUMENT_ROOT.'/core/lib/xcal.lib.php';
29 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
30 
31 
35 class 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 = $this->todo_count + 1; // new to do begin
194  $type = "VTODO";
195  break;
196 
197  case "BEGIN:VEVENT":
198  $this->event_count = $this->event_count + 1; // new event begin
199  $type = "VEVENT";
200  break;
201 
202  case "BEGIN:VFREEBUSY":
203  $this->freebusy_count = $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 = $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  $value = $this->cal[$type][$this->event_count][$key].$value;
281  break;
282  case 'VFREEBUSY':
283  $value = $this->cal[$type][$this->freebusy_count][$key].$value;
284  break;
285  case 'VTODO':
286  $value = $this->cal[$type][$this->todo_count][$key].$value;
287  break;
288  }
289  }
290 
291  if (($key == "DTSTAMP") || ($key == "LAST-MODIFIED") || ($key == "CREATED")) {
292  $value = $this->ical_date_to_unix($value);
293  }
294  //if ($key == "RRULE" ) $value = $this->ical_rrule($value);
295 
296  if (stristr($key, "DTSTART") || stristr($key, "DTEND") || stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
297  if (stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
298  list($key, $value) = array($key, $value);
299  } else {
300  list($key, $value) = $this->ical_dt_date($key, $value);
301  }
302  }
303 
304  switch ($type) {
305  case "VTODO":
306  $this->cal[$type][$this->todo_count][$key] = $value;
307  break;
308 
309  case "VEVENT":
310  $this->cal[$type][$this->event_count][$key] = $value;
311  break;
312 
313  case "VFREEBUSY":
314  $this->cal[$type][$this->freebusy_count][$key] = $value;
315  break;
316 
317  default:
318  $this->cal[$type][$key] = $value;
319  break;
320  }
321  $this->last_key = $key;
322  }
323 
324  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
331  public function retun_key_value($text)
332  {
333  // phpcs:enable
334  /*
335  preg_match("/([^:]+)[:]([\w\W]+)/", $text, $matches);
336 
337  if (empty($matches))
338  {
339  return array(false,$text);
340  }
341  else
342  {
343  $matches = array_splice($matches, 1, 2);
344  return $matches;
345  }*/
346  return explode(':', $text, 2);
347  }
348 
349  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
356  public function ical_rrule($value)
357  {
358  // phpcs:enable
359  $result = array();
360  $rrule = explode(';', $value);
361  foreach ($rrule as $line) {
362  $rcontent = explode('=', $line);
363  $result[$rcontent[0]] = $rcontent[1];
364  }
365  return $result;
366  }
367 
368  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
375  public function ical_date_to_unix($ical_date)
376  {
377  // phpcs:enable
378  $ical_date = str_replace('T', '', $ical_date);
379  $ical_date = str_replace('Z', '', $ical_date);
380 
381  $ntime = 0;
382  // TIME LIMITED EVENT
383  $date = array();
384  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)) {
385  $ntime = dol_mktime($date[4], $date[5], $date[6], $date[2], $date[3], $date[1], true);
386  }
387 
388  //if (empty($date[4])) print 'Error bad date: '.$ical_date.' - date1='.$date[1];
389  //print dol_print_date($ntime,'dayhour');exit;
390  return $ntime; // ntime is a GTM time
391  }
392 
393  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
401  public function ical_dt_date($key, $value)
402  {
403  // phpcs:enable
404  $return_value = array();
405 
406  // Analyse TZID
407  $temp = explode(";", $key);
408 
409  if (empty($temp[1])) { // not TZID in key
410  $value = $this->ical_date_to_unix($value);
411  return array($key, $value);
412  }
413 
414  $value = str_replace('T', '', $value);
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
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
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
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
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
486  public function get_todo_list()
487  {
488  // phpcs:enable
489  return $this->cal['VTODO'];
490  }
491 
492  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
498  public function get_calender_data()
499  {
500  // phpcs:enable
501  return $this->cal['VCALENDAR'];
502  }
503 
504  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
510  public function get_all_data()
511  {
512  // phpcs:enable
513  return $this->cal;
514  }
515 }
Class to read/parse ICal calendars.
Definition: ical.class.php:36
get_all_data()
Return array with all data.
Definition: ical.class.php:510
get_event_list()
Return eventlist array (not sorted eventlist array)
Definition: ical.class.php:462
parse($uri, $usecachefile='', $delaycache=3600)
Translate Calendar.
Definition: ical.class.php:143
ical_dt_date($key, $value)
Return unix date from iCal date format.
Definition: ical.class.php:401
ical_rrule($value)
Parse RRULE return array.
Definition: ical.class.php:356
retun_key_value($text)
Parse text "XXXX:value text some with : " and return array($key = "XXXX", $value="value");.
Definition: ical.class.php:331
get_todo_count()
Returns the number of to do.
Definition: ical.class.php:129
ical_dtstart_compare($a, $b)
Compare two unix timestamp.
Definition: ical.class.php:450
get_event_count()
Returns the number of calendar events.
Definition: ical.class.php:117
__construct()
Constructor.
Definition: ical.class.php:81
add_to_array($type, $key, $value)
Add to $this->ical array one value and key.
Definition: ical.class.php:270
read_file($file)
Read text file, icalender text file.
Definition: ical.class.php:92
get_sort_event_list()
Return sorted eventlist as array or false if calendar is empty.
Definition: ical.class.php:430
get_freebusy_list()
Return freebusy array (not sort eventlist array)
Definition: ical.class.php:474
get_todo_list()
Return to do array (not sorted todo array)
Definition: ical.class.php:486
get_calender_data()
Return base calendar data.
Definition: ical.class.php:498
ical_date_to_unix($ical_date)
Return Unix time from ical date time format (YYYYMMDD[T]HHMMSS[Z] or YYYYMMDD[T]HHMMSS)
Definition: ical.class.php:375
dol_filemtime($pathoffile)
Return time of a file.
Definition: files.lib.php:635
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).
Definition: geturl.lib.php:42
quotedPrintDecode($str)
Decode vcal format.
Definition: xcal.lib.php:592