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