Merge branch '2015.11-develop' into 2016.11
[tine20] / tine20 / Tinebase / WebDav / Collection / AbstractContainerTree.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  WebDAV
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Lars Kneschke <l.kneschke@metaways.de>
9  * @copyright   Copyright (c) 2014-2014 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * class to handle top level folders for an application
14  *
15  * @package     Tinebase
16  * @subpackage  WebDAV
17  */
18 abstract class Tinebase_WebDav_Collection_AbstractContainerTree extends \Sabre\DAV\Collection implements \Sabre\DAV\IProperties, \Sabre\DAVACL\IACL, \Sabre\DAV\IExtendedCollection
19 {
20     /**
21      * the current application object
22      * 
23      * @var Tinebase_Model_Application
24      */
25     protected $_application;
26     
27     /**
28      * application name
29      *
30      * @var string
31      */
32     protected $_applicationName;
33     
34     /**
35      * app has personal folders
36      *
37      * @var string
38      */
39     protected $_hasPersonalFolders = true;
40     
41     /**
42      * app has records folder
43      *
44      * @var string
45      */
46     protected $_hasRecordFolder = true;
47     
48     /**
49      * the current path
50      * 
51      * @var string
52      */
53     protected $_path;
54     
55     /**
56      * @var array
57      */
58     protected $_pathParts;
59     
60     /**
61      * 
62      * @var boolean
63      */
64     protected $_useIdAsName;
65     
66     protected static $_classCache = array (
67         '_getUser' => array()
68     );
69     
70     /**
71      * contructor
72      * 
73      * @param string $path         the current path
74      * @param bool   $useIdAsName  use name or id as node name
75      */
76     public function __construct($path, $useIdAsName = false)
77     {
78         $this->_path        = $path;
79         $this->_useIdAsName = $useIdAsName;
80     }
81
82     /**
83      * use login as folder name
84      *
85      * @return boolean
86      */
87     protected function _useLoginAsFolderName()
88     {
89         return Tinebase_Config::getInstance()->get(Tinebase_Config::USE_LOGINNAME_AS_FOLDERNAME);
90     }
91     
92     /**
93      * (non-PHPdoc)
94      * @see \Sabre\DAV\Collection::createDirectory()
95      */
96     public function createDirectory($name) 
97     {
98         return $this->_createContainer(array(
99             'name' => $name
100         ));
101     }
102     
103     /**
104      * (non-PHPdoc)
105      * @see \Sabre\DAV\IExtendedCollection::createExtendedCollection()
106      */
107     public function createExtendedCollection($name, array $resourceType, array $properties)
108     {
109         return $this->_createContainer(array(
110             'name'  => isset($properties['{DAV:}displayname']) ? $properties['{DAV:}displayname'] : $name,
111             'uuid'  => $name,
112             'color' => isset($properties['{http://apple.com/ns/ical/}calendar-color']) ? substr($properties['{http://apple.com/ns/ical/}calendar-color'], 0, 7) : null
113         ));
114     }
115     
116     /**
117      * (non-PHPdoc)
118      * @see Sabre\DAV\Collection::getChild()
119      */
120     public function getChild($name)
121     {
122         switch (count($this->_getPathParts())) {
123             # path == /<applicationPrefix> (for example calendars)
124             # return folders for currentuser, other users and 'shared' folder
125             # $name can be
126             # * contact_id of user
127             # * 'shared'
128             case 1:
129                 if ($name === Tinebase_Model_Container::TYPE_SHARED ||
130                     ($this->_hasRecordFolder && $name === Tinebase_FileSystem::FOLDER_TYPE_RECORDS)) {
131                     $path = $this->_path . '/' . $name;
132                     
133                 } elseif ($this->_hasPersonalFolders) {
134                     if ($name === '__currentuser__') {
135                         $path = $this->_path . '/__currentuser__';
136                         
137                     } else {
138                         try {
139                             // check if it exists only
140                             $this->_getUser($name);
141                             
142                         } catch (Tinebase_Exception_NotFound $tenf) {
143                             $message = "Directory $this->_path/$name not found";
144                             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
145                                 __METHOD__ . '::' . __LINE__ . ' ' . $message);
146                             throw new \Sabre\DAV\Exception\NotFound($message);
147                         }
148                         
149                         $path = $this->_path . '/' . $name;
150                     }
151                     
152                 } else {
153                     throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
154                 }
155                 
156                 $className = $this->_getApplicationName() . '_Frontend_WebDAV';
157                 
158                 return new $className($path, $this->_useIdAsName);
159                 
160                 break;
161                 
162             # path == /<applicationPrefix>/<contactid>|'shared'
163             # list container
164             case 2:
165                 if (Tinebase_Helper::array_value(1, $this->_getPathParts()) == Tinebase_Model_Container::TYPE_SHARED) {
166                     try { 
167                         if ($name instanceof Tinebase_Model_Container) {
168                             $container = $name;
169                         } elseif ($this->_useIdAsName) {
170                             // first try to fetch by uuid ...
171                             try {
172                                 $container = Tinebase_Container::getInstance()->getByProperty((string) $name, 'uuid');
173                             } catch (Tinebase_Exception_NotFound $tenf) {
174                                 // ... if that fails by id
175                                 $container = Tinebase_Container::getInstance()->getContainerById($name);
176                             }
177                         } else {
178                             $container = Tinebase_Container::getInstance()->getContainerByName(
179                                 $this->_getApplicationName(),
180                                 (string) $name,
181                                 Tinebase_Model_Container::TYPE_SHARED
182                             );
183                         }
184                         
185                     } catch (Tinebase_Exception_NotFound $tenf) {
186                         throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
187                         
188                     } catch (Tinebase_Exception_InvalidArgument $teia) {
189                         // invalid container id provided
190                         throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
191                     }
192                 } elseif ($this->_hasRecordFolder && Tinebase_Helper::array_value(1, $this->_getPathParts()) == Tinebase_FileSystem::FOLDER_TYPE_RECORDS) {
193                     
194                     return new Tinebase_Frontend_WebDAV_RecordCollection($this->_path . '/' . $name);
195                     
196                 } elseif ($this->_hasPersonalFolders) {
197                     if (Tinebase_Helper::array_value(1, $this->_getPathParts()) === '__currentuser__') {
198                         $accountId = Tinebase_Core::getUser()->accountId;
199                         
200                     } else {
201                         try {
202                             $accountId = $this->_getUser(Tinebase_Helper::array_value(1, $this->_getPathParts()))->accountId;
203                             
204                         } catch (Tinebase_Exception_NotFound $tenf) {
205                             throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
206                         }
207                     }
208                     
209                     try {
210                         if ($name instanceof Tinebase_Model_Container) {
211                             $container = $name;
212                         } elseif ($this->_useIdAsName) {
213                             // first try to fetch by uuid ...
214                             try {
215                                 $container = Tinebase_Container::getInstance()->getByProperty((string) $name, 'uuid');
216                             } catch (Tinebase_Exception_NotFound $tenf) {
217                                 // ... if that fails by id
218                                 $container = Tinebase_Container::getInstance()->getContainerById($name);
219                             }
220                             
221                         } else { 
222                             $container = Tinebase_Container::getInstance()->getContainerByName(
223                                 $this->_getApplicationName(),
224                                 (string) $name,
225                                 Tinebase_Model_Container::TYPE_PERSONAL, 
226                                 $accountId
227                             );
228                         }
229                         
230                     } catch (Tinebase_Exception_NotFound $tenf) {
231                         throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
232                         
233                     } catch (Tinebase_Exception_InvalidArgument $teia) {
234                         // invalid container id provided
235                         throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
236                     }
237                     
238                 } else {
239                     throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
240                 }
241                 
242                 if (!Tinebase_Core::getUser()->hasGrant($container, Tinebase_Model_Grants::GRANT_READ) ||
243                     !Tinebase_Core::getUser()->hasGrant($container, Tinebase_Model_Grants::GRANT_SYNC)) {
244                     throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
245                 }
246                 
247                 $objectClass = Tinebase_Application::getInstance()->getApplicationById($container->application_id)->name . '_Frontend_WebDAV_Container';
248                 
249                 if (! class_exists($objectClass)) {
250                     throw new \Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
251                 }
252                  
253                 return new $objectClass($container, $this->_useIdAsName);
254                 
255                 break;
256                 
257             default:
258                 throw new Sabre\DAV\Exception\NotFound("Directory $this->_path/$name not found");
259             
260                 break;
261         }
262     }
263     
264     /**
265      * Returns an array with all the child nodes
266      * 
267      * the records subtree is not returned as child here. It's only available via getChild().
268      *
269      * @return \Sabre\DAV\INode[]
270      */
271     function getChildren()
272     {
273         $children = array();
274         
275         switch (count($this->_getPathParts())) {
276             # path == /<applicationPrefix> (for example calendars)
277             # return folders for currentuser, other users and 'shared' folder
278             case 1:
279                 $children[] = $this->getChild(Tinebase_Model_Container::TYPE_SHARED);
280                 
281                 if ($this->_hasPersonalFolders) {
282                     $children[] = $this->getChild(
283                         $this->_useIdAsName ? Tinebase_Core::getUser()->contact_id : $this->_useLoginAsFolderName() ? Tinebase_Core::getUser()->accountLoginName : Tinebase_Core::getUser()->accountDisplayName
284                     );
285                     
286                     $otherUsers = Tinebase_Container::getInstance()->getOtherUsers(Tinebase_Core::getUser(), $this->_getApplicationName(), array(
287                         Tinebase_Model_Grants::GRANT_READ,
288                         Tinebase_Model_Grants::GRANT_SYNC
289                     ));
290                     
291                     foreach ($otherUsers as $user) {
292                         if ($user->contact_id && $user->visibility === Tinebase_Model_User::VISIBILITY_DISPLAYED) {
293                             try {
294                                 $folderId = $this->_useIdAsName ? $user->contact_id : $this->_useLoginAsFolderName() ? $user->accountLoginName : $user->accountDisplayName;
295
296                                 $children[] = $this->getChild($folderId);
297                             } catch (\Sabre\DAV\Exception\NotFound $sdavenf) {
298                                 // ignore contacts not found
299                             }
300                         }
301                     }
302                 }
303         
304                 break;
305             
306             # path == /<applicationPrefix>/<contactid>|'shared'
307             # list container
308             case 2:
309                 if (Tinebase_Helper::array_value(1, $this->_getPathParts()) == Tinebase_Model_Container::TYPE_SHARED) {
310                     $containers = Tinebase_Container::getInstance()->getSharedContainer(
311                         Tinebase_Core::getUser(),
312                         $this->_getApplicationName(),
313                         array(
314                             Tinebase_Model_Grants::GRANT_READ,
315                             Tinebase_Model_Grants::GRANT_SYNC
316                         )
317                     );
318                     
319                 } elseif ($this->_hasPersonalFolders) {
320                     if (Tinebase_Helper::array_value(1, $this->_getPathParts()) === '__currentuser__') {
321                         $accountId = Tinebase_Core::getUser()->accountId;
322                         
323                     } else {
324                         try {
325                             $accountId = $this->_getUser(Tinebase_Helper::array_value(1, $this->_getPathParts()))->accountId;
326                         } catch (Tinebase_Exception_NotFound $tenf) {
327                             throw new \Sabre\DAV\Exception\NotFound("Path $this->_path not found");
328                         }
329                     }
330                     
331                     try {
332                         if ($this->_getApplicationName() === 'Filemanager' || $this->_clientSupportsDelegations()) {
333                             $containers = Tinebase_Container::getInstance()->getPersonalContainer(
334                                 Tinebase_Core::getUser(),
335                                 $this->_getApplicationName(),
336                                 $accountId,
337                                 array(
338                                     Tinebase_Model_Grants::GRANT_READ, 
339                                     Tinebase_Model_Grants::GRANT_SYNC
340                                 )
341                             ); 
342                         } else {
343                             $containers = Tinebase_Container::getInstance()->getContainerByACL(Tinebase_Core::getUser(), $this->_getApplicationName(),  array(
344                                 Tinebase_Model_Grants::GRANT_READ, 
345                                 Tinebase_Model_Grants::GRANT_SYNC
346                             ));
347                         }
348                     } catch (Tinebase_Exception_AccessDenied $tead) {
349                         throw new Sabre\DAV\Exception\NotFound("Could not find path (" . $tead->getMessage() . ")");
350                     }
351                     
352                 } else {
353                     throw new Sabre\DAV\Exception\NotFound("Path $this->_path not found");
354                 }
355                 
356                 foreach ($containers as $container) {
357                     try {
358                         $children[] = $this->getChild($this->_useIdAsName ? $container->getId() : $container->name);
359                     } catch (\Sabre\DAV\Exception\NotFound $sdavenf) {
360                         // ignore containers not found
361                     }
362                 }
363                 
364                 break;
365                 
366             default:
367                 throw new Sabre\DAV\Exception\NotFound("Path $this->_path not found");
368                 
369                 break;
370         }
371         
372         return $children;
373     }
374     
375     /**
376      * checks if client supports delegations
377      * 
378      * @return boolean
379      * 
380      * @todo don't use $_SERVER to fetch user agent
381      * @todo move user agent parsing to Tinebase
382      */
383     protected function _clientSupportsDelegations()
384     {
385         if (isset($_SERVER['HTTP_USER_AGENT'])) {
386             list($backend, $version) = Calendar_Convert_Event_VCalendar_Factory::parseUserAgent($_SERVER['HTTP_USER_AGENT']);
387             $clientSupportsDelegations = ($backend === Calendar_Convert_Event_VCalendar_Factory::CLIENT_MACOSX);
388         } else {
389             $clientSupportsDelegations = false;
390         }
391         
392         return $clientSupportsDelegations;
393     }
394     
395     /**
396      * return etag
397      * 
398      * @return string
399      */
400     public function getETag()
401     {
402         $etags = array();
403         
404         foreach ($this->getChildren() as $child) {
405             $etags[] = $child->getETag();
406         }
407         
408         return '"' . sha1(implode(null, $etags)) . '"';
409     }
410     
411     /**
412      * Returns a group principal
413      *
414      * This must be a url to a principal, or null if there's no owner
415      *
416      * @return string|null
417      */
418     public function getGroup()
419     {
420         return null;
421     }
422     
423     /**
424      * (non-PHPdoc)
425      * @see \Sabre\DAV\Node::getLastModified()
426      */
427     public function getLastModified()
428     {
429         $lastModified = 1;
430         
431         foreach ($this->getChildren() as $child) {
432             $lastModified = $child->getLastModified() > $lastModified ? $child->getLastModified() : $lastModified;
433         }
434         
435         return $lastModified;
436     }
437     
438     /**
439      * Returns a list of ACE's for this node.
440      *
441      * Each ACE has the following properties:
442      *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
443      *     currently the only supported privileges
444      *   * 'principal', a url to the principal who owns the node
445      *   * 'protected' (optional), indicating that this ACE is not allowed to
446      *      be updated.
447      *      
448      * @todo implement real logic
449      * @return array
450      */
451     public function getACL() 
452     {
453         $principal = 'principals/users/' . Tinebase_Core::getUser()->contact_id;
454         
455         return array(
456             array(
457                 'privilege' => '{DAV:}read',
458                 'principal' => $principal,
459                 'protected' => true,
460             ),
461             array(
462                 'privilege' => '{DAV:}write',
463                 'principal' => $principal,
464                 'protected' => true,
465             )
466         );
467     }
468     
469     /**
470      * Returns the name of the node
471      *
472      * @return string
473      */
474     public function getName()
475     {
476         if (count($this->_getPathParts()) === 2 && 
477             Tinebase_Helper::array_value(1, $this->_getPathParts()) !== Tinebase_Model_Container::TYPE_SHARED &&
478             !$this->_useIdAsName
479         ) {
480             try {
481                 $user = $this->_getUser(Tinebase_Helper::array_value(1, $this->_getPathParts()));
482                 
483                 $name = $this->_useLoginAsFolderName() ? $user->accountLoginName : $user->accountDisplayName;
484                 
485             } catch (Tinebase_Exception_NotFound $tenf) {
486                 list(,$name) = Sabre\DAV\URLUtil::splitPath($this->_path);
487             }
488             
489         } else {
490             list(,$name) = Sabre\DAV\URLUtil::splitPath($this->_path);
491         }
492         
493         return $name;
494     }
495     
496     /**
497      * Returns the owner principal
498      *
499      * This must be a url to a principal, or null if there's no owner
500      * 
501      * @return string|null
502      */
503     public function getOwner()
504     {
505         if (count($this->_getPathParts()) === 2 && $this->getName() !== Tinebase_Model_Container::TYPE_SHARED) {
506             try {
507                 $user = $this->_getUser(Tinebase_Helper::array_value(1, $this->_getPathParts()));
508             } catch (Tinebase_Exception_NotFound $tenf) {
509                 return null;
510             }
511             
512             return 'principals/users/' . $user->contact_id;
513         }
514         
515         return null;
516     }
517     
518     /**
519      * Returns the list of properties
520      *
521      * @param array $requestedProperties
522      * @return array
523      */
524     public function getProperties($requestedProperties) 
525     {
526         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
527             __METHOD__ . '::' . __LINE__ . ' path: ' . $this->_path . ' ' . print_r($requestedProperties, true));
528         
529         $response = array();
530     
531         foreach ($requestedProperties as $property) {
532             switch ($property) {
533                 case '{DAV:}displayname':
534                     if (count($this->_getPathParts()) === 2 && $this->getName() !== Tinebase_Model_Container::TYPE_SHARED) {
535                         try {
536                             $user = $this->_getUser(Tinebase_Helper::array_value(1, $this->_getPathParts()));
537                             $contact = Addressbook_Controller_Contact::getInstance()->get($user->contact_id);
538                         } catch (Tinebase_Exception_NotFound $tenf) {
539                             continue;
540                         }
541                         
542                         $response[$property] = $contact->n_fileas;
543                     }
544                     
545                     break;
546                     
547                 case '{DAV:}owner':
548                     if ($this->getOwner()) {
549                         $response[$property] = new \Sabre\DAVACL\Property\Principal(
550                             \Sabre\DAVACL\Property\Principal::HREF, $this->getOwner()
551                         );
552                     }
553                     
554                     break;
555                     
556                 case '{DAV:}getetag':
557                     $response[$property] = $this->getETag();
558                     
559                     break;
560             }
561         }
562         
563         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
564             __METHOD__ . '::' . __LINE__ . ' path: ' . $this->_path . ' ' . print_r($response, true));
565         
566         return $response;
567     }
568     
569     /**
570      * Updates the ACL
571      *
572      * This method will receive a list of new ACE's.
573      *
574      * @param array $acl
575      * @return void
576      */
577     public function setACL(array $acl)
578     {
579         throw new Sabre\DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
580     }
581     
582     /**
583      * Updates properties on this node,
584      *
585      * The properties array uses the propertyName in clark-notation as key,
586      * and the array value for the property value. In the case a property
587      * should be deleted, the property value will be null.
588      *
589      * This method must be atomic. If one property cannot be changed, the
590      * entire operation must fail.
591      *
592      * If the operation was successful, true can be returned.
593      * If the operation failed, false can be returned.
594      *
595      * Deletion of a non-existant property is always succesful.
596      *
597      * Lastly, it is optional to return detailed information about any
598      * failures. In this case an array should be returned with the following
599      * structure:
600      *
601      * array(
602      *   403 => array(
603      *      '{DAV:}displayname' => null,
604      *   ),
605      *   424 => array(
606      *      '{DAV:}owner' => null,
607      *   )
608      * )
609      *
610      * In this example it was forbidden to update {DAV:}displayname. 
611      * (403 Forbidden), which in turn also caused {DAV:}owner to fail
612      * (424 Failed Dependency) because the request needs to be atomic.
613      *
614      * @param array $mutations 
615      * @return bool|array 
616      */
617     public function updateProperties($mutations) 
618     {
619         $result = array(
620             200 => array(),
621             403 => array()
622         );
623
624         foreach ($mutations as $key => $value) {
625             switch ($key) {
626                 // once iCal tried to set default-alarm config with a negative feedback
627                 // it doesn't send default-alarms to the server any longer. So we fake
628                 // success here as workaround to let the client send its default alarms
629                 case '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}default-alarm-vevent-datetime':
630                 case '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}default-alarm-vevent-date':
631                 case '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}default-alarm-vtodo-datetime':
632                 case '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}default-alarm-vtodo-date':
633                     // fake success
634                     $result['200'][$key] = null;
635                     break;
636
637                 default:
638                     $result['403'][$key] = null;
639             }
640         }
641
642         return $result;
643     }
644     
645     /**
646      * 
647      */
648     public function getSupportedPrivilegeSet()
649     {
650         return null;
651     }
652     
653     /**
654      * return application object
655      * 
656      * @return Tinebase_Model_Application
657      */
658     protected function _getApplication()
659     {
660         if (!$this->_application) {
661             $this->_application = Tinebase_Application::getInstance()->getApplicationByName($this->_getApplicationName());
662         }
663         
664         return $this->_application;
665     }
666     
667     /**
668      * creates a new container
669      * 
670      * @todo allow to create personal folders only when in currents users own path
671      * 
672      * @param  array  $properties  properties for new container
673      * @throws \Sabre\DAV\Exception\Forbidden
674      * @return Tinebase_Model_Container
675      */
676     protected function _createContainer(array $properties) 
677     {
678         if (count($this->_getPathParts()) !== 2) {
679             throw new \Sabre\DAV\Exception\Forbidden('Permission denied to create directory ' . $properties['name']);
680         }
681         
682         $containerType = Tinebase_Helper::array_value(1, $this->_getPathParts()) == Tinebase_Model_Container::TYPE_SHARED ?
683             Tinebase_Model_Container::TYPE_SHARED :
684             Tinebase_Model_Container::TYPE_PERSONAL;
685         
686         $newContainer = new Tinebase_Model_Container(array_merge($properties, array(
687             'type'              => $containerType,
688             'backend'           => 'Sql',
689             'application_id'    => $this->_getApplication()->getId(),
690             'model'             => Tinebase_Core::getApplicationInstance($this->_applicationName)->getDefaultModel()
691         )));
692
693         try {
694             $container = Tinebase_Container::getInstance()->addContainer($newContainer);
695         } catch (Tinebase_Exception_AccessDenied $tead) {
696             throw new \Sabre\DAV\Exception\Forbidden('Permission denied to create directory ' . $properties['name']);
697         }
698         
699         return $container;
700     }
701     
702     /**
703      * return application name
704      * 
705      * @return string
706      */
707     protected function _getApplicationName()
708     {
709         if (!$this->_applicationName) {
710             $this->_applicationName = Tinebase_Helper::array_value(0, explode('_', get_class($this)));
711         }
712         
713         return $this->_applicationName;
714     }
715     
716     /**
717      * get path parts
718      * 
719      * @return array
720      */
721     protected function _getPathParts()
722     {
723         if (!$this->_pathParts) {
724             $this->_pathParts = $this->_parsePath($this->_path);
725         }
726         
727         return $this->_pathParts;
728     }
729     
730     /**
731      * split path into parts
732      * 
733      * @param  string  $_path
734      * @return array
735      */
736     protected function _parsePath($_path)
737     {
738         $pathParts = explode('/', trim($this->_path, '/'));
739         
740         return $pathParts;
741     }
742
743     protected function _getUser($_id)
744     {
745         $classCacheId = ($this->_useIdAsName ? 'contact_id' : $this->_useLoginAsFolderName() ? 'accountLoginName' : 'accountDisplayName') . $_id;
746
747         if (isset(self::$_classCache[__FUNCTION__][$classCacheId])) {
748             return self::$_classCache[__FUNCTION__][$classCacheId];
749         }
750
751         if ($this->_useIdAsName) {
752             $contact = Addressbook_Controller_Contact::getInstance()->get($_id);
753             $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $contact->account_id, 'Tinebase_Model_FullUser');
754         } else {
755             if ($this->_useLoginAsFolderName()) {
756                 $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountLoginName', $_id, 'Tinebase_Model_FullUser');
757             } else {
758                 $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountDisplayName', $_id, 'Tinebase_Model_FullUser');
759             }
760         }
761
762         self::$_classCache[__FUNCTION__][$classCacheId] = $user;
763
764         return $user;
765     }
766 }