root,'',$server['REQUEST_URI']);
$infos = explode('/',$target);
if(isset($infos[0]))$user = $infos[0];
if(isset($infos[1])) $calendar = $infos[1];
$logFunction = $this->log;
if(isset($logFunction)) $logFunction('notice','receive : '.$method.', body : '.PHP_EOL.$body.PHP_EOL.PHP_EOL.' --- target : '.$server['REQUEST_URI'].PHP_EOL.PHP_EOL);
switch($method) {
//Le client veux la liste des évenements de $calendar pour le user $user au format ical
case 'REPORT':
$stream = '';
$callback = $this->searchEvents;
$events = $callback($user,$calendar);
if(isset($logFunction)) $logFunction('notice','fetch '.count($events).' events, parsing to xml ...'.PHP_EOL.PHP_EOL);
foreach($events as $event){
$stream .= '
'.$this->root.$user.'/'.$calendar.'/'.$event['id'].'.ics
';
$stream .= IcalEvent::toString($event);
$stream .='
'.$event['updated'].'
HTTP/1.1 200 OK
';
}
$stream .='';
if(isset($logFunction)) $logFunction('notice','return stream : '.$stream.' '.PHP_EOL.PHP_EOL,true);
header('HTTP/1.1 207 Multi-Status');
echo $stream;
exit();
break;
case 'PROPFIND':
//Le client souhaite avoir le ctag (timestamp de derniere mise a jour du calendrier ciblé), si ce ctag est égal au dernier ctag fournis, le client ne met pas a jour les events
if(strpos($body, 'getctag')!==false) {
$callback = $this->calendarLastUpdate;
$lastUpdate = $callback($user,$calendar);
$stream = '
'.$this->root.'
'.$this->root.$user.'
'.$this->root.$user.'
'.$lastUpdate.'
HTTP/1.1 200 OK
';
}
//pour le calenderier ciblé, le client souhaite voir tous les etag (tag de derniere maj d'un évenement) pour voir lesquels il doit demander au serveur
if(strpos($body, 'getetag')!==false) {
$f = $this->searchEvents;
$events = $f($user,$calendar);
$stream = '
'.$this->root.$user.'/'.$calendar.'/
HTTP/1.1 200 OK
HTTP/1.1 404 Not Found
';
foreach($events as $event){
$stream .= '
'.$this->root.$user.'/'.$calendar.'/'.$event['id'].'.ics
text/calendar; charset=utf-8
'.$event['updated'].'
HTTP/1.1 200 OK
';
}
$stream .= '';
}
if(isset($logFunction)) $logFunction('notice','return stream : '.$stream.' '.PHP_EOL.PHP_EOL,true);
header('HTTP/1.1 207 Multi-Status');
echo $stream;
exit();
break;
//Récuperation d'un fichier (ou dossier ?)
case 'GET':
header('HTTP/1.1 200 Ok');
break;
//Envois d'un evenement
case 'PUT':
header('HTTP/1.1 201 Created');
preg_match('|([0-9]*)\.ics|i',$server['REQUEST_URI'],$m);
$callback = $this->saveEvent;
$events = $callback($user,$calendar,$m[1],IcalEvent::fromString($body));
if(isset($logFunction)) $logFunction('notice','event saved '.$user.'/'.$calendar.', id: '.$m[1].PHP_EOL.PHP_EOL);
break;
//Supression dossier/fichier
case 'DELETE':
header('HTTP/1.1 200 Ok');
$callback = $this->deleteEvent;
preg_match('|([0-9]*)\.ics|i',$server['REQUEST_URI'],$m);
try{
if(!isset($m[1])) throw new Exception("Event id non récuperable: ".$server['REQUEST_URI']);
$callback($user,$calendar,$m[1]);
if(isset($logFunction)) $logFunction('notice','event deleted '.$user.'/'.$calendar.', id: '.$m[1].PHP_EOL.PHP_EOL);
header('HTTP/1.1 200 Ok');
}catch(Exeption $e){
if(isset($logFunction)) $logFunction('error','unable to delete event: '.$e->getMessage().' '.PHP_EOL.PHP_EOL);
header('HTTP/1.1 403 Forbidden');
}
/*
200 => 'Ok',
201 => 'Created',
204 => 'No Content',
207 => 'Multi-Status',
403 => 'Forbidden',
404 => 'Not Found',
409 => 'Conflict',
415 => 'Unsupported Media Type',
500 => 'Internal Server Error',
501 => 'Method not implemented',
);
return 'HTTP/1.1 ' . $code . ' ' . $msg[$code];
*/
break;
//Déplacement/renommage dossier/fichier
case 'MOVE':
header('HTTP/1.1 200 Ok');
break;
//The OPTIONS method allows an http client to find out what HTTP methods are supported on a specific url.
case 'OPTIONS':
header('Allows: options get head post delete trace propfind proppatch copy mkcol put');
break;
case 'HEAD':
header('HTTP/1.1 200 Ok');
//header('HTTP/1.1 501 Method not implemented');
break;
case 'POST':
header('HTTP/1.1 501 Method not implemented');
break;
case 'TRACE':
header('HTTP/1.1 501 Method not implemented');
break;
//Updates properties of a resource or collection.
case 'PROPPATCH':
header('HTTP/1.1 501 Method not implemented');
break;
//Copie d'un élement vers un nouvel emplacement
case 'COPY':
//header('HTTP/1.1 501 Method not implemented');
header('HTTP/1.1 200 Ok');
break;
//Verouillage d'un élement
case 'LOCK':
header('HTTP/1.1 501 Method not implemented');
break;
//Déverouillage d'un élement
case 'UNLOCK':
header('HTTP/1.1 501 Method not implemented');
break;
}
}
}
class IcalEvent{
public $title,$description,$start,$end,$frequency,$location,$categories,$alarms,$ics,$uid;
public static function fromString($ical){
$event = new self();
$lines = array();
foreach(explode("\n",$ical) as $line):
$columns = explode(":",trim($line));
if(!isset($columns[1])) continue;
$key = $columns[0];
$value = $columns[1];
$keyvalues = explode(';',$key);
$key = array_shift($keyvalues);
//Ne prendre que la premiere description
if($key=='DESCRIPTION' && isset($lines['DESCRIPTION'])) continue;
$lines[$key] = $value;
endforeach;
if(isset($lines['CATEGORIES'])) $event->categories = $lines['CATEGORIES'];
if(isset($lines['SUMMARY'])) $event->title = $lines['SUMMARY'];
if(isset($lines['DESCRIPTION'])) $event->description = $lines['DESCRIPTION'];
if(isset($lines['DTSTART'])) $event->start = strtotime($lines['DTSTART']);
if(isset($lines['DTEND'])) $event->end = strtotime($lines['DTEND']);
if(isset($lines['RRULE'])) $event->frequency = $lines['RRULE'];
if(isset($lines['LOCATION'])) $event->location = $lines['LOCATION'];
if(isset($lines['UID'])) $event->uid = $lines['UID'];
if(isset($lines['TRIGGER'])) $event->alarms = $lines['TRIGGER'];
return $event;
}
public static function toString($event){
$ics = 'BEGIN:VCALENDAR'."\n";
$ics .= 'PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN'."\n";
$ics .= 'VERSION:2.0'."\n";
$ics .= 'BEGIN:VTIMEZONE'."\n";
$ics .= 'TZID:Europe/Paris'."\n";
$ics .= 'BEGIN:DAYLIGHT'."\n";
$ics .= 'TZOFFSETFROM:+0100'."\n";
$ics .= 'TZOFFSETTO:+0200'."\n";
$ics .= 'TZNAME:CEST'."\n";
$ics .= 'END:DAYLIGHT'."\n";
$ics .= 'BEGIN:STANDARD'."\n";
$ics .= 'TZOFFSETFROM:+0200'."\n";
$ics .= 'TZOFFSETTO:+0100'."\n";
$ics .= 'TZNAME:CET'."\n";
$ics .= 'DTSTART:19701025T030000'."\n";
$ics .= 'END:STANDARD'."\n";
$ics .= 'END:VTIMEZONE'."\n";
$ics .= 'BEGIN:VEVENT'."\n";
$ics .= 'DTSTART:19700329T020000'."\n";
if(isset($event['recurrence']) && $event['recurrence']!='')
$ics .= 'RRULE:'.$event['recurrence']."\n";
$ics .= 'CREATED:'.date('Ymd',$event['created']).'T'.date('His',$event['created']).'Z'."\n";
$ics .= 'LAST-MODIFIED:'.date('Ymd',$event['updated']).'T'.date('His',$event['updated']).'Z'."\n";
$ics .= 'DTSTAMP:20181209T222306Z'."\n";
$ics .= 'UID:'.$event['id']."\n";
if(!empty($event['label']))
$ics .= 'SUMMARY:'.$event['label']."\n";
if(!empty($event['type']))
$ics .= 'CATEGORIES:'.$event['type']."\n";
if(!empty($event['description']))
$ics .= 'DESCRIPTION:'.str_replace(array(PHP_EOL,"\r","\n"),'\n',$event['description'])." \n";
if($event['street'].$event['zip'].$event['city']!='')
$ics .= 'LOCATION:'.$event['street'].' '.$event['zip'].' '.$event['city']."\n";
$ics .= 'DTSTART;TZID=Europe/Paris:'.date('Ymd',$event['startDate']).'T'.date('His',$event['startDate'])."\n";
$ics .= 'DTEND;TZID=Europe/Paris:'.date('Ymd',$event['endDate']).'T'.date('His',$event['endDate'])."\n";
$ics .= 'TRANSP:OPAQUE'."\n"; //TRANSP : Définit si la ressource affectée à l'événement est rendu indisponible (OPAQUE, TRANSPARENT)
if($event['notificationNumber']!='0'){
if($event['notificationUnity'] == 'minut') $notification= 'PT'.$event['notificationNumber'].'M';
if($event['notificationUnity'] == 'hour') $notification = 'PT'.$event['notificationNumber'].'H';
if($event['notificationUnity'] == 'day') $notification = 'P'.$event['notificationNumber'].'D';
$ics .= 'BEGIN:VALARM'."\n";
$ics .= 'ACTION:DISPLAY'."\n";
$ics .= 'TRIGGER;VALUE=DURATION:-'.$notification.''."\n";
$ics .= 'DESCRIPTION:Rappel erp'."\n";
$ics .= 'END:VALARM'."\n";
}
$ics .= 'STATUS:CONFIRMED'."\n"; //STATUS : Statut de l'événement (TENTATIVE, CONFIRMED, CANCELLED)
$ics .= 'END:VEVENT'."\n";
$ics .= 'END:VCALENDAR'."\n";
return $ics;
}
}
?>