8dd919b507c6478029ae6b4c28f3e1ebd6c3c993
[tine20] / tine20 / Calendar / Frontend / WebDAV / Container.php
1 <?php
2
3 use Sabre\VObject;
4 use Sabre\DAVACL;
5 use Sabre\CalDAV;
6
7 /**
8  * Tine 2.0
9  *
10  * @package     Calendar
11  * @subpackage  Frontend
12  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
13  * @author      Lars Kneschke <l.kneschke@metaways.de>
14  * @copyright   Copyright (c) 2011-2013 Metaways Infosystems GmbH (http://www.metaways.de)
15  */
16
17 /**
18  * class to handle containers in CalDAV tree
19  *
20  * @package     Calendar
21  * @subpackage  Frontend
22  */
23 class Calendar_Frontend_WebDAV_Container extends Tinebase_WebDav_Container_Abstract implements Sabre\CalDAV\ICalendar, Sabre\CalDAV\IShareableCalendar
24 {
25     protected $_applicationName = 'Calendar';
26     
27     protected $_model = 'Event';
28     
29     protected $_suffix = '.ics';
30     
31     /**
32      * (non-PHPdoc)
33      * @see Sabre\DAV\Collection::getChild()
34      */
35     public function getChild($_name)
36     {
37         $modelName = $this->_application->name . '_Model_' . $this->_model;
38         
39         if ($_name instanceof $modelName) {
40             $object = $_name;
41         } else {
42             $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter';
43             $filter = new $filterClass(array(
44                 array(
45                     'field'     => 'container_id',
46                     'operator'  => 'equals',
47                     'value'     => $this->_container->getId()
48                 ),
49                 array('condition' => 'OR', 'filters' => array(
50                     array(
51                         'field'     => 'id',
52                         'operator'  => 'equals',
53                         'value'     => $this->_getIdFromName($_name)
54                     ),
55                     array(
56                         'field'     => 'uid',
57                         'operator'  => 'equals',
58                         'value'     => $this->_getIdFromName($_name)
59                     )
60                 ))
61             ));
62             $object = $this->_getController()->search($filter, null, false, false, 'sync')->getFirstRecord();
63         
64             if ($object == null) {
65                 throw new Sabre\DAV\Exception\NotFound('Object not found');
66             }
67         }
68         
69         $httpRequest = new Sabre\HTTP\Request();
70         
71         // lie about existence of event of request is a PUT request from an ATTENDEE for an already existing event 
72         // to prevent ugly (and not helpful) error messages on the client
73         if (isset($_SERVER['REQUEST_METHOD']) && $httpRequest->getMethod() == 'PUT' && $httpRequest->getHeader('If-None-Match') === '*') {
74             if (
75                 $object->organizer != Tinebase_Core::getUser()->contact_id && 
76                 Calendar_Model_Attender::getOwnAttender($object->attendee) !== null
77             ) {
78                 throw new Sabre\DAV\Exception\NotFound('Object not found');
79             }
80         }
81         
82         $objectClass = $this->_application->name . '_Frontend_WebDAV_' . $this->_model;
83         
84         return new $objectClass($this->_container, $object);
85     }
86     
87     /**
88      * Returns an array with all the child nodes
89      *
90      * @return Sabre\DAV\INode[]
91      */
92     function getChildren()
93     {
94         $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter';
95         $filter = new $filterClass(array(
96             array(
97                 'field'     => 'container_id',
98                 'operator'  => 'equals',
99                 'value'     => $this->_container->getId()
100             ),
101             array(
102                 'field'    => 'period', 
103                 'operator'  => 'within', 
104                 'value'     => array(
105                     'from'  => Tinebase_DateTime::now()->subWeek(4),
106                     'until' => Tinebase_DateTime::now()->addYear(4)
107                 )
108             )
109         ));
110         
111         /**
112          * see http://forge.tine20.org/mantisbt/view.php?id=5122
113          * we must use action 'sync' and not 'get' as
114          * otherwise the calendar also return events the user only can see because of freebusy
115          */
116         $objects = $this->_getController()->search($filter, null, false, false, 'sync');
117         
118         $children = array();
119         
120         foreach ($objects as $object) {
121             $children[] = $this->getChild($object);
122         }
123         
124         return $children;
125     }
126     
127     /**
128      * Returns the list of properties
129      *
130      * @param array $requestedProperties
131      * @return array
132      */
133     public function getProperties($requestedProperties) 
134     {
135         $ctags = Tinebase_Container::getInstance()->getContentSequence($this->_container);
136         
137         $properties = array(
138             '{http://calendarserver.org/ns/}getctag' => $ctags,
139             'id'                => $this->_container->getId(),
140             'uri'               => $this->_useIdAsName == true ? $this->_container->getId() : $this->_container->name,
141             '{DAV:}resource-id' => 'urn:uuid:' . $this->_container->getId(),
142             '{DAV:}owner'       => new Sabre\DAVACL\Property\Principal(Sabre\DAVACL\Property\Principal::HREF, 'principals/users/' . Tinebase_Core::getUser()->contact_id),
143             '{DAV:}displayname' => $this->_container->name,
144             '{http://apple.com/ns/ical/}calendar-color' => (empty($this->_container->color)) ? '#000000' : $this->_container->color,
145             
146             '{' . Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre\CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT')),
147             '{' . Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-data'          => new Sabre\CalDAV\Property\SupportedCalendarData(),
148             '{' . Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-description'             => 'Calendar ' . $this->_container->name,
149             '{' . Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-timezone'                => Tinebase_WebDav_Container_Abstract::getCalendarVTimezone($this->_application)
150         );
151         
152         if (!empty(Tinebase_Core::getUser()->accountEmailAddress)) {
153             $properties['{' . Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-address-set'    ] = new Sabre\DAV\Property\HrefList(array('mailto:' . Tinebase_Core::getUser()->accountEmailAddress), false);
154         }
155         
156         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) 
157             Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . print_r($properties, true));
158         
159         $response = array();
160     
161         foreach($requestedProperties as $prop) {
162             if (isset($properties[$prop])) {
163                 $response[$prop] = $properties[$prop];
164             }
165         }
166         
167         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) 
168             Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . print_r($response, true));
169         
170         return $response;
171     }
172     
173     protected function _getController()
174     {
175         if ($this->_controller === null) {
176             $this->_controller = Calendar_Controller_MSEventFacade::getInstance();
177         }
178         
179         return $this->_controller;
180     }
181     
182     /**
183      * Performs a calendar-query on the contents of this calendar.
184      *
185      * The calendar-query is defined in RFC4791 : CalDAV. Using the
186      * calendar-query it is possible for a client to request a specific set of
187      * object, based on contents of iCalendar properties, date-ranges and
188      * iCalendar component types (VTODO, VEVENT).
189      *
190      * This method should just return a list of (relative) urls that match this
191      * query.
192      *
193      * The list of filters are specified as an array. The exact array is
194      * documented by \Sabre\CalDAV\CalendarQueryParser.
195      *
196      * @param array $filters
197      * @return array
198      */
199     public function calendarQuery(array $filters)
200     {
201         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
202             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' filters ' . print_r($filters, true));
203         
204         $filterArray = array(array(
205             'field'    => 'container_id',
206             'operator' => 'equals',
207             'value'    => $this->_container->getId()
208         ));
209         
210         $periodFrom = null;
211         $periodUntil = null;
212         if (isset($filters['comp-filters']) && isset($filters['comp-filters'][0]['time-range'])) {
213             $timeRange = $filters['comp-filters'][0]['time-range'];
214             if (isset($timeRange['start'])) {
215                 if (! isset($timeRange['end'])) {
216                     // create default time-range end in 4 years from now 
217                     $timeRange['end'] = new DateTime('NOW');
218                     $timeRange['end']->add(new DateInterval('P4Y'));
219                 }
220                 
221                 $periodFrom = new Tinebase_DateTime($timeRange['start']);
222                 $periodUntil = new Tinebase_DateTime($timeRange['end']);
223             }
224         }
225
226         // @see 0009162: CalDAV Performance issues for many events
227         // create default time-range end in 4 years from now and 2 months back (configurable) if no filter was set by client
228         if ($periodFrom === null) {
229             $periodFrom = Tinebase_DateTime::now()->subMonth(Calendar_Config::getInstance()->get(Calendar_Config::MAX_FILTER_PERIOD_CALDAV, 2));
230         }
231         if ($periodUntil === null) {
232             $periodUntil = Tinebase_DateTime::now()->addYear(4);
233         }
234         
235         $filterArray[] = array(
236             'field' => 'period',
237             'operator' => 'within',
238             'value' => array(
239                 'from'  => $periodFrom,
240                 'until' => $periodUntil
241             )
242         );
243         
244         $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter';
245         $filter = new $filterClass($filterArray);
246     
247         // @see http://forge.tine20.org/mantisbt/view.php?id=5122
248         // we must use action 'sync' and not 'get' as
249         // otherwise the calendar also return events the user only can see because of freebusy
250         $ids = $this->_getController()->search($filter, null, false, true, 'sync');
251     
252         return $ids;
253     }
254     
255     /**
256      * (non-PHPdoc)
257      * @see \Sabre\CalDAV\IShareableCalendar::getShares()
258      */
259     public function getShares()
260     {
261         $result = array();
262         
263         try {
264             $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($this->_container);
265         } catch (Tinebase_Exception_AccessDenied $e) {
266             // user has no right/grant to see all grants of this container
267             $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants');
268             $grants->addRecord(Tinebase_Container::getInstance()->getGrantsOfAccount(Tinebase_Core::getUser(), $this->_container));
269         }
270         
271         foreach ($grants as $grant) {
272             
273             switch ($grant->account_type) {
274                 case 'anyone':
275                     $href       = '/principals/groups/anyone';
276                     $commonName = 'Anyone';
277                     break;
278                 
279                 case 'group':
280                     try {
281                         $list       = Tinebase_Group::getInstance()->getGroupById($grant->account_id);
282                     } catch (Tinebase_Exception_NotFound $tenf) {
283                         continue;
284                     }
285                      
286                     $href       = '/principals/groups/' . $list->list_id;
287                     $commonName = $list->name;
288                     
289                     break;
290                     
291                 case 'user':
292                     try {
293                         $contact    = Tinebase_User::getInstance()->getUserById($grant->account_id);
294                     } catch (Tinebase_Exception_NotFound $tenf) {
295                         continue;
296                     }
297                      
298                     $href       = '/principals/users/' . $contact->contact_id;
299                     $commonName = $contact->accountDisplayName;
300                     break;
301             }
302             
303             $writeAble = $grant[Tinebase_Model_Grants::GRANT_ADMIN] || 
304                          ( $grant[Tinebase_Model_Grants::GRANT_READ] && 
305                            $grant[Tinebase_Model_Grants::GRANT_ADD]  && 
306                            $grant[Tinebase_Model_Grants::GRANT_EDIT] &&
307                            $grant[Tinebase_Model_Grants::GRANT_DELETE] );
308             
309             $result[] = array(
310                 'href'       => $href,
311                 'commonName' => $commonName,
312                 'status'     => Sabre\CalDAV\SharingPlugin::STATUS_ACCEPTED,
313                 'readOnly'   => !$writeAble, 
314                 'summary'    => null            //optional
315             ); 
316         }
317         
318         return $result;
319     }
320     
321     /**
322      * Returns the list of supported privileges for this node.
323      *
324      * The returned data structure is a list of nested privileges.
325      * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
326      * standard structure.
327      *
328      * If null is returned from this method, the default privilege set is used,
329      * which is fine for most common usecases.
330      *
331      * @return array|null
332      */
333     public function getSupportedPrivilegeSet() 
334     {
335         $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet();
336
337         // We need to inject 'read-free-busy' in the tree, aggregated under
338         // {DAV:}read.
339         foreach($default['aggregates'] as &$agg) {
340
341             if ($agg['privilege'] !== '{DAV:}read') continue;
342
343             $agg['aggregates'][] = array(
344                 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}read-free-busy',
345             );
346
347         }
348         
349         return $default;
350     }
351     
352     /**
353      * (non-PHPdoc)
354      * @see \Sabre\CalDAV\IShareableCalendar::updateShares()
355      */
356     public function updateShares(array $add, array $remove)
357     {
358         
359     }
360 }