123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- <?php
- /* Copyright (C) 2008-2011 Laurent Destailleur <eldy@users.sourceforge.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- /**
- * \file htdocs/core/lib/xcal.lib.php
- * \brief Function to manage calendar files (vcal/ical/...)
- */
- /**
- * Build a file from an array of events
- * All input params and data must be encoded in $conf->charset_output
- *
- * @param string $format "vcal" or "ical"
- * @param string $title Title of export
- * @param string $desc Description of export
- * @param array $events_array Array of events ("uid","startdate","duration","enddate","title","summary","category","email","url","desc","author")
- * @param string $outputfile Output file
- * @return int < 0 if ko, Nb of events in file if ok
- */
- function build_calfile($format, $title, $desc, $events_array, $outputfile)
- {
- global $conf, $langs;
- dol_syslog("xcal.lib.php::build_calfile Build cal file ".$outputfile." to format ".$format);
- if (empty($outputfile)) {
- // -1 = error
- return -1;
- }
- // Note: A cal file is an UTF8 encoded file
- $calfileh = fopen($outputfile, "w");
- if ($calfileh) {
- include_once DOL_DOCUMENT_ROOT."/core/lib/date.lib.php";
- $now = dol_now();
- $encoding = "";
- if ($format === "vcal") {
- $encoding = "ENCODING=QUOTED-PRINTABLE:";
- }
- // Print header
- fwrite($calfileh, "BEGIN:VCALENDAR\n");
- // version is always "2.0"
- fwrite($calfileh, "VERSION:2.0\n");
- fwrite($calfileh, "METHOD:PUBLISH\n");
- fwrite($calfileh, "PRODID:-//DOLIBARR ".DOL_VERSION."\n");
- fwrite($calfileh, "CALSCALE:GREGORIAN\n");
- fwrite($calfileh, "X-WR-CALNAME:".$encoding.format_cal($format, $title)."\n");
- fwrite($calfileh, "X-WR-CALDESC:".$encoding.format_cal($format, $desc)."\n");
- //fwrite($calfileh,"X-WR-TIMEZONE:Europe/Paris\n");
- if (!empty($conf->global->MAIN_AGENDA_EXPORT_CACHE) && $conf->global->MAIN_AGENDA_EXPORT_CACHE > 60) {
- $hh = convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE, "hour");
- $mm = convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE, "min");
- $ss = convertSecondToTime($conf->global->MAIN_AGENDA_EXPORT_CACHE, "sec");
- fwrite($calfileh, "X-PUBLISHED-TTL: P".$hh."H".$mm."M".$ss."S\n");
- }
- foreach ($events_array as $key => $event) {
- // See http://fr.wikipedia.org/wiki/ICalendar for format
- // See http://www.ietf.org/rfc/rfc2445.txt for RFC
- // TODO: avoid use extra event array, use objects direct thahtwas created before
- $uid = $event["uid"];
- $type = $event["type"];
- $startdate = $event["startdate"];
- $duration = $event["duration"];
- $enddate = $event["enddate"];
- $summary = $event["summary"];
- $category = $event["category"];
- $priority = $event["priority"];
- $fulldayevent = $event["fulldayevent"];
- $location = $event["location"];
- $email = $event["email"];
- $url = $event["url"];
- $transparency = $event["transparency"];
- $description = dol_string_nohtmltag(preg_replace("/<br[\s\/]?>/i", "\n", $event["desc"]), 0);
- $created = $event["created"];
- $modified = $event["modified"];
- $assignedUsers = $event["assignedUsers"];
- //print $fulldayevent.' '.dol_print_date($startdate, 'dayhour', 'gmt');
- // Format
- $summary = format_cal($format, $summary);
- $description = format_cal($format, $description);
- $category = format_cal($format, $category);
- $location = format_cal($format, $location);
- // Output the vCard/iCal VEVENT object
- /*
- Example from Google ical export for a 1 hour event:
- BEGIN:VEVENT
- DTSTART:20101103T120000Z
- DTEND:20101103T130000Z
- DTSTAMP:20101121T144902Z
- UID:4eilllcsq8r1p87ncg7vc8dbpk@google.com
- CREATED:20101121T144657Z
- DESCRIPTION:
- LAST-MODIFIED:20101121T144707Z
- LOCATION:
- SEQUENCE:0
- STATUS:CONFIRMED
- SUMMARY:Tâche 1 heure
- TRANSP:OPAQUE
- END:VEVENT
- Example from Google ical export for a 1 day event:
- BEGIN:VEVENT
- DTSTART;VALUE=DATE:20101102
- DTEND;VALUE=DATE:20101103
- DTSTAMP:20101121T144902Z
- UID:d09t43kcf1qgapu9efsmmo1m6k@google.com
- CREATED:20101121T144607Z
- DESCRIPTION:
- LAST-MODIFIED:20101121T144607Z
- LOCATION:
- SEQUENCE:0
- STATUS:CONFIRMED
- SUMMARY:Tâche 1 jour
- TRANSP:TRANSPARENT
- END:VEVENT
- */
- if ($type === "event") {
- fwrite($calfileh, "BEGIN:VEVENT\n");
- fwrite($calfileh, "UID:".$uid."\n");
- if (!empty($email)) {
- fwrite($calfileh, "ORGANIZER:MAILTO:".$email."\n");
- fwrite($calfileh, "CONTACT:MAILTO:".$email."\n");
- }
- if (!empty($url)) {
- fwrite($calfileh, "URL:".$url."\n");
- }
- if (is_array($assignedUsers)) {
- foreach ($assignedUsers as $assignedUser) {
- if ($assignedUser->email === $email) {
- continue;
- }
- fwrite($calfileh, "ATTENDEE;RSVP=TRUE:mailto:".$assignedUser->email."\n");
- }
- }
- if ($created) {
- fwrite($calfileh, "CREATED:".dol_print_date($created, "dayhourxcard", true)."\n");
- }
- if ($modified) {
- fwrite($calfileh, "LAST-MODIFIED:".dol_print_date($modified, "dayhourxcard", true)."\n");
- }
- fwrite($calfileh, "SUMMARY:".$encoding.$summary."\n");
- fwrite($calfileh, "DESCRIPTION:".$encoding.$description."\n");
- if (!empty($location)) {
- fwrite($calfileh, "LOCATION:".$encoding.$location."\n");
- }
- if ($fulldayevent) {
- fwrite($calfileh, "X-FUNAMBOL-ALLDAY:1\n");
- }
- // see https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcical/0f262da6-c5fd-459e-9f18-145eba86b5d2
- if ($fulldayevent) {
- fwrite($calfileh, "X-MICROSOFT-CDO-ALLDAYEVENT:TRUE\n");
- }
- // Date must be GMT dates
- // Current date
- fwrite($calfileh, "DTSTAMP:".dol_print_date($now, "dayhourxcard", 'gmt')."\n");
- // Start date
- $prefix = "";
- $startdatef = dol_print_date($startdate, "dayhourxcard", 'gmt');
- if ($fulldayevent) {
- // Local time
- $prefix = ";VALUE=DATE";
- $startdatef = dol_print_date($startdate, "dayxcard", 'gmt');
- }
- fwrite($calfileh, "DTSTART".$prefix.":".$startdatef."\n");
- // End date
- if ($fulldayevent) {
- if (empty($enddate)) {
- // We add 1 day needed for full day event (DTEND must be next day after event).
- // This is mention in https://datatracker.ietf.org/doc/html/rfc5545:
- // "The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event."
- $enddate = dol_time_plus_duree($startdate, 1, "d");
- }
- } else {
- if (empty($enddate)) {
- $enddate = $startdate + $duration;
- }
- }
- $prefix = "";
- $enddatef = dol_print_date($enddate, "dayhourxcard", 'gmt');
- if ($fulldayevent) {
- $prefix = ";VALUE=DATE";
- // We add 1 second so we reach the +1 day needed for full day event (DTEND must be next day after event)
- // This is mention in https://datatracker.ietf.org/doc/html/rfc5545:
- // "The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event."
- $enddatef = dol_print_date($enddate + 1, "dayxcard", 'gmt');
- }
- fwrite($calfileh, "DTEND".$prefix.":".$enddatef."\n");
- fwrite($calfileh, "STATUS:CONFIRMED\n");
- if (!empty($transparency)) {
- fwrite($calfileh, "TRANSP:".$transparency."\n");
- }
- if (!empty($category)) {
- fwrite($calfileh, "CATEGORIES:".$encoding.$category."\n");
- }
- fwrite($calfileh, "END:VEVENT\n");
- }
- // Output the vCard/iCal VJOURNAL object
- if ($type === "journal") {
- fwrite($calfileh, "BEGIN:VJOURNAL\n");
- fwrite($calfileh, "UID:".$uid."\n");
- if (!empty($email)) {
- fwrite($calfileh, "ORGANIZER:MAILTO:".$email."\n");
- fwrite($calfileh, "CONTACT:MAILTO:".$email."\n");
- }
- if (!empty($url)) {
- fwrite($calfileh, "URL:".$url."\n");
- }
- if ($created) {
- fwrite($calfileh, "CREATED:".dol_print_date($created, "dayhourxcard", 'gmt')."\n");
- }
- if ($modified) {
- fwrite($calfileh, "LAST-MODIFIED:".dol_print_date($modified, "dayhourxcard", 'gmt')."\n");
- }
- fwrite($calfileh, "SUMMARY:".$encoding.$summary."\n");
- fwrite($calfileh, "DESCRIPTION:".$encoding.$description."\n");
- fwrite($calfileh, "STATUS:CONFIRMED\n");
- fwrite($calfileh, "CATEGORIES:".$category."\n");
- fwrite($calfileh, "LOCATION:".$location."\n");
- fwrite($calfileh, "TRANSP:OPAQUE\n");
- fwrite($calfileh, "CLASS:CONFIDENTIAL\n");
- fwrite($calfileh, "DTSTAMP:".dol_print_date($startdatef, "dayhourxcard", 'gmt')."\n");
- fwrite($calfileh, "END:VJOURNAL\n");
- }
- }
- // Footer
- fwrite($calfileh, "END:VCALENDAR");
- fclose($calfileh);
- dolChmod($outputfile);
- } else {
- dol_syslog("xcal.lib.php::build_calfile Failed to open file ".$outputfile." for writing");
- return -2;
- }
- }
- /**
- * Build a file from an array of events.
- * All input data must be encoded in $conf->charset_output
- *
- * @param string $format "rss"
- * @param string $title Title of export
- * @param string $desc Description of export
- * @param array $events_array Array of events ("uid","startdate","summary","url","desc","author","category","image") or Array of WebsitePage
- * @param string $outputfile Output file
- * @param string $filter (optional) Filter
- * @param string $url Url (If empty, forge URL for agenda RSS export)
- * @param string $langcode Language code to show in header
- * @return int < 0 if ko, Nb of events in file if ok
- */
- function build_rssfile($format, $title, $desc, $events_array, $outputfile, $filter = '', $url = '', $langcode = '')
- {
- global $user, $conf, $langs, $mysoc;
- global $dolibarr_main_url_root;
- dol_syslog("xcal.lib.php::build_rssfile Build rss file ".$outputfile." to format ".$format);
- if (empty($outputfile)) {
- // -1 = error
- return -1;
- }
- $fichier = fopen($outputfile, "w");
- if ($fichier) {
- // Print header
- fwrite($fichier, '<?xml version="1.0" encoding="'.$langs->charset_output.'"?>');
- fwrite($fichier, "\n");
- fwrite($fichier, '<rss version="2.0">');
- fwrite($fichier, "\n");
- fwrite($fichier, "<channel>\n");
- fwrite($fichier, "<title>".$title."</title>\n");
- if ($langcode) {
- fwrite($fichier, "<language>".$langcode."</language>\n");
- }
- // Define $urlwithroot
- $urlwithouturlroot = preg_replace("/".preg_quote(DOL_URL_ROOT, "/")."$/i", "", trim($dolibarr_main_url_root));
- $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
- //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
- // Url
- if (empty($url)) {
- $url = $urlwithroot."/public/agenda/agendaexport.php?format=rss&exportkey=".urlencode($conf->global->MAIN_AGENDA_XCAL_EXPORTKEY);
- }
- fwrite($fichier, "<link><![CDATA[".$url."]]></link>\n");
- // Image
- if (!empty($mysoc->logo_squarred_small)) {
- $urlimage = $urlwithroot.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode($mysoc->logo_squarred_small);
- if ($urlimage) {
- fwrite($fichier, "<image><url><![CDATA[".$urlimage."]]></url><title>'.$title.</title></image>\n");
- }
- }
- foreach ($events_array as $key => $event) {
- $eventqualified = true;
- if ($filter) {
- // TODO Add a filter
- $eventqualified = false;
- }
- if ($eventqualified) {
- if (is_object($event) && get_class($event) == 'WebsitePage') {
- // Convert object into an array
- $tmpevent = array();
- $tmpevent['uid'] = $event->id;
- $tmpevent['startdate'] = $event->date_creation;
- $tmpevent['summary'] = $event->title;
- $tmpevent['url'] = $event->fullpageurl ? $event->fullpageurl : $event->pageurl.'.php';
- $tmpevent['author'] = $event->author_alias ? $event->author_alias : 'unknown';
- //$tmpevent['category'] = '';
- $tmpevent['desc'] = $event->description;
- $tmpevent['image'] = $GLOBALS['website']->virtualhost.'/medias/'.$event->image;
- $event = $tmpevent;
- }
- $uid = $event["uid"];
- $startdate = $event["startdate"];
- $summary = $event["summary"];
- $url = $event["url"];
- $author = $event["author"];
- $category = $event["category"];
- if (!empty($event["image"])) {
- $image = $event["image"];
- }
- /* No place inside a RSS
- $priority = $event["priority"];
- $fulldayevent = $event["fulldayevent"];
- $location = $event["location"];
- $email = $event["email"];
- */
- $description = dol_string_nohtmltag(preg_replace("/<br[\s\/]?>/i", "\n", $event["desc"]), 0);
- fwrite($fichier, "<item>\n");
- fwrite($fichier, "<title><![CDATA[".$summary."]]></title>\n");
- fwrite($fichier, "<link><![CDATA[".$url."]]></link>\n");
- fwrite($fichier, "<author><![CDATA[".$author."]]></author>\n");
- fwrite($fichier, "<category><![CDATA[".$category."]]></category>\n");
- fwrite($fichier, "<description><![CDATA[");
- if (!empty($image)) {
- fwrite($fichier, '<p><img class="center" src="'.$image.'"/></p>');
- }
- if ($description) {
- fwrite($fichier, $description);
- }
- // else
- // fwrite($fichier, "NoDesc");
- fwrite($fichier, "]]></description>\n");
- fwrite($fichier, "<pubDate>".date("r", $startdate)."</pubDate>\n");
- fwrite($fichier, "<guid isPermaLink=\"true\"><![CDATA[".$uid."]]></guid>\n");
- fwrite($fichier, "<source><![CDATA[Dolibarr]]></source>\n");
- fwrite($fichier, "</item>\n");
- }
- }
- fwrite($fichier, "</channel>");
- fwrite($fichier, "\n");
- fwrite($fichier, "</rss>");
- fclose($fichier);
- dolChmod($outputfile);
- }
- }
- /**
- * Encode for cal export
- *
- * @param string $format "vcal" or "ical"
- * @param string $string String to encode
- * @return string String encoded
- */
- function format_cal($format, $string)
- {
- global $conf;
- $newstring = $string;
- if ($format === "vcal") {
- $newstring = quotedPrintEncode($newstring);
- }
- if ($format === "ical") {
- // Replace new lines chars by "\n"
- $newstring = preg_replace("/\r\n/i", "\\n", $newstring);
- $newstring = preg_replace("/\n\r/i", "\\n", $newstring);
- $newstring = preg_replace("/\n/i", "\\n", $newstring);
- // Must not exceed 75 char. Cut with "\r\n"+Space
- $newstring = calEncode($newstring);
- }
- return $newstring;
- }
- /**
- * Cut string after 75 chars. Add CRLF+Space.
- * line must be encoded in UTF-8
- *
- * @param string $line String to convert
- * @return string String converted
- */
- function calEncode($line)
- {
- $out = "";
- $newpara = "";
- // If mb_ functions exists, it"s better to use them
- if (function_exists("mb_strlen")) {
- $strlength = mb_strlen($line, "UTF-8");
- for ($j = 0; $j < $strlength; $j++) {
- // Take char at position $j
- $char = mb_substr($line, $j, 1, "UTF-8");
- if ((mb_strlen($newpara, "UTF-8") + mb_strlen($char, "UTF-8")) >= 75) {
- // CRLF + Space for cal
- $out .= $newpara."\r\n ";
- $newpara = "";
- }
- $newpara .= $char;
- }
- $out .= $newpara;
- } else {
- $strlength = dol_strlen($line);
- for ($j = 0; $j < $strlength; $j++) {
- // Take char at position $j
- $char = substr($line, $j, 1);
- if ((dol_strlen($newpara) + dol_strlen($char)) >= 75) {
- // CRLF + Space for cal
- $out .= $newpara."\r\n ";
- $newpara = "";
- }
- $newpara .= $char;
- }
- $out .= $newpara;
- }
- return trim($out);
- }
- /**
- * Encode into vcal format
- *
- * @param string $str String to convert
- * @param int $forcal (optional) 1 = For cal
- * @return string String converted
- */
- function quotedPrintEncode($str, $forcal = 0)
- {
- $lines = preg_split("/\r\n/", $str);
- $out = "";
- foreach ($lines as $line) {
- $newpara = "";
- // Do not use dol_strlen here, we need number of bytes
- $strlength = strlen($line);
- for ($j = 0; $j < $strlength; $j++) {
- $char = substr($line, $j, 1);
- $ascii = ord($char);
- if ($ascii < 32 || $ascii === 61 || $ascii > 126) {
- $char = "=".strtoupper(sprintf("%02X", $ascii));
- }
- // Do not use dol_strlen here, we need number of bytes
- if ((strlen($newpara) + strlen($char)) >= 76) {
- // New line with carray-return (CR) and line-feed (LF)
- $out .= $newpara."=\r\n";
- // extra space for cal
- if ($forcal) {
- $out .= " ";
- }
- $newpara = "";
- }
- $newpara .= $char;
- }
- $out .= $newpara;
- }
- return trim($out);
- }
- /**
- * Decode vcal format
- *
- * @param string $str String to convert
- * @return string String converted
- */
- function quotedPrintDecode($str)
- {
- return trim(quoted_printable_decode(preg_replace("/=\r?\n/", "", $str)));
- }
|