dolibarr 19.0.3
xcal.lib.php
Go to the documentation of this file.
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 <https://www.gnu.org/licenses/>.
16 */
17
18
35function build_calfile($format, $title, $desc, $events_array, $outputfile)
36{
37 global $conf, $langs;
38
39 dol_syslog("xcal.lib.php::build_calfile Build cal file ".$outputfile." to format ".$format);
40
41 if (empty($outputfile)) {
42 // -1 = error
43 return -1;
44 }
45
46 $nbevents = 0;
47
48 // Note: A cal file is an UTF8 encoded file
49 $calfileh = fopen($outputfile, "w");
50
51 if ($calfileh) {
52 include_once DOL_DOCUMENT_ROOT."/core/lib/date.lib.php";
53
54 $now = dol_now();
55 $encoding = "";
56
57 if ($format === "vcal") {
58 $encoding = "ENCODING=QUOTED-PRINTABLE:";
59 }
60
61 // Print header
62 fwrite($calfileh, "BEGIN:VCALENDAR\n");
63
64 // version is always "2.0"
65 fwrite($calfileh, "VERSION:2.0\n");
66
67 fwrite($calfileh, "METHOD:PUBLISH\n");
68 fwrite($calfileh, "PRODID:-//DOLIBARR ".DOL_VERSION."\n");
69 fwrite($calfileh, "CALSCALE:GREGORIAN\n");
70 fwrite($calfileh, "X-WR-CALNAME:".$encoding.format_cal($format, $title)."\n");
71 fwrite($calfileh, "X-WR-CALDESC:".$encoding.format_cal($format, $desc)."\n");
72 //fwrite($calfileh,"X-WR-TIMEZONE:Europe/Paris\n");
73
74 if (getDolGlobalString('MAIN_AGENDA_EXPORT_CACHE') && getDolGlobalInt('MAIN_AGENDA_EXPORT_CACHE') > 60) {
75 $hh = convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE, "hour");
76 $mm = convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE, "min");
77 $ss = convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE, "sec");
78
79 fwrite($calfileh, "X-PUBLISHED-TTL: P".$hh."H".$mm."M".$ss."S\n");
80 }
81
82 foreach ($events_array as $key => $event) {
83 // See http://fr.wikipedia.org/wiki/ICalendar for format
84 // See http://www.ietf.org/rfc/rfc2445.txt for RFC
85
86 // TODO: avoid use extra event array, use objects direct thahtwas created before
87
88 $uid = $event["uid"];
89 $type = $event["type"];
90 $startdate = $event["startdate"];
91 $duration = $event["duration"];
92 $enddate = $event["enddate"];
93 $summary = $event["summary"];
94 $category = $event["category"];
95 $priority = $event["priority"];
96 $fulldayevent = $event["fulldayevent"];
97 $location = $event["location"];
98 $email = $event["email"];
99 $url = $event["url"];
100 $transparency = $event["transparency"];
101 $description = dol_string_nohtmltag(preg_replace("/<br[\s\/]?>/i", "\n", $event["desc"]), 0);
102 $created = $event["created"];
103 $modified = $event["modified"];
104 $assignedUsers = $event["assignedUsers"];
105 //print $fulldayevent.' '.dol_print_date($startdate, 'dayhour', 'gmt');
106
107 // Format
108 $summary = format_cal($format, $summary);
109 $description = format_cal($format, $description);
110 $category = format_cal($format, $category);
111 $location = format_cal($format, $location);
112
113 // Output the vCard/iCal VEVENT object
114 /*
115 Example from Google ical export for a 1 hour event:
116 BEGIN:VEVENT
117 DTSTART:20101103T120000Z
118 DTEND:20101103T130000Z
119 DTSTAMP:20101121T144902Z
120 UID:4eilllcsq8r1p87ncg7vc8dbpk@google.com
121 CREATED:20101121T144657Z
122 DESCRIPTION:
123 LAST-MODIFIED:20101121T144707Z
124 LOCATION:
125 SEQUENCE:0
126 STATUS:CONFIRMED
127 SUMMARY:Tâche 1 heure
128 TRANSP:OPAQUE
129 END:VEVENT
130
131 Example from Google ical export for a 1 day event:
132 BEGIN:VEVENT
133 DTSTART;VALUE=DATE:20101102
134 DTEND;VALUE=DATE:20101103
135 DTSTAMP:20101121T144902Z
136 UID:d09t43kcf1qgapu9efsmmo1m6k@google.com
137 CREATED:20101121T144607Z
138 DESCRIPTION:
139 LAST-MODIFIED:20101121T144607Z
140 LOCATION:
141 SEQUENCE:0
142 STATUS:CONFIRMED
143 SUMMARY:Tâche 1 jour
144 TRANSP:TRANSPARENT
145 END:VEVENT
146 */
147
148 if ($type === "event") {
149 $nbevents++;
150
151 fwrite($calfileh, "BEGIN:VEVENT\n");
152 fwrite($calfileh, "UID:".$uid."\n");
153
154 if (!empty($email)) {
155 fwrite($calfileh, "ORGANIZER:MAILTO:".$email."\n");
156 fwrite($calfileh, "CONTACT:MAILTO:".$email."\n");
157 }
158
159 if (!empty($url)) {
160 fwrite($calfileh, "URL:".$url."\n");
161 }
162
163 if (is_array($assignedUsers)) {
164 foreach ($assignedUsers as $assignedUser) {
165 if ($assignedUser->email === $email) {
166 continue;
167 }
168
169 fwrite($calfileh, "ATTENDEE;RSVP=TRUE:mailto:".$assignedUser->email."\n");
170 }
171 }
172
173 if ($created) {
174 fwrite($calfileh, "CREATED:".dol_print_date($created, "dayhourxcard", true)."\n");
175 }
176
177 if ($modified) {
178 fwrite($calfileh, "LAST-MODIFIED:".dol_print_date($modified, "dayhourxcard", true)."\n");
179 }
180
181 fwrite($calfileh, "SUMMARY:".$encoding.$summary."\n");
182 fwrite($calfileh, "DESCRIPTION:".$encoding.$description."\n");
183
184 if (!empty($location)) {
185 fwrite($calfileh, "LOCATION:".$encoding.$location."\n");
186 }
187
188 if ($fulldayevent) {
189 fwrite($calfileh, "X-FUNAMBOL-ALLDAY:1\n");
190 }
191
192 // see https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcical/0f262da6-c5fd-459e-9f18-145eba86b5d2
193 if ($fulldayevent) {
194 fwrite($calfileh, "X-MICROSOFT-CDO-ALLDAYEVENT:TRUE\n");
195 }
196
197 // Date must be GMT dates
198 // Current date
199 fwrite($calfileh, "DTSTAMP:".dol_print_date($now, "dayhourxcard", 'gmt')."\n");
200
201 // Start date
202 $prefix = "";
203 $startdatef = dol_print_date($startdate, "dayhourxcard", 'gmt');
204
205 if ($fulldayevent) {
206 // For fullday event, date was stored with old version by using the user timezone instead of storing the date at UTC+0
207 // in the timezone of server (so for a PHP timezone of -3, we should store '2023-05-31 21:00:00.000'
208 // Using option MAIN_STORE_FULL_EVENT_IN_GMT=1 change the behaviour to store in GMT for full day event. This must become
209 // the default behaviour but there is no way to change keeping old saved date compatible.
210 $tzforfullday = getDolGlobalString('MAIN_STORE_FULL_EVENT_IN_GMT');
211 // Local time should be used to prevent users in time zones earlier than GMT from being one day earlier
212 $prefix = ";VALUE=DATE";
213 if ($tzforfullday) {
214 $startdatef = dol_print_date($startdate, "dayxcard", 'gmt');
215 } else {
216 $startdatef = dol_print_date($startdate, "dayxcard", 'tzserver');
217 }
218 }
219
220 fwrite($calfileh, "DTSTART".$prefix.":".$startdatef."\n");
221
222 // End date
223 if ($fulldayevent) {
224 if (empty($enddate)) {
225 // We add 1 day needed for full day event (DTEND must be next day after event).
226 // This is mention in https://datatracker.ietf.org/doc/html/rfc5545:
227 // "The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event."
228 $enddate = dol_time_plus_duree($startdate, 1, "d");
229 }
230 } else {
231 if (empty($enddate)) {
232 $enddate = $startdate + $duration;
233 }
234 }
235
236 $prefix = "";
237 $enddatef = dol_print_date($enddate, "dayhourxcard", 'gmt');
238
239 if ($fulldayevent) {
240 $prefix = ";VALUE=DATE";
241 // We add 1 second so we reach the +1 day needed for full day event (DTEND must be next day after event)
242 // This is mention in https://datatracker.ietf.org/doc/html/rfc5545:
243 // "The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event."
244 $enddatef = dol_print_date($enddate + 1, "dayxcard", 'tzserver');
245 }
246
247 fwrite($calfileh, "DTEND".$prefix.":".$enddatef."\n");
248 fwrite($calfileh, "STATUS:CONFIRMED\n");
249
250 if (!empty($transparency)) {
251 fwrite($calfileh, "TRANSP:".$transparency."\n");
252 }
253
254 if (!empty($category)) {
255 fwrite($calfileh, "CATEGORIES:".$encoding.$category."\n");
256 }
257
258 fwrite($calfileh, "END:VEVENT\n");
259 }
260
261 // Output the vCard/iCal VJOURNAL object
262 if ($type === "journal") {
263 $nbevents++;
264
265 fwrite($calfileh, "BEGIN:VJOURNAL\n");
266 fwrite($calfileh, "UID:".$uid."\n");
267
268 if (!empty($email)) {
269 fwrite($calfileh, "ORGANIZER:MAILTO:".$email."\n");
270 fwrite($calfileh, "CONTACT:MAILTO:".$email."\n");
271 }
272
273 if (!empty($url)) {
274 fwrite($calfileh, "URL:".$url."\n");
275 }
276
277 if ($created) {
278 fwrite($calfileh, "CREATED:".dol_print_date($created, "dayhourxcard", 'gmt')."\n");
279 }
280
281 if ($modified) {
282 fwrite($calfileh, "LAST-MODIFIED:".dol_print_date($modified, "dayhourxcard", 'gmt')."\n");
283 }
284
285 fwrite($calfileh, "SUMMARY:".$encoding.$summary."\n");
286 fwrite($calfileh, "DESCRIPTION:".$encoding.$description."\n");
287 fwrite($calfileh, "STATUS:CONFIRMED\n");
288 fwrite($calfileh, "CATEGORIES:".$category."\n");
289 fwrite($calfileh, "LOCATION:".$location."\n");
290 fwrite($calfileh, "TRANSP:OPAQUE\n");
291 fwrite($calfileh, "CLASS:CONFIDENTIAL\n");
292 fwrite($calfileh, "DTSTAMP:".dol_print_date($startdatef, "dayhourxcard", 'gmt')."\n");
293
294 fwrite($calfileh, "END:VJOURNAL\n");
295 }
296 }
297
298 // Footer
299 fwrite($calfileh, "END:VCALENDAR");
300
301 fclose($calfileh);
302 dolChmod($outputfile);
303 } else {
304 dol_syslog("xcal.lib.php::build_calfile Failed to open file ".$outputfile." for writing");
305 return -2;
306 }
307
308 return $nbevents;
309}
310
325function build_rssfile($format, $title, $desc, $events_array, $outputfile, $filter = '', $url = '', $langcode = '')
326{
327 global $user, $conf, $langs, $mysoc;
328 global $dolibarr_main_url_root;
329
330 dol_syslog("xcal.lib.php::build_rssfile Build rss file ".$outputfile." to format ".$format);
331
332 if (empty($outputfile)) {
333 // -1 = error
334 return -1;
335 }
336
337 $nbevents = 0;
338
339 $fichier = fopen($outputfile, "w");
340
341 if ($fichier) {
342 // Print header
343 fwrite($fichier, '<?xml version="1.0" encoding="'.$langs->charset_output.'"?>');
344 fwrite($fichier, "\n");
345
346 fwrite($fichier, '<rss version="2.0">');
347 fwrite($fichier, "\n");
348
349 fwrite($fichier, "<channel>\n");
350 fwrite($fichier, "<title>".dol_escape_xml($title)."</title>\n");
351 if ($langcode) {
352 fwrite($fichier, "<language>".dol_escape_xml($langcode)."</language>\n");
353 }
354
355 // Define $urlwithroot
356 $urlwithouturlroot = preg_replace("/".preg_quote(DOL_URL_ROOT, "/")."$/i", "", trim($dolibarr_main_url_root));
357 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
358 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
359
360 // Url
361 if (empty($url)) {
362 $url = $urlwithroot."/public/agenda/agendaexport.php?format=rss&exportkey=".urlencode($conf->global->MAIN_AGENDA_XCAL_EXPORTKEY);
363 }
364 fwrite($fichier, "<link><![CDATA[".$url."]]></link>\n");
365
366 // Image
367 if (!empty($mysoc->logo_squarred_small)) {
368 $urlimage = $urlwithroot.'/viewimage.php?cache=1&amp;modulepart=mycompany&amp;file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_small);
369 if ($urlimage) {
370 fwrite($fichier, "<image><url><![CDATA[".$urlimage."]]></url><title>'.$title.</title></image>\n");
371 }
372 }
373
374 foreach ($events_array as $key => $event) {
375 $eventqualified = true;
376
377 if ($filter) {
378 // TODO Add a filter
379
380 $eventqualified = false;
381 }
382
383 if ($eventqualified) {
384 $nbevents++;
385
386 if (is_object($event) && get_class($event) == 'WebsitePage') {
387 // Convert object into an array
388 $tmpevent = array();
389 $tmpevent['uid'] = $event->id;
390 $tmpevent['startdate'] = $event->date_creation;
391 $tmpevent['summary'] = $event->title;
392 $tmpevent['url'] = $event->fullpageurl ? $event->fullpageurl : $event->pageurl.'.php';
393 $tmpevent['author'] = $event->author_alias ? $event->author_alias : 'unknown';
394 //$tmpevent['category'] = '';
395 $tmpevent['desc'] = $event->description;
396 $tmpevent['image'] = $GLOBALS['website']->virtualhost.'/medias/'.$event->image;
397 $event = $tmpevent;
398 }
399
400 $uid = $event["uid"];
401 $startdate = $event["startdate"];
402 $summary = $event["summary"];
403 $url = $event["url"];
404 $author = $event["author"];
405 $category = empty($event["category"]) ? null : $event["category"];
406 if (!empty($event["image"])) {
407 $image = $event["image"];
408 }
409 /* No place inside a RSS
410 $priority = $event["priority"];
411 $fulldayevent = $event["fulldayevent"];
412 $location = $event["location"];
413 $email = $event["email"];
414 */
415
416 $description = dol_string_nohtmltag(preg_replace("/<br[\s\/]?>/i", "\n", $event["desc"]), 0);
417
418 fwrite($fichier, "<item>\n");
419 fwrite($fichier, "<title><![CDATA[".$summary."]]></title>\n");
420 fwrite($fichier, "<link><![CDATA[".$url."]]></link>\n");
421 fwrite($fichier, "<author><![CDATA[".$author."]]></author>\n");
422 if (!empty($category)) {
423 fwrite($fichier, "<category><![CDATA[".$category."]]></category>\n");
424 }
425 fwrite($fichier, "<description><![CDATA[");
426 if (!empty($image)) {
427 fwrite($fichier, '<p><img class="center" src="'.$image.'"/></p>');
428 }
429
430 if ($description) {
431 fwrite($fichier, $description);
432 }
433 // else
434 // fwrite($fichier, "NoDesc");
435
436 fwrite($fichier, "]]></description>\n");
437 fwrite($fichier, "<pubDate>".date("r", $startdate)."</pubDate>\n");
438 fwrite($fichier, "<guid isPermaLink=\"true\"><![CDATA[".$uid."]]></guid>\n");
439 fwrite($fichier, "<source><![CDATA[Dolibarr]]></source>\n");
440 fwrite($fichier, "</item>\n");
441 }
442 }
443
444 fwrite($fichier, "</channel>");
445 fwrite($fichier, "\n");
446 fwrite($fichier, "</rss>");
447
448 fclose($fichier);
449 dolChmod($outputfile);
450 }
451
452 return $nbevents;
453}
454
462function format_cal($format, $string)
463{
464 $newstring = $string;
465
466 if ($format === "vcal") {
467 $newstring = quotedPrintEncode($newstring);
468 }
469
470 if ($format === "ical") {
471 // Replace new lines chars by "\n"
472 $newstring = preg_replace("/\r\n/i", "\\n", $newstring);
473 $newstring = preg_replace("/\n\r/i", "\\n", $newstring);
474 $newstring = preg_replace("/\n/i", "\\n", $newstring);
475
476 // Must not exceed 75 char. Cut with "\r\n"+Space
477 $newstring = calEncode($newstring);
478 }
479
480 return $newstring;
481}
482
490function calEncode($line)
491{
492 $out = "";
493 $newpara = "";
494
495 // If mb_ functions exists, it"s better to use them
496 if (function_exists("mb_strlen")) {
497 $strlength = mb_strlen($line, "UTF-8");
498
499 for ($j = 0; $j < $strlength; $j++) {
500 // Take char at position $j
501 $char = dol_substr($line, $j, 1, "UTF-8");
502
503 if ((mb_strlen($newpara, "UTF-8") + mb_strlen($char, "UTF-8")) >= 75) {
504 // CRLF + Space for cal
505 $out .= $newpara."\r\n ";
506
507 $newpara = "";
508 }
509
510 $newpara .= $char;
511 }
512
513 $out .= $newpara;
514 } else {
515 $strlength = dol_strlen($line);
516
517 for ($j = 0; $j < $strlength; $j++) {
518 // Take char at position $j
519 $char = substr($line, $j, 1);
520
521 if ((dol_strlen($newpara) + dol_strlen($char)) >= 75) {
522 // CRLF + Space for cal
523 $out .= $newpara."\r\n ";
524
525 $newpara = "";
526 }
527
528 $newpara .= $char;
529 }
530
531 $out .= $newpara;
532 }
533
534 return trim($out);
535}
536
537
545function quotedPrintEncode($str, $forcal = 0)
546{
547 $lines = preg_split("/\r\n/", $str);
548 $out = "";
549
550 foreach ($lines as $line) {
551 $newpara = "";
552
553 // Do not use dol_strlen here, we need number of bytes
554 $strlength = strlen($line);
555
556 for ($j = 0; $j < $strlength; $j++) {
557 $char = substr($line, $j, 1);
558 $ascii = ord($char);
559
560 if ($ascii < 32 || $ascii === 61 || $ascii > 126) {
561 $char = "=".strtoupper(sprintf("%02X", $ascii));
562 }
563
564 // Do not use dol_strlen here, we need number of bytes
565 if ((strlen($newpara) + strlen($char)) >= 76) {
566 // New line with carray-return (CR) and line-feed (LF)
567 $out .= $newpara."=\r\n";
568
569 // extra space for cal
570 if ($forcal) {
571 $out .= " ";
572 }
573
574 $newpara = "";
575 }
576
577 $newpara .= $char;
578 }
579
580 $out .= $newpara;
581 }
582 return trim($out);
583}
584
591function quotedPrintDecode($str)
592{
593 return trim(quoted_printable_decode(preg_replace("/=\r?\n/", "", $str)));
594}
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:125
convertSecondToTime($iSecond, $format='all', $lengthOfDay=86400, $lengthOfWeek=7)
Return, in clear text, value of a number of seconds in days, hours and minutes.
Definition date.lib.php:243
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
dol_escape_xml($stringtoescape)
Returns text escaped for inclusion into a XML string.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
quotedPrintEncode($str, $forcal=0)
Encode into vcal format.
Definition xcal.lib.php:545
build_calfile($format, $title, $desc, $events_array, $outputfile)
Build a file from an array of events All input params and data must be encoded in $conf->charset_outp...
Definition xcal.lib.php:35
format_cal($format, $string)
Encode for cal export.
Definition xcal.lib.php:462
build_rssfile($format, $title, $desc, $events_array, $outputfile, $filter='', $url='', $langcode='')
Build a file from an array of events.
Definition xcal.lib.php:325
calEncode($line)
Cut string after 75 chars.
Definition xcal.lib.php:490
quotedPrintDecode($str)
Decode vcal format.
Definition xcal.lib.php:591