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