xcal.lib.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <?php
  2. /* Copyright (C) 2008-2011 Laurent Destailleur <eldy@users.sourceforge.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \file htdocs/core/lib/xcal.lib.php
  19. * \brief Function to manage calendar files (vcal/ical/...)
  20. */
  21. /**
  22. * Build a file from an array of events
  23. * All input params and data must be encoded in $conf->charset_output
  24. *
  25. * @param string $format 'vcal' or 'ical'
  26. * @param string $title Title of export
  27. * @param string $desc Description of export
  28. * @param array $events_array Array of events ('eid','startdate','duration','enddate','title','summary','category','email','url','desc','author')
  29. * @param string $outputfile Output file
  30. * @return int <0 if ko, Nb of events in file if ok
  31. */
  32. function build_calfile($format,$title,$desc,$events_array,$outputfile)
  33. {
  34. global $conf,$langs;
  35. dol_syslog("xcal.lib.php::build_calfile Build cal file ".$outputfile." to format ".$format);
  36. if (empty($outputfile)) return -1;
  37. // Note: A cal file is an UTF8 encoded file
  38. $calfileh=fopen($outputfile,'w');
  39. if ($calfileh)
  40. {
  41. include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  42. $now=dol_now();
  43. $encoding='';
  44. if ($format == 'vcal') $encoding='ENCODING=QUOTED-PRINTABLE:';
  45. // Print header
  46. fwrite($calfileh,"BEGIN:VCALENDAR\n");
  47. fwrite($calfileh,"VERSION:2.0\n");
  48. fwrite($calfileh,"METHOD:PUBLISH\n");
  49. //fwrite($calfileh,"PRODID:-//DOLIBARR ".DOL_VERSION."//EN\n");
  50. fwrite($calfileh,"PRODID:-//DOLIBARR ".DOL_VERSION."\n");
  51. fwrite($calfileh,"CALSCALE:GREGORIAN\n");
  52. fwrite($calfileh,"X-WR-CALNAME:".$encoding.format_cal($format,$title)."\n");
  53. fwrite($calfileh,"X-WR-CALDESC:".$encoding.format_cal($format,$desc)."\n");
  54. //fwrite($calfileh,"X-WR-TIMEZONE:Europe/Paris\n");
  55. if (! empty($conf->global->MAIN_AGENDA_EXPORT_CACHE)
  56. && $conf->global->MAIN_AGENDA_EXPORT_CACHE > 60){
  57. $hh=convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE,'hour');
  58. $mm=convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE,'min');
  59. $ss=convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE,'sec');
  60. fwrite($calfileh,"X-PUBLISHED-TTL: P".$hh."H".$mm."M".$ss."S\n");
  61. }
  62. foreach ($events_array as $date => $event)
  63. {
  64. $eventqualified=true;
  65. if ($eventqualified)
  66. {
  67. // See http://fr.wikipedia.org/wiki/ICalendar for format
  68. // See http://www.ietf.org/rfc/rfc2445.txt for RFC
  69. $uid = $event['uid'];
  70. $type = $event['type'];
  71. $startdate = $event['startdate'];
  72. $duration = $event['duration'];
  73. $enddate = $event['enddate'];
  74. $summary = $event['summary'];
  75. $category = $event['category'];
  76. $priority = $event['priority'];
  77. $fulldayevent = $event['fulldayevent'];
  78. $location = $event['location'];
  79. $email = $event['email'];
  80. $url = $event['url'];
  81. $transparency = $event['transparency']; // OPAQUE (busy) or TRANSPARENT (not busy)
  82. $description=preg_replace('/<br[\s\/]?>/i',"\n",$event['desc']);
  83. $description=dol_string_nohtmltag($description,0); // Remove html tags
  84. $created = $event['created'];
  85. $modified = $event['modified'];
  86. // Uncomment for tests
  87. //$summary="Resume";
  88. //$description="Description";
  89. //$description="MemberValidatedInDolibarr gd gdf gd gdff\nNom: tgdf g dfgdf gfd r ter\nType: gdfgfdf dfg fd gfd gd gdf gdf gfd gdfg dfg ddf\nAuteur: AD01fg dgdgdfg df gdf gd";
  90. // Format
  91. $summary=format_cal($format,$summary);
  92. $description=format_cal($format,$description);
  93. $category=format_cal($format,$category);
  94. $location=format_cal($format,$location);
  95. // Output the vCard/iCal VEVENT object
  96. /*
  97. Example from Google ical export for a 1 hour event:
  98. BEGIN:VEVENT
  99. DTSTART:20101103T120000Z
  100. DTEND:20101103T130000Z
  101. DTSTAMP:20101121T144902Z
  102. UID:4eilllcsq8r1p87ncg7vc8dbpk@google.com
  103. CREATED:20101121T144657Z
  104. DESCRIPTION:
  105. LAST-MODIFIED:20101121T144707Z
  106. LOCATION:
  107. SEQUENCE:0
  108. STATUS:CONFIRMED
  109. SUMMARY:Tâche 1 heure
  110. TRANSP:OPAQUE
  111. END:VEVENT
  112. Example from Google ical export for a 1 day event:
  113. BEGIN:VEVENT
  114. DTSTART;VALUE=DATE:20101102
  115. DTEND;VALUE=DATE:20101103
  116. DTSTAMP:20101121T144902Z
  117. UID:d09t43kcf1qgapu9efsmmo1m6k@google.com
  118. CREATED:20101121T144607Z
  119. DESCRIPTION:
  120. LAST-MODIFIED:20101121T144607Z
  121. LOCATION:
  122. SEQUENCE:0
  123. STATUS:CONFIRMED
  124. SUMMARY:Tâche 1 jour
  125. TRANSP:TRANSPARENT
  126. END:VEVENT
  127. */
  128. if ($type == 'event')
  129. {
  130. fwrite($calfileh,"BEGIN:VEVENT\n");
  131. fwrite($calfileh,"UID:".$uid."\n");
  132. if (! empty($email))
  133. {
  134. fwrite($calfileh,"ORGANIZER:MAILTO:".$email."\n");
  135. fwrite($calfileh,"CONTACT:MAILTO:".$email."\n");
  136. }
  137. if (! empty($url))
  138. {
  139. fwrite($calfileh,"URL:".$url."\n");
  140. };
  141. if ($created) fwrite($calfileh,"CREATED:".dol_print_date($created,'dayhourxcard',true)."\n");
  142. if ($modified) fwrite($calfileh,"LAST-MODIFIED:".dol_print_date($modified,'dayhourxcard',true)."\n");
  143. fwrite($calfileh,"SUMMARY:".$encoding.$summary."\n");
  144. fwrite($calfileh,"DESCRIPTION:".$encoding.$description."\n");
  145. /* Other keys:
  146. // Status values for a "VEVENT"
  147. statvalue = "TENTATIVE" ;Indicates event is
  148. ;tentative.
  149. / "CONFIRMED" ;Indicates event is
  150. ;definite.
  151. / "CANCELLED" ;Indicates event was
  152. // Status values for "VTODO".
  153. statvalue =/ "NEEDS-ACTION" ;Indicates to-do needs action.
  154. / "COMPLETED" ;Indicates to-do completed.
  155. / "IN-PROCESS" ;Indicates to-do in process of
  156. / "CANCELLED" ;Indicates to-do was cancelled.
  157. // Status values for "VJOURNAL".
  158. statvalue =/ "DRAFT" ;Indicates journal is draft.
  159. / "FINAL" ;Indicates journal is final.
  160. / "CANCELLED" ;Indicates journal is removed.
  161. */
  162. //fwrite($calfileh,"CLASS:PUBLIC\n"); // PUBLIC, PRIVATE, CONFIDENTIAL
  163. //fwrite($calfileh,"X-MICROSOFT-CDO-BUSYSTATUS:1\n");
  164. //ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Laurent Destailleur;X-NUM-GUESTS=0:mailto:eldy10@gmail.com
  165. if (! empty($location)) fwrite($calfileh,"LOCATION:".$encoding.$location."\n");
  166. if ($fulldayevent) fwrite($calfileh,"X-FUNAMBOL-ALLDAY:1\n");
  167. if ($fulldayevent) fwrite($calfileh,"X-MICROSOFT-CDO-ALLDAYEVENT:1\n");
  168. // Date must be GMT dates
  169. // Current date
  170. fwrite($calfileh,"DTSTAMP:".dol_print_date($now,'dayhourxcard',true)."\n");
  171. // Start date
  172. $prefix='';
  173. $startdatef = dol_print_date($startdate,'dayhourxcard',true);
  174. if ($fulldayevent)
  175. {
  176. $prefix=';VALUE=DATE';
  177. $startdatef = dol_print_date($startdate,'dayxcard',false); // Local time
  178. }
  179. fwrite($calfileh,"DTSTART".$prefix.":".$startdatef."\n");
  180. // End date
  181. if ($fulldayevent)
  182. {
  183. if (empty($enddate)) $enddate=dol_time_plus_duree($startdate,1,'d');
  184. }
  185. else
  186. {
  187. if (empty($enddate)) $enddate=$startdate+$duration;
  188. }
  189. $prefix='';
  190. $enddatef = dol_print_date($enddate,'dayhourxcard',true);
  191. if ($fulldayevent)
  192. {
  193. $prefix=';VALUE=DATE';
  194. $enddatef = dol_print_date($enddate+1,'dayxcard',false);
  195. //$enddatef .= dol_print_date($enddate+1,'dayhourxcard',false); // Local time
  196. }
  197. fwrite($calfileh,"DTEND".$prefix.":".$enddatef."\n");
  198. fwrite($calfileh,'STATUS:CONFIRMED'."\n");
  199. if (! empty($transparency)) fwrite($calfileh,"TRANSP:".$transparency."\n");
  200. if (! empty($category)) fwrite($calfileh,"CATEGORIES:".$encoding.$category."\n");
  201. fwrite($calfileh,"END:VEVENT\n");
  202. }
  203. // Output the vCard/iCal VTODO object
  204. // ...
  205. //PERCENT-COMPLETE:39
  206. // Output the vCard/iCal VJOURNAL object
  207. if ($type == 'journal')
  208. {
  209. fwrite($calfileh,"BEGIN:VJOURNAL\n");
  210. fwrite($calfileh,"UID:".$uid."\n");
  211. if (! empty($email))
  212. {
  213. fwrite($calfileh,"ORGANIZER:MAILTO:".$email."\n");
  214. fwrite($calfileh,"CONTACT:MAILTO:".$email."\n");
  215. }
  216. if (! empty($url))
  217. {
  218. fwrite($calfileh,"URL:".$url."\n");
  219. };
  220. if ($created) fwrite($calfileh,"CREATED:".dol_print_date($created,'dayhourxcard',true)."\n");
  221. if ($modified) fwrite($calfileh,"LAST-MODIFIED:".dol_print_date($modified,'dayhourxcard',true)."\n");
  222. fwrite($calfileh,"SUMMARY:".$encoding.$summary."\n");
  223. fwrite($calfileh,"DESCRIPTION:".$encoding.$description."\n");
  224. fwrite($calfileh,'STATUS:CONFIRMED'."\n");
  225. fwrite($calfileh,"CATEGORIES:".$category."\n");
  226. fwrite($calfileh,"LOCATION:".$location."\n");
  227. fwrite($calfileh,"TRANSP:OPAQUE\n");
  228. fwrite($calfileh,"CLASS:CONFIDENTIAL\n");
  229. fwrite($calfileh,"DTSTAMP:".dol_print_date($startdatef,'dayhourxcard',true)."\n");
  230. fwrite($calfileh,"END:VJOURNAL\n");
  231. }
  232. // Put other info in comment
  233. /*
  234. $comment=array();
  235. $comment ['eid'] = $eid;
  236. $comment ['url'] = $linktoevent;
  237. $comment ['date'] = dol_mktime($evttime,"Ymd");
  238. $comment ['duration'] = $duration;
  239. $comment ['startdate'] = $startdate;
  240. $comment ['enddate'] = $enddate;
  241. fwrite($calfileh,"COMMENT:" . serialize ($comment) . "\n");
  242. */
  243. }
  244. }
  245. // Footer
  246. fwrite($calfileh,"END:VCALENDAR");
  247. fclose($calfileh);
  248. if (! empty($conf->global->MAIN_UMASK))
  249. @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
  250. }
  251. else
  252. {
  253. dol_syslog("xcal.lib.php::build_calfile Failed to open file ".$outputfile." for writing");
  254. return -2;
  255. }
  256. }
  257. /**
  258. * Build a file from an array of events.
  259. * All input data must be encoded in $conf->charset_output
  260. *
  261. * @param string $format 'rss'
  262. * @param string $title Title of export
  263. * @param string $desc Description of export
  264. * @param array $events_array Array of events ('uid','startdate','summary','url','desc','author','category')
  265. * @param string $outputfile Output file
  266. * @param string $filter Filter
  267. * @return int <0 if ko, Nb of events in file if ok
  268. */
  269. function build_rssfile($format,$title,$desc,$events_array,$outputfile,$filter='')
  270. {
  271. global $user,$conf,$langs;
  272. global $dolibarr_main_url_root;
  273. dol_syslog("xcal.lib.php::build_rssfile Build rss file ".$outputfile." to format ".$format);
  274. if (empty($outputfile)) return -1;
  275. $fichier=fopen($outputfile,'w');
  276. if ($fichier)
  277. {
  278. $date=date("r");
  279. // Print header
  280. $form='<?xml version="1.0" encoding="'.$langs->charset_output.'"?>';
  281. fwrite($fichier, $form);
  282. fwrite($fichier, "\n");
  283. $form='<rss version="2.0">';
  284. fwrite($fichier, $form);
  285. fwrite($fichier, "\n");
  286. $form="<channel>\n<title>".$title."</title>\n";
  287. fwrite($fichier, $form);
  288. $form='<description><![CDATA['.$desc.'.]]></description>'."\n".
  289. // '<language>fr</language>'."\n".
  290. '<copyright>Dolibarr</copyright>'."\n".
  291. '<lastBuildDate>'.$date.'</lastBuildDate>'."\n".
  292. '<generator>Dolibarr</generator>'."\n";
  293. // Define $urlwithroot
  294. $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
  295. $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
  296. //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
  297. $url=$urlwithroot.'/public/agenda/agendaexport.php?format=rss&exportkey='.urlencode($conf->global->MAIN_AGENDA_XCAL_EXPORTKEY);
  298. $form.='<link><![CDATA['.$url.']]></link>'."\n";
  299. //print $form;
  300. fwrite($fichier, $form);
  301. foreach ($events_array as $date => $event)
  302. {
  303. $eventqualified=true;
  304. if ($filter)
  305. {
  306. // TODO Add a filter
  307. $eventqualified=false;
  308. }
  309. if ($eventqualified)
  310. {
  311. $uid = $event['uid'];
  312. $startdate = $event['startdate'];
  313. $summary = $event['summary'];
  314. $url = $event['url'];
  315. $author = $event['author'];
  316. $category = $event['category'];
  317. /* No place inside a RSS
  318. $priority = $event['priority'];
  319. $fulldayevent = $event['fulldayevent'];
  320. $location = $event['location'];
  321. $email = $event['email'];
  322. */
  323. $description=preg_replace('/<br[\s\/]?>/i',"\n",$event['desc']);
  324. $description=dol_string_nohtmltag($description,0); // Remove html tags
  325. fwrite($fichier, "<item>\n");
  326. fwrite($fichier, "<title><![CDATA[".$summary."]]></title>\n");
  327. fwrite($fichier, "<link><![CDATA[".$url."]]></link>\n");
  328. fwrite($fichier, "<author><![CDATA[".$author."]]></author>\n");
  329. fwrite($fichier, "<category><![CDATA[".$category."]]></category>\n");
  330. fwrite($fichier, "<description><![CDATA[");
  331. if ($description) fwrite($fichier, $description);
  332. //else fwrite($fichier, 'NoDesc');
  333. fwrite($fichier, "]]></description>\n");
  334. fwrite($fichier, "<pubDate>".date("r", $startdate)."</pubDate>\n");
  335. fwrite($fichier, "<guid isPermaLink=\"true\"><![CDATA[".$uid."]]></guid>\n");
  336. fwrite($fichier, "<source><![CDATA[Dolibarr]]></source>\n");
  337. fwrite($fichier, "</item>\n");
  338. }
  339. }
  340. fwrite($fichier, '</channel>');
  341. fwrite($fichier, "\n");
  342. fwrite($fichier, '</rss>');
  343. fclose($fichier);
  344. if (! empty($conf->global->MAIN_UMASK))
  345. @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
  346. }
  347. }
  348. /**
  349. * Encode for cal export
  350. *
  351. * @param string $format vcal or ical
  352. * @param string $string string to encode
  353. * @return string string encoded
  354. */
  355. function format_cal($format,$string)
  356. {
  357. global $conf;
  358. $newstring=$string;
  359. if ($format == 'vcal')
  360. {
  361. $newstring=quotedPrintEncode($newstring);
  362. }
  363. if ($format == 'ical')
  364. {
  365. // Replace new lines chars by '\n'
  366. $newstring=preg_replace('/'."\r\n".'/i',"\n",$newstring);
  367. $newstring=preg_replace('/'."\n\r".'/i',"\n",$newstring);
  368. $newstring=preg_replace('/'."\n".'/i','\n',$newstring);
  369. // Must not exceed 75 char. Cut with "\r\n"+Space
  370. $newstring=calEncode($newstring);
  371. }
  372. return $newstring;
  373. }
  374. /**
  375. * Cut string after 75 chars. Add CRLF+Space.
  376. * line must be encoded in UTF-8
  377. *
  378. * @param string $line String to convert
  379. * @return string String converted
  380. */
  381. function calEncode($line)
  382. {
  383. $out = '';
  384. $newpara = '';
  385. // If mb_ functions exists, it's better to use them
  386. if (function_exists('mb_strlen'))
  387. {
  388. $strlength=mb_strlen($line, 'UTF-8');
  389. for ($j = 0; $j <= ($strlength - 1); $j++)
  390. {
  391. $char = mb_substr($line, $j, 1, 'UTF-8'); // Take char at position $j
  392. if ((mb_strlen($newpara, 'UTF-8') + mb_strlen($char, 'UTF-8')) >= 75)
  393. {
  394. $out .= $newpara . "\r\n "; // CRLF + Space for cal
  395. $newpara = '';
  396. }
  397. $newpara .= $char;
  398. }
  399. $out .= $newpara;
  400. }
  401. else
  402. {
  403. $strlength=dol_strlen($line);
  404. for ($j = 0; $j <= ($strlength - 1); $j++)
  405. {
  406. $char = substr($line, $j, 1); // Take char at position $j
  407. if ((dol_strlen($newpara) + dol_strlen($char)) >= 75 )
  408. {
  409. $out .= $newpara . "\r\n "; // CRLF + Space for cal
  410. $newpara = '';
  411. }
  412. $newpara .= $char;
  413. }
  414. $out .= $newpara;
  415. }
  416. return trim($out);
  417. }
  418. /**
  419. * Encode into vcal format
  420. *
  421. * @param string $str String to convert
  422. * @param int $forcal 1=For cal
  423. * @return string String converted
  424. */
  425. function quotedPrintEncode($str,$forcal=0)
  426. {
  427. $lines = preg_split("/\r\n/", $str);
  428. $out = '';
  429. foreach ($lines as $line)
  430. {
  431. $newpara = '';
  432. $strlength=strlen($line); // Do not use dol_strlen here, we need number of bytes
  433. for ($j = 0; $j <= ($strlength - 1); $j++)
  434. {
  435. $char = substr($line, $j, 1);
  436. $ascii = ord($char);
  437. if ( $ascii < 32 || $ascii == 61 || $ascii > 126 )
  438. $char = '=' . strtoupper(sprintf("%02X", $ascii));
  439. if ((strlen($newpara) + strlen($char)) >= 76 ) // Do not use dol_strlen here, we need number of bytes
  440. {
  441. $out .= $newpara . '=' . "\r\n"; // CRLF
  442. if ($forcal) $out .= " "; // + Space for cal
  443. $newpara = '';
  444. }
  445. $newpara .= $char;
  446. }
  447. $out .= $newpara;
  448. }
  449. return trim($out);
  450. }
  451. /**
  452. * Decode vcal format
  453. *
  454. * @param string $str String to convert
  455. * @return string String converted
  456. */
  457. function quotedPrintDecode($str)
  458. {
  459. $out = preg_replace('/=\r?\n/', '', $str);
  460. $out = quoted_printable_decode($out); // Available with PHP 4+
  461. return trim($out);
  462. }