dolibarr  19.0.0-dev
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 Frédéric France <frederic.france@netlogic.fr>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
27 require_once DOL_DOCUMENT_ROOT.'/core/lib/xcal.lib.php';
28 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
29 
30 
34 class ICal
35 {
36  // Text in file
37  public $file_text;
38  public $cal; // Array to save iCalendar parse data
39  public $event_count; // Number of Events
40  public $todo_count; // Number of Todos
41  public $freebusy_count; // Number of Freebusy
42  public $last_key; //Help variable save last key (multiline string)
43  public $error;
44 
45 
49  public function __construct()
50  {
51  }
52 
53  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
60  public function read_file($file)
61  {
62  // phpcs:enable
63  $this->file = $file;
64  $file_text = '';
65 
66  $tmpresult = getURLContent($file, 'GET');
67  if ($tmpresult['http_code'] != 200) {
68  $file_text = null;
69  $this->error = 'Error: '.$tmpresult['http_code'].' '.$tmpresult['content'];
70  } else {
71  $file_text = preg_replace("/[\r\n]{1,} /", "", $tmpresult['content']);
72  }
73  //var_dump($tmpresult);
74 
75  return $file_text; // return all text
76  }
77 
78  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
84  public function get_event_count()
85  {
86  // phpcs:enable
87  return $this->event_count;
88  }
89 
90  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
96  public function get_todo_count()
97  {
98  // phpcs:enable
99  return $this->todo_count;
100  }
101 
110  public function parse($uri, $usecachefile = '', $delaycache = 3600)
111  {
112  $this->cal = array(); // new empty array
113 
114  $this->event_count = -1;
115  $this->file_text = null;
116 
117  // Save file into a cache
118  if ($usecachefile) {
119  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
120  $datefile = dol_filemtime($usecachefile);
121  $now = dol_now('gmt');
122  //print $datefile.' '.$now.' ...';
123  if ($datefile && $datefile > ($now - $delaycache)) {
124  // We reuse the cache file
125  $this->file_text = file_get_contents($usecachefile);
126  }
127  }
128 
129  // read FILE text
130  if (is_null($this->file_text)) {
131  $this->file_text = $this->read_file($uri);
132 
133  if ($usecachefile && !is_null($this->file_text)) {
134  // Save the file content into cache file
135  file_put_contents($usecachefile, $this->file_text, LOCK_EX);
136  dolChmod($usecachefile);
137  }
138  }
139 
140  $this->file_text = preg_split("[\n]", $this->file_text);
141 
142  // is this text vcalendar standard text ? on line 1 is BEGIN:VCALENDAR
143  if (!stristr($this->file_text[0], 'BEGIN:VCALENDAR')) {
144  return 'error not VCALENDAR';
145  }
146 
147  $insidealarm = 0;
148  $tmpkey = ''; $tmpvalue = ''; $type = '';
149  foreach ($this->file_text as $text) {
150  $text = trim($text); // trim one line
151  if (!empty($text)) {
152  // get Key and Value VCALENDAR:Begin -> Key = VCALENDAR, Value = begin
153  list($key, $value) = $this->retun_key_value($text);
154  //var_dump($text.' -> '.$key.' - '.$value);
155 
156  switch ($text) { // search special string
157  case "BEGIN:VTODO":
158  $this->todo_count = $this->todo_count + 1; // new to do begin
159  $type = "VTODO";
160  break;
161 
162  case "BEGIN:VEVENT":
163  $this->event_count = $this->event_count + 1; // new event begin
164  $type = "VEVENT";
165  break;
166 
167  case "BEGIN:VFREEBUSY":
168  $this->freebusy_count = $this->freebusy_count + 1; // new event begin
169  $type = "VFREEBUSY";
170  break;
171 
172  case "BEGIN:VCALENDAR": // all other special string
173  case "BEGIN:DAYLIGHT":
174  case "BEGIN:VTIMEZONE":
175  case "BEGIN:STANDARD":
176  $type = $value; // save array under value key
177  break;
178 
179  case "END:VTODO": // end special text - goto VCALENDAR key
180  case "END:VEVENT":
181  case "END:VFREEBUSY":
182  case "END:VCALENDAR":
183  case "END:DAYLIGHT":
184  case "END:VTIMEZONE":
185  case "END:STANDARD":
186  $type = "VCALENDAR";
187  break;
188 
189  // Manage VALARM that are inside a VEVENT to avoid fields of VALARM to overwrites fields of VEVENT
190  case "BEGIN:VALARM":
191  $insidealarm = 1;
192  break;
193  case "END:VALARM":
194  $insidealarm = 0;
195  break;
196 
197  default: // no special string (SUMMARY, DESCRIPTION, ...)
198  if ($tmpvalue) {
199  $tmpvalue .= $text;
200  if (!preg_match('/=$/', $text)) { // No more lines
201  $key = $tmpkey;
202  $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue));
203  $tmpkey = '';
204  $tmpvalue = '';
205  }
206  } elseif (preg_match('/^ENCODING=QUOTED-PRINTABLE:/i', $value)) {
207  if (preg_match('/=$/', $value)) {
208  $tmpkey = $key;
209  $tmpvalue = $tmpvalue.preg_replace('/=$/', "", $value); // We must wait to have next line to have complete message
210  } else {
211  $value = quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i', '', $tmpvalue.$value));
212  }
213  } //$value=quotedPrintDecode($tmpvalue.$value);
214  if (!$insidealarm && !$tmpkey) {
215  $this->add_to_array($type, $key, $value); // add to array
216  }
217  break;
218  }
219  }
220  }
221 
222  //var_dump($this->cal);
223  return $this->cal;
224  }
225 
226  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
235  public function add_to_array($type, $key, $value)
236  {
237  // phpcs:enable
238 
239  //print 'type='.$type.' key='.$key.' value='.$value.'<br>'."\n";
240 
241  if (empty($key)) {
242  $key = $this->last_key;
243  switch ($type) {
244  case 'VEVENT':
245  $value = $this->cal[$type][$this->event_count][$key].$value;
246  break;
247  case 'VFREEBUSY':
248  $value = $this->cal[$type][$this->freebusy_count][$key].$value;
249  break;
250  case 'VTODO':
251  $value = $this->cal[$type][$this->todo_count][$key].$value;
252  break;
253  }
254  }
255 
256  if (($key == "DTSTAMP") || ($key == "LAST-MODIFIED") || ($key == "CREATED")) {
257  $value = $this->ical_date_to_unix($value);
258  }
259  //if ($key == "RRULE" ) $value = $this->ical_rrule($value);
260 
261  if (stristr($key, "DTSTART") || stristr($key, "DTEND") || stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
262  if (stristr($key, "DTSTART;VALUE=DATE") || stristr($key, "DTEND;VALUE=DATE")) {
263  list($key, $value) = array($key, $value);
264  } else {
265  list($key, $value) = $this->ical_dt_date($key, $value);
266  }
267  }
268 
269  switch ($type) {
270  case "VTODO":
271  $this->cal[$type][$this->todo_count][$key] = $value;
272  break;
273 
274  case "VEVENT":
275  $this->cal[$type][$this->event_count][$key] = $value;
276  break;
277 
278  case "VFREEBUSY":
279  $this->cal[$type][$this->freebusy_count][$key] = $value;
280  break;
281 
282  default:
283  $this->cal[$type][$key] = $value;
284  break;
285  }
286  $this->last_key = $key;
287  }
288 
289  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
296  public function retun_key_value($text)
297  {
298  // phpcs:enable
299  /*
300  preg_match("/([^:]+)[:]([\w\W]+)/", $text, $matches);
301 
302  if (empty($matches))
303  {
304  return array(false,$text);
305  }
306  else
307  {
308  $matches = array_splice($matches, 1, 2);
309  return $matches;
310  }*/
311  return explode(':', $text, 2);
312  }
313 
314  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
321  public function ical_rrule($value)
322  {
323  // phpcs:enable
324  $result = array();
325  $rrule = explode(';', $value);
326  foreach ($rrule as $line) {
327  $rcontent = explode('=', $line);
328  $result[$rcontent[0]] = $rcontent[1];
329  }
330  return $result;
331  }
332 
333  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
340  public function ical_date_to_unix($ical_date)
341  {
342  // phpcs:enable
343  $ical_date = str_replace('T', '', $ical_date);
344  $ical_date = str_replace('Z', '', $ical_date);
345 
346  $ntime = 0;
347  // TIME LIMITED EVENT
348  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)) {
349  $ntime = dol_mktime($date[4], $date[5], $date[6], $date[2], $date[3], $date[1], true);
350  }
351 
352  //if (empty($date[4])) print 'Error bad date: '.$ical_date.' - date1='.$date[1];
353  //print dol_print_date($ntime,'dayhour');exit;
354  return $ntime; // ntime is a GTM time
355  }
356 
357  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
365  public function ical_dt_date($key, $value)
366  {
367  // phpcs:enable
368  $return_value = array();
369  $value = $this->ical_date_to_unix($value);
370 
371  // Analyse TZID
372  $temp = explode(";", $key);
373 
374  if (empty($temp[1])) { // not TZID
375  $value = str_replace('T', '', $value);
376  return array($key, $value);
377  }
378 
379  $key = $temp[0];
380  $temp = explode("=", $temp[1]);
381  $return_value[$temp[0]] = $temp[1];
382  $return_value['unixtime'] = $value;
383 
384  return array($key, $return_value);
385  }
386 
387  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
393  public function get_sort_event_list()
394  {
395  // phpcs:enable
396  $temp = $this->get_event_list();
397  if (!empty($temp)) {
398  usort($temp, array(&$this, "ical_dtstart_compare"));
399  return $temp;
400  } else {
401  return false;
402  }
403  }
404 
405  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
413  public function ical_dtstart_compare($a, $b)
414  {
415  // phpcs:enable
416  return strnatcasecmp($a['DTSTART']['unixtime'], $b['DTSTART']['unixtime']);
417  }
418 
419  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
425  public function get_event_list()
426  {
427  // phpcs:enable
428  return (empty($this->cal['VEVENT']) ? array() : $this->cal['VEVENT']);
429  }
430 
431  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
437  public function get_freebusy_list()
438  {
439  // phpcs:enable
440  return (empty($this->cal['VFREEBUSY']) ? array() : $this->cal['VFREEBUSY']);
441  }
442 
443  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
449  public function get_todo_list()
450  {
451  // phpcs:enable
452  return $this->cal['VTODO'];
453  }
454 
455  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
461  public function get_calender_data()
462  {
463  // phpcs:enable
464  return $this->cal['VCALENDAR'];
465  }
466 
467  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
473  public function get_all_data()
474  {
475  // phpcs:enable
476  return $this->cal;
477  }
478 }
Class to read/parse ICal calendars.
Definition: ical.class.php:35
get_all_data()
Return array with all data.
Definition: ical.class.php:473
get_event_list()
Return eventlist array (not sorted eventlist array)
Definition: ical.class.php:425
parse($uri, $usecachefile='', $delaycache=3600)
Translate Calendar.
Definition: ical.class.php:110
ical_dt_date($key, $value)
Return unix date from iCal date format.
Definition: ical.class.php:365
ical_rrule($value)
Parse RRULE return array.
Definition: ical.class.php:321
retun_key_value($text)
Parse text "XXXX:value text some with : " and return array($key = "XXXX", $value="value");.
Definition: ical.class.php:296
get_todo_count()
Returns the number of to do.
Definition: ical.class.php:96
ical_dtstart_compare($a, $b)
Compare two unix timestamp.
Definition: ical.class.php:413
get_event_count()
Returns the number of calendar events.
Definition: ical.class.php:84
__construct()
Constructor.
Definition: ical.class.php:49
add_to_array($type, $key, $value)
Add to $this->ical array one value and key.
Definition: ical.class.php:235
read_file($file)
Read text file, icalender text file.
Definition: ical.class.php:60
get_sort_event_list()
Return sorted eventlist as array or false if calendar is empty.
Definition: ical.class.php:393
get_freebusy_list()
Return freebusy array (not sort eventlist array)
Definition: ical.class.php:437
get_todo_list()
Return to do array (not sorted todo array)
Definition: ical.class.php:449
get_calender_data()
Return base calendar data.
Definition: ical.class.php:461
ical_date_to_unix($ical_date)
Return Unix time from ical date time fomrat (YYYYMMDD[T]HHMMSS[Z] or YYYYMMDD[T]HHMMSS)
Definition: ical.class.php:340
dol_filemtime($pathoffile)
Return time of a file.
Definition: files.lib.php:599
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
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:41
quotedPrintDecode($str)
Decode vcal format.
Definition: xcal.lib.php:590