1<?php 2 3namespace SabreForRainLoop\CalDAV; 4 5use SabreForRainLoop\DAV; 6use SabreForRainLoop\VObject; 7 8/** 9 * ICS Exporter 10 * 11 * This plugin adds the ability to export entire calendars as .ics files. 12 * This is useful for clients that don't support CalDAV yet. They often do 13 * support ics files. 14 * 15 * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/). 16 * @author Evert Pot (http://evertpot.com/) 17 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License 18 */ 19class ICSExportPlugin extends DAV\ServerPlugin { 20 21 /** 22 * Reference to Server class 23 * 24 * @var \SabreForRainLoop\DAV\Server 25 */ 26 protected $server; 27 28 /** 29 * Initializes the plugin and registers event handlers 30 * 31 * @param \SabreForRainLoop\DAV\Server $server 32 * @return void 33 */ 34 public function initialize(DAV\Server $server) { 35 36 $this->server = $server; 37 $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); 38 39 } 40 41 /** 42 * 'beforeMethod' event handles. This event handles intercepts GET requests ending 43 * with ?export 44 * 45 * @param string $method 46 * @param string $uri 47 * @return bool 48 */ 49 public function beforeMethod($method, $uri) { 50 51 if ($method!='GET') return; 52 if ($this->server->httpRequest->getQueryString()!='export') return; 53 54 // splitting uri 55 list($uri) = explode('?',$uri,2); 56 57 $node = $this->server->tree->getNodeForPath($uri); 58 59 if (!($node instanceof Calendar)) return; 60 61 // Checking ACL, if available. 62 if ($aclPlugin = $this->server->getPlugin('acl')) { 63 $aclPlugin->checkPrivileges($uri, '{DAV:}read'); 64 } 65 66 $this->server->httpResponse->setHeader('Content-Type','text/calendar'); 67 $this->server->httpResponse->sendStatus(200); 68 69 $nodes = $this->server->getPropertiesForPath($uri, array( 70 '{' . Plugin::NS_CALDAV . '}calendar-data', 71 ),1); 72 73 $this->server->httpResponse->sendBody($this->generateICS($nodes)); 74 75 // Returning false to break the event chain 76 return false; 77 78 } 79 80 /** 81 * Merges all calendar objects, and builds one big ics export 82 * 83 * @param array $nodes 84 * @return string 85 */ 86 public function generateICS(array $nodes) { 87 88 $calendar = new VObject\Component\VCalendar(); 89 $calendar->version = '2.0'; 90 if (DAV\Server::$exposeVersion) { 91 $calendar->prodid = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; 92 } else { 93 $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; 94 } 95 $calendar->calscale = 'GREGORIAN'; 96 97 $collectedTimezones = array(); 98 99 $timezones = array(); 100 $objects = array(); 101 102 foreach($nodes as $node) { 103 104 if (!isset($node[200]['{' . Plugin::NS_CALDAV . '}calendar-data'])) { 105 continue; 106 } 107 $nodeData = $node[200]['{' . Plugin::NS_CALDAV . '}calendar-data']; 108 109 $nodeComp = VObject\Reader::read($nodeData); 110 111 foreach($nodeComp->children() as $child) { 112 113 switch($child->name) { 114 case 'VEVENT' : 115 case 'VTODO' : 116 case 'VJOURNAL' : 117 $objects[] = $child; 118 break; 119 120 // VTIMEZONE is special, because we need to filter out the duplicates 121 case 'VTIMEZONE' : 122 // Naively just checking tzid. 123 if (in_array((string)$child->TZID, $collectedTimezones)) continue; 124 125 $timezones[] = $child; 126 $collectedTimezones[] = $child->TZID; 127 break; 128 129 } 130 131 } 132 133 } 134 135 foreach($timezones as $tz) $calendar->add($tz); 136 foreach($objects as $obj) $calendar->add($obj); 137 138 return $calendar->serialize(); 139 140 } 141 142} 143