CalDAV - SpeedUpPropfindPlugin: postgres compatibility
[tine20] / tine20 / Calendar / Frontend / CalDAV / SpeedUpPropfindPlugin.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Calendar
6  * @subpackage  Frontend
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Paul Mehrer <p.mehrer@metaways.de>
9  * @copyright   Copyright (c) 2015-2015 Metaways Infosystems GmbH (http://www.metaways.de)
10  *
11  */
12
13 /**
14  * Sabre speedup plugin for propfind
15  *
16  * This plugin checks if all properties requested by propfind can be served with one single query.
17  *
18  * @package     Calendar
19  * @subpackage  Frontend
20  */
21
22 class Calendar_Frontend_CalDAV_SpeedUpPropfindPlugin extends Sabre\DAV\ServerPlugin
23 {
24     /**
25      * Reference to server object
26      *
27      * @var Sabre\DAV\Server
28      */
29     private $server;
30
31     /**
32      * Returns a plugin name.
33      *
34      * Using this name other plugins will be able to access other plugins
35      * using \Sabre\DAV\Server::getPlugin
36      *
37      * @return string
38      */
39     public function getPluginName()
40     {
41         return 'speedUpPropfindPlugin';
42     }
43
44     /**
45      * Initializes the plugin
46      *
47      * @param Sabre\DAV\Server $server
48      * @return void
49      */
50     public function initialize(Sabre\DAV\Server $server)
51     {
52         $this->server = $server;
53
54         $self = $this;
55         $server->subscribeEvent('beforeMethod', function($method, $uri) use ($self) {
56             if ('PROPFIND' === $method)
57                 return $self->propfind($uri);
58             elseif ('REPORT' === $method)
59                 return $self->report($uri);
60             else
61                 return true;
62         });
63     }
64
65     /**
66      * This functions handles REPORT requests specific to CalDAV
67      *
68      * @param string $uri
69      * @return bool
70      */
71     public function report($uri)
72     {
73         if ($this->server->httpRequest->getHeader('Depth') !== '1') {
74             return true;
75         }
76
77         $body = $this->server->httpRequest->getBody(true);
78         rewind($this->server->httpRequest->getBody());
79         $dom = Sabre\DAV\XMLUtil::loadDOMDocument($body);
80
81         $reportName = Sabre\DAV\XMLUtil::toClarkNotation($dom->firstChild);
82
83         if(strpos($reportName, 'calendar-query') !== false) {
84
85             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
86                 . " in report speedup");
87
88             $properties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($dom->firstChild));
89             if (count($properties) != 2 || !in_array('{DAV:}getetag', $properties) || !in_array('{DAV:}getcontenttype',$properties)) {
90
91                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
92                     . " requested properties dont match speedup conditions, continuing");
93
94                 return true;
95             }
96
97             $filter = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter');
98             if ($filter->length != 1 || $filter->item(0)->childNodes->length != 1 ||
99                 $filter->item(0)->childNodes->item(0)->getAttribute('name') !== 'VCALENDAR' ||
100                 $filter->item(0)->childNodes->item(0)->childNodes->length != 1 ||
101                 $filter->item(0)->childNodes->item(0)->childNodes->item(0)->getAttribute('name') !== 'VTODO' ||
102                 $filter->item(0)->childNodes->item(0)->childNodes->item(0)->hasChildNodes()) {
103
104                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
105                     . " requested properties dont match speedup conditions, continuing");
106
107                 return true;
108             }
109
110
111             return $this->_speedUpRequest($uri);
112         }
113         return true;
114     }
115
116     /**
117      * This functions handles PROPFIND requests specific to CalDAV
118      * 
119      * @param string $uri
120      * @return bool
121      */
122     public function propfind($uri)
123     {
124         if ($this->server->httpRequest->getHeader('Depth') !== '1') {
125             return true;
126         }
127
128         $body = $this->server->httpRequest->getBody(true);
129         rewind($this->server->httpRequest->getBody());
130         $dom = Sabre\DAV\XMLUtil::loadDOMDocument($body);
131
132         $reportName = Sabre\DAV\XMLUtil::toClarkNotation($dom->firstChild);
133
134         if($reportName === '{DAV:}propfind') {
135
136             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
137                 . " in propfind speedup");
138
139             $properties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($dom->firstChild));
140             if (count($properties) != 2 || !in_array('{DAV:}getetag', $properties) || !in_array('{DAV:}getcontenttype',$properties)) {
141
142                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
143                     . " requested properties dont match speedup conditions, continuing");
144
145                 return true;
146             }
147
148
149             return $this->_speedUpRequest($uri);
150         }
151         return true;
152     }
153
154     /**
155      * @param string $uri
156      * @return bool
157      */
158     protected function _speedUpRequest($uri)
159     {
160         /**
161          * @var Calendar_Frontend_WebDAV_Container
162          */
163         $node = $this->server->tree->getNodeForPath($uri);
164         if (!($node instanceof Calendar_Frontend_WebDAV_Container) ) {
165             return true;
166         }
167
168         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
169             . " speedup sql start");
170
171         $db = Tinebase_Core::getDb();
172
173         $stmt = $db->query('SELECT ev.id, ev.seq FROM tine20_cal_events AS ev WHERE ev.is_deleted = 0 AND ' .
174             'ev.recurid IS NULL AND (ev.container_id = ' . $db->quote($node->getId()) . ' OR ev.id IN (
175             SELECT cal_event_id FROM tine20_cal_attendee WHERE displaycontainer_id = ' . $db->quote($node->getId()) . ')) GROUP BY ev.id');
176
177         $result = $stmt->fetchAll();
178
179         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
180             . " speedup sql done");
181
182         $dom = new \DOMDocument('1.0', 'utf-8');
183
184         //$dom->formatOutput = true;
185         $multiStatus = $dom->createElement('d:multistatus');
186
187         // Adding in default namespaces
188         foreach ($this->server->xmlNamespaces as $namespace => $prefix) {
189             $multiStatus->setAttribute('xmlns:' . $prefix, $namespace);
190         }
191
192         /*$response = $dom->createElement('d:response');
193         $href = $dom->createElement('d:href', $uri);
194         $response->appendChild($href);
195         $multiStatus->appendChild($response);*/
196
197         foreach ($result as $row) {
198             $a = array();
199             $a[200] = array(
200                 '{DAV:}getetag' => '"' . sha1($row['id'] . $row['seq']) . '"',
201                 '{DAV:}getcontenttype' => 'text/calendar',
202             );
203             $href = $uri . '/' . $row['id'] . '.ics';
204             $response = new Sabre\DAV\Property\Response($href, $a);
205             $response->serialize($this->server, $multiStatus);
206         }
207
208         $dom->appendChild($multiStatus);
209
210         $this->server->httpResponse->sendStatus(207);
211         $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
212         $this->server->httpResponse->sendBody($dom->saveXML());
213
214         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
215             . " speedup successfully responded to request");
216
217         return false;
218     }
219 }