*
* 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 .
*/
/**
* \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 ('eid','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)) 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");
fwrite($calfileh,"VERSION:2.0\n");
fwrite($calfileh,"METHOD:PUBLISH\n");
//fwrite($calfileh,"PRODID:-//DOLIBARR ".DOL_VERSION."//EN\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 $date => $event)
{
$eventqualified=true;
if ($eventqualified)
{
// See http://fr.wikipedia.org/wiki/ICalendar for format
// See http://www.ietf.org/rfc/rfc2445.txt for RFC
$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']; // OPAQUE (busy) or TRANSPARENT (not busy)
$description=preg_replace('/ /i',"\n",$event['desc']);
$description=dol_string_nohtmltag($description,0); // Remove html tags
$created = $event['created'];
$modified = $event['modified'];
// Uncomment for tests
//$summary="Resume";
//$description="Description";
//$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";
// 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 ($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");
/* Other keys:
// Status values for a "VEVENT"
statvalue = "TENTATIVE" ;Indicates event is
;tentative.
/ "CONFIRMED" ;Indicates event is
;definite.
/ "CANCELLED" ;Indicates event was
// Status values for "VTODO".
statvalue =/ "NEEDS-ACTION" ;Indicates to-do needs action.
/ "COMPLETED" ;Indicates to-do completed.
/ "IN-PROCESS" ;Indicates to-do in process of
/ "CANCELLED" ;Indicates to-do was cancelled.
// Status values for "VJOURNAL".
statvalue =/ "DRAFT" ;Indicates journal is draft.
/ "FINAL" ;Indicates journal is final.
/ "CANCELLED" ;Indicates journal is removed.
*/
//fwrite($calfileh,"CLASS:PUBLIC\n"); // PUBLIC, PRIVATE, CONFIDENTIAL
//fwrite($calfileh,"X-MICROSOFT-CDO-BUSYSTATUS:1\n");
//ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Laurent Destailleur;X-NUM-GUESTS=0:mailto:eldy10@gmail.com
if (! empty($location)) fwrite($calfileh,"LOCATION:".$encoding.$location."\n");
if ($fulldayevent) fwrite($calfileh,"X-FUNAMBOL-ALLDAY:1\n");
if ($fulldayevent) fwrite($calfileh,"X-MICROSOFT-CDO-ALLDAYEVENT:1\n");
// Date must be GMT dates
// Current date
fwrite($calfileh,"DTSTAMP:".dol_print_date($now,'dayhourxcard',true)."\n");
// Start date
$prefix='';
$startdatef = dol_print_date($startdate,'dayhourxcard',true);
if ($fulldayevent)
{
$prefix=';VALUE=DATE';
$startdatef = dol_print_date($startdate,'dayxcard',false); // Local time
}
fwrite($calfileh,"DTSTART".$prefix.":".$startdatef."\n");
// End date
if ($fulldayevent)
{
if (empty($enddate)) $enddate=dol_time_plus_duree($startdate,1,'d');
}
else
{
if (empty($enddate)) $enddate=$startdate+$duration;
}
$prefix='';
$enddatef = dol_print_date($enddate,'dayhourxcard',true);
if ($fulldayevent)
{
$prefix=';VALUE=DATE';
$enddatef = dol_print_date($enddate+1,'dayxcard',false);
//$enddatef .= dol_print_date($enddate+1,'dayhourxcard',false); // Local time
}
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 VTODO object
// ...
//PERCENT-COMPLETE:39
// 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',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");
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',true)."\n");
fwrite($calfileh,"END:VJOURNAL\n");
}
// Put other info in comment
/*
$comment=array();
$comment ['eid'] = $eid;
$comment ['url'] = $linktoevent;
$comment ['date'] = dol_mktime($evttime,"Ymd");
$comment ['duration'] = $duration;
$comment ['startdate'] = $startdate;
$comment ['enddate'] = $enddate;
fwrite($calfileh,"COMMENT:" . serialize ($comment) . "\n");
*/
}
}
// Footer
fwrite($calfileh,"END:VCALENDAR");
fclose($calfileh);
if (! empty($conf->global->MAIN_UMASK))
@chmod($outputfile, octdec($conf->global->MAIN_UMASK));
}
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')
* @param string $outputfile Output file
* @param string $filter Filter
* @return int <0 if ko, Nb of events in file if ok
*/
function build_rssfile($format,$title,$desc,$events_array,$outputfile,$filter='')
{
global $user,$conf,$langs;
global $dolibarr_main_url_root;
dol_syslog("xcal.lib.php::build_rssfile Build rss file ".$outputfile." to format ".$format);
if (empty($outputfile)) return -1;
$fichier=fopen($outputfile,'w');
if ($fichier)
{
$date=date("r");
// Print header
$form='charset_output.'"?>';
fwrite($fichier, $form);
fwrite($fichier, "\n");
$form='';
fwrite($fichier, $form);
fwrite($fichier, "\n");
$form="\n".$title."\n";
fwrite($fichier, $form);
$form=''."\n".
// 'fr'."\n".
'Dolibarr'."\n".
''.$date.''."\n".
'Dolibarr'."\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=$urlwithroot.'/public/agenda/agendaexport.php?format=rss&exportkey='.urlencode($conf->global->MAIN_AGENDA_XCAL_EXPORTKEY);
$form.=''."\n";
//print $form;
fwrite($fichier, $form);
foreach ($events_array as $date => $event)
{
$eventqualified=true;
if ($filter)
{
// TODO Add a filter
$eventqualified=false;
}
if ($eventqualified)
{
$uid = $event['uid'];
$startdate = $event['startdate'];
$summary = $event['summary'];
$url = $event['url'];
$author = $event['author'];
$category = $event['category'];
/* No place inside a RSS
$priority = $event['priority'];
$fulldayevent = $event['fulldayevent'];
$location = $event['location'];
$email = $event['email'];
*/
$description=preg_replace('/ /i',"\n",$event['desc']);
$description=dol_string_nohtmltag($description,0); // Remove html tags
fwrite($fichier, "\n");
fwrite($fichier, "\n");
fwrite($fichier, "\n");
fwrite($fichier, "\n");
fwrite($fichier, "\n");
fwrite($fichier, "\n");
fwrite($fichier, "".date("r", $startdate)."\n");
fwrite($fichier, "\n");
fwrite($fichier, "\n");
fwrite($fichier, "\n");
}
}
fwrite($fichier, '');
fwrite($fichier, "\n");
fwrite($fichier, '');
fclose($fichier);
if (! empty($conf->global->MAIN_UMASK))
@chmod($outputfile, octdec($conf->global->MAIN_UMASK));
}
}
/**
* 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 - 1); $j++)
{
$char = mb_substr($line, $j, 1, 'UTF-8'); // Take char at position $j
if ((mb_strlen($newpara, 'UTF-8') + mb_strlen($char, 'UTF-8')) >= 75)
{
$out .= $newpara . "\r\n "; // CRLF + Space for cal
$newpara = '';
}
$newpara .= $char;
}
$out .= $newpara;
}
else
{
$strlength=dol_strlen($line);
for ($j = 0; $j <= ($strlength - 1); $j++)
{
$char = substr($line, $j, 1); // Take char at position $j
if ((dol_strlen($newpara) + dol_strlen($char)) >= 75 )
{
$out .= $newpara . "\r\n "; // CRLF + Space for cal
$newpara = '';
}
$newpara .= $char;
}
$out .= $newpara;
}
return trim($out);
}
/**
* Encode into vcal format
*
* @param string $str String to convert
* @param int $forcal 1=For cal
* @return string String converted
*/
function quotedPrintEncode($str,$forcal=0)
{
$lines = preg_split("/\r\n/", $str);
$out = '';
foreach ($lines as $line)
{
$newpara = '';
$strlength=strlen($line); // Do not use dol_strlen here, we need number of bytes
for ($j = 0; $j <= ($strlength - 1); $j++)
{
$char = substr($line, $j, 1);
$ascii = ord($char);
if ( $ascii < 32 || $ascii == 61 || $ascii > 126 )
$char = '=' . strtoupper(sprintf("%02X", $ascii));
if ((strlen($newpara) + strlen($char)) >= 76 ) // Do not use dol_strlen here, we need number of bytes
{
$out .= $newpara . '=' . "\r\n"; // CRLF
if ($forcal) $out .= " "; // + Space for cal
$newpara = '';
}
$newpara .= $char;
}
$out .= $newpara;
}
return trim($out);
}
/**
* Decode vcal format
*
* @param string $str String to convert
* @return string String converted
*/
function quotedPrintDecode($str)
{
$out = preg_replace('/=\r?\n/', '', $str);
$out = quoted_printable_decode($out); // Available with PHP 4+
return trim($out);
}