ical.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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@capnetworks.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. /**
  21. * \file htdocs/comm/action/class/ical.class.php
  22. * \ingroup agenda
  23. * \brief File of class to parse ical calendars
  24. */
  25. require_once DOL_DOCUMENT_ROOT.'/core/lib/xcal.lib.php';
  26. /**
  27. * Class to read/parse ICal calendars
  28. */
  29. class ICal
  30. {
  31. var $file_text; // Text in file
  32. var $cal; // Array to save iCalendar parse data
  33. var $event_count; // Number of Events
  34. var $todo_count; // Number of Todos
  35. var $freebusy_count; // Number of Freebusy
  36. var $last_key; //Help variable save last key (multiline string)
  37. /**
  38. * Constructor
  39. */
  40. public function __construct()
  41. {
  42. }
  43. /**
  44. * Read text file, icalender text file
  45. *
  46. * @param string $file File
  47. * @return string
  48. */
  49. function read_file($file)
  50. {
  51. $this->file = $file;
  52. $file_text='';
  53. $tmparray=file($file);
  54. if (is_array($tmparray))
  55. {
  56. $file_text = join("", $tmparray); //load file
  57. $file_text = preg_replace("/[\r\n]{1,} /","",$file_text);
  58. }
  59. return $file_text; // return all text
  60. }
  61. /**
  62. * Returns the number of calendar events
  63. *
  64. * @return int
  65. */
  66. function get_event_count()
  67. {
  68. return $this->event_count;
  69. }
  70. /**
  71. * Returns the number of to do
  72. *
  73. * @return int
  74. */
  75. function get_todo_count()
  76. {
  77. return $this->todo_count;
  78. }
  79. /**
  80. * Translate Calendar
  81. *
  82. * @param string $uri Url
  83. * @return array
  84. */
  85. function parse($uri)
  86. {
  87. $this->cal = array(); // new empty array
  88. $this->event_count = -1;
  89. // read FILE text
  90. $this->file_text = $this->read_file($uri);
  91. $this->file_text = preg_split("[\n]", $this->file_text);
  92. // is this text vcalendar standart text ? on line 1 is BEGIN:VCALENDAR
  93. if (!stristr($this->file_text[0],'BEGIN:VCALENDAR')) return 'error not VCALENDAR';
  94. $insidealarm=0;
  95. $tmpkey='';$tmpvalue='';
  96. foreach ($this->file_text as $text)
  97. {
  98. $text = trim($text); // trim one line
  99. if (!empty($text))
  100. {
  101. // get Key and Value VCALENDAR:Begin -> Key = VCALENDAR, Value = begin
  102. list($key, $value) = $this->retun_key_value($text);
  103. //var_dump($text.' -> '.$key.' - '.$value);
  104. switch ($text) // search special string
  105. {
  106. case "BEGIN:VTODO":
  107. $this->todo_count = $this->todo_count+1; // new to do begin
  108. $type = "VTODO";
  109. break;
  110. case "BEGIN:VEVENT":
  111. $this->event_count = $this->event_count+1; // new event begin
  112. $type = "VEVENT";
  113. break;
  114. case "BEGIN:VFREEBUSY":
  115. $this->freebusy_count = $this->freebusy_count+1; // new event begin
  116. $type = "VFREEBUSY";
  117. break;
  118. case "BEGIN:VCALENDAR": // all other special string
  119. case "BEGIN:DAYLIGHT":
  120. case "BEGIN:VTIMEZONE":
  121. case "BEGIN:STANDARD":
  122. $type = $value; // save array under value key
  123. break;
  124. case "END:VTODO": // end special text - goto VCALENDAR key
  125. case "END:VEVENT":
  126. case "END:VFREEBUSY":
  127. case "END:VCALENDAR":
  128. case "END:DAYLIGHT":
  129. case "END:VTIMEZONE":
  130. case "END:STANDARD":
  131. $type = "VCALENDAR";
  132. break;
  133. // Manage VALARM that are inside a VEVENT to avoid fields of VALARM to overwrites fields of VEVENT
  134. case "BEGIN:VALARM":
  135. $insidealarm=1;
  136. break;
  137. case "END:VALARM":
  138. $insidealarm=0;
  139. break;
  140. default: // no special string (SUMMARY, DESCRIPTION, ...)
  141. if ($tmpvalue)
  142. {
  143. $tmpvalue .= $text;
  144. if (! preg_match('/=$/',$text)) // No more lines
  145. {
  146. $key=$tmpkey;
  147. $value=quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i','',$tmpvalue));
  148. $tmpkey='';
  149. $tmpvalue='';
  150. }
  151. }
  152. elseif (preg_match('/^ENCODING=QUOTED-PRINTABLE:/i',$value))
  153. {
  154. if (preg_match('/=$/',$value))
  155. {
  156. $tmpkey=$key;
  157. $tmpvalue=$tmpvalue.preg_replace('/=$/',"",$value); // We must wait to have next line to have complete message
  158. }
  159. else
  160. {
  161. $value=quotedPrintDecode(preg_replace('/^ENCODING=QUOTED-PRINTABLE:/i','',$tmpvalue.$value));
  162. }
  163. } //$value=quotedPrintDecode($tmpvalue.$value);
  164. if (! $insidealarm && ! $tmpkey) $this->add_to_array($type, $key, $value); // add to array
  165. break;
  166. }
  167. }
  168. }
  169. //var_dump($this->cal);
  170. return $this->cal;
  171. }
  172. /**
  173. * Add to $this->ical array one value and key.
  174. *
  175. * @param string $type Type ('VTODO', 'VEVENT', 'VFREEBUSY', 'VCALENDAR'...)
  176. * @param string $key Key ('DTSTART', ...). Note: Field is never 'DTSTART;TZID=...' because ';...' was before removed and added as another property
  177. * @param string $value Value
  178. * @return void
  179. */
  180. function add_to_array($type, $key, $value)
  181. {
  182. //print 'type='.$type.' key='.$key.' value='.$value.'<br>'."\n";
  183. if (empty($key))
  184. {
  185. $key = $this->last_key;
  186. switch ($type)
  187. {
  188. case 'VEVENT': $value = $this->cal[$type][$this->event_count][$key].$value;break;
  189. case 'VFREEBUSY': $value = $this->cal[$type][$this->freebusy_count][$key].$value;break;
  190. case 'VTODO': $value = $this->cal[$type][$this->todo_count][$key].$value;break;
  191. }
  192. }
  193. if (($key == "DTSTAMP") or ($key == "LAST-MODIFIED") or ($key == "CREATED")) $value = $this->ical_date_to_unix($value);
  194. //if ($key == "RRULE" ) $value = $this->ical_rrule($value);
  195. if (stristr($key,"DTSTART") or stristr($key,"DTEND") or stristr($key,"DTSTART;VALUE=DATE") or stristr($key,"DTEND;VALUE=DATE"))
  196. {
  197. if (stristr($key,"DTSTART;VALUE=DATE") or stristr($key,"DTEND;VALUE=DATE"))
  198. {
  199. list($key,$value) = array($key,$value);
  200. }
  201. else
  202. {
  203. list($key,$value) = $this->ical_dt_date($key,$value);
  204. }
  205. }
  206. switch ($type)
  207. {
  208. case "VTODO":
  209. $this->cal[$type][$this->todo_count][$key] = $value;
  210. break;
  211. case "VEVENT":
  212. $this->cal[$type][$this->event_count][$key] = $value;
  213. break;
  214. case "VFREEBUSY":
  215. $this->cal[$type][$this->freebusy_count][$key] = $value;
  216. break;
  217. default:
  218. $this->cal[$type][$key] = $value;
  219. break;
  220. }
  221. $this->last_key = $key;
  222. }
  223. /**
  224. * Parse text "XXXX:value text some with : " and return array($key = "XXXX", $value="value");
  225. *
  226. * @param string $text Text
  227. * @return array
  228. */
  229. function retun_key_value($text)
  230. {
  231. /*
  232. preg_match("/([^:]+)[:]([\w\W]+)/", $text, $matches);
  233. if (empty($matches))
  234. {
  235. return array(false,$text);
  236. }
  237. else
  238. {
  239. $matches = array_splice($matches, 1, 2);
  240. return $matches;
  241. }*/
  242. return explode(':',$text,2);
  243. }
  244. /**
  245. * Parse RRULE return array
  246. *
  247. * @param string $value string
  248. * @return array
  249. */
  250. function ical_rrule($value)
  251. {
  252. $result=array();
  253. $rrule = explode(';',$value);
  254. foreach ($rrule as $line)
  255. {
  256. $rcontent = explode('=', $line);
  257. $result[$rcontent[0]] = $rcontent[1];
  258. }
  259. return $result;
  260. }
  261. /**
  262. * Return Unix time from ical date time fomrat (YYYYMMDD[T]HHMMSS[Z] or YYYYMMDD[T]HHMMSS)
  263. *
  264. * @param string $ical_date String date
  265. * @return int
  266. */
  267. function ical_date_to_unix($ical_date)
  268. {
  269. $ical_date = str_replace('T', '', $ical_date);
  270. $ical_date = str_replace('Z', '', $ical_date);
  271. $ntime=0;
  272. // TIME LIMITED EVENT
  273. 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))
  274. $ntime=dol_mktime($date[4], $date[5], $date[6], $date[2],$date[3], $date[1], true);
  275. //if (empty($date[4])) print 'Error bad date: '.$ical_date.' - date1='.$date[1];
  276. //print dol_print_date($ntime,'dayhour');exit;
  277. return $ntime; // ntime is a GTM time
  278. }
  279. /**
  280. * Return unix date from iCal date format
  281. *
  282. * @param string $key Key
  283. * @param string $value Value
  284. * @return array
  285. */
  286. function ical_dt_date($key, $value)
  287. {
  288. $return_value=array();
  289. $value = $this->ical_date_to_unix($value);
  290. // Analyse TZID
  291. $temp = explode(";",$key);
  292. if (empty($temp[1])) // not TZID
  293. {
  294. $value = str_replace('T', '', $value);
  295. return array($key,$value);
  296. }
  297. $key = $temp[0];
  298. $temp = explode("=", $temp[1]);
  299. $return_value[$temp[0]] = $temp[1];
  300. $return_value['unixtime'] = $value;
  301. return array($key,$return_value);
  302. }
  303. /**
  304. * Return sorted eventlist as array or false if calenar is empty
  305. *
  306. * @return array
  307. */
  308. function get_sort_event_list()
  309. {
  310. $temp = $this->get_event_list();
  311. if (!empty($temp))
  312. {
  313. usort($temp, array(&$this, "ical_dtstart_compare"));
  314. return $temp;
  315. }
  316. else
  317. {
  318. return false;
  319. }
  320. }
  321. /**
  322. * Compare two unix timestamp
  323. *
  324. * @param array $a Operand a
  325. * @param array $b Operand b
  326. * @return integer
  327. */
  328. function ical_dtstart_compare($a, $b)
  329. {
  330. return strnatcasecmp($a['DTSTART']['unixtime'], $b['DTSTART']['unixtime']);
  331. }
  332. /**
  333. * Return eventlist array (not sort eventlist array)
  334. *
  335. * @return array
  336. */
  337. function get_event_list()
  338. {
  339. return (! empty($this->cal['VEVENT'])?$this->cal['VEVENT']:'');
  340. }
  341. /**
  342. * Return eventlist array (not sort eventlist array)
  343. *
  344. * @return array
  345. */
  346. function get_freebusy_list()
  347. {
  348. return $this->cal['VFREEBUSY'];
  349. }
  350. /**
  351. * Return to do array (not sort to do array)
  352. *
  353. * @return array
  354. */
  355. function get_todo_list()
  356. {
  357. return $this->cal['VTODO'];
  358. }
  359. /**
  360. * Return base calendar data
  361. *
  362. * @return array
  363. */
  364. function get_calender_data()
  365. {
  366. return $this->cal['VCALENDAR'];
  367. }
  368. /**
  369. * Return array with all data
  370. *
  371. * @return array
  372. */
  373. function get_all_data()
  374. {
  375. return $this->cal;
  376. }
  377. }