0009846: allow download of record attachments
authorPhilipp Schüle <p.schuele@metaways.de>
Tue, 22 Jul 2014 12:08:44 +0000 (14:08 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 4 Sep 2014 09:26:16 +0000 (11:26 +0200)
* adds classes for handling webdav requests for applications records
* allows (non-anonymous) download of record attachments
* adds Calendar attachment download test
* some minor code and logging improvements

https://forge.tine20.org/mantisbt/view.php?id=9846

Change-Id: I71c888742314b854b552ee70a35a8441da41ef0d
Reviewed-on: http://gerrit.tine20.com/customers/522
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
tests/tine20/Calendar/Frontend/WebDAV/AllTests.php
tests/tine20/Calendar/JsonTests.php
tests/tine20/TestCase.php
tests/tine20/Tinebase/Frontend/AllTests.php
tests/tine20/Tinebase/Frontend/WebDAV/RecordTest.php [new file with mode: 0644]
tine20/Calendar/Frontend/WebDAV/Container.php
tine20/Setup/Controller.php
tine20/Tinebase/Frontend/WebDAV/Node.php
tine20/Tinebase/Frontend/WebDAV/Record.php [new file with mode: 0644]
tine20/Tinebase/Frontend/WebDAV/RecordCollection.php [new file with mode: 0644]
tine20/Tinebase/WebDav/Collection/Abstract.php

index 951347f..7355c8c 100644 (file)
@@ -8,15 +8,6 @@
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
-/**
- * Test helper
- */
-require_once dirname(dirname(dirname(dirname(__FILE__)))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-if (! defined('PHPUnit_MAIN_METHOD')) {
-    define('PHPUnit_MAIN_METHOD', 'Calendar_Frontend_WebDAV_AllTests::main');
-}
-
 class Calendar_Frontend_WebDAV_AllTests
 {
     public static function main ()
@@ -32,7 +23,3 @@ class Calendar_Frontend_WebDAV_AllTests
         return $suite;
     }
 }
-
-if (PHPUnit_MAIN_METHOD == 'Calendar_Frontend_WebDAV_AllTests::main') {
-    Calendar_Frontend_WebDAV_AllTests::main();
-}
index cdbce17..aa239d8 100644 (file)
@@ -1432,9 +1432,7 @@ class Calendar_JsonTests extends Calendar_TestCase
      */
     public function testAddAttachmentToRecurSeries()
     {
-        $tempFileBackend = new Tinebase_TempFile();
-        $tempFile = $tempFileBackend->createTempFile(dirname(dirname(__FILE__)) . '/Filemanager/files/test.txt');
-        
+        $tempFile = $this->_getTempFile();
         $recurSet = array_value('results', $this->testSearchRecuringIncludes());
         // update recurseries 
         $someRecurInstance = $recurSet[2];
index 2eb4141..f99d3b9 100644 (file)
@@ -4,7 +4,7 @@
  * 
  * @package     Tests
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2013-2014 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  */
 
@@ -310,4 +310,16 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
         
         return $xml;
     }
+    
+    /**
+     * get test temp file
+     * 
+     * @return Tinebase_TempFile
+     */
+    protected function _getTempFile()
+    {
+        $tempFileBackend = new Tinebase_TempFile();
+        $tempFile = $tempFileBackend->createTempFile(dirname(__FILE__) . '/Filemanager/files/test.txt');
+        return $tempFile;
+    }
 }
index a838518..701dc43 100644 (file)
@@ -4,19 +4,10 @@
  * 
  * @package     Tinebase
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2007-2010 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Matthias Greiling <m.greiling@metaways.de>
  */
 
-/**
- * Test helper
- */
-require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-if (! defined('PHPUnit_MAIN_METHOD')) {
-    define('PHPUnit_MAIN_METHOD', 'Tinebase_Frontend_AllTests::main');
-}
-
 class Tinebase_Frontend_AllTests
 {
     public static function main()
@@ -32,11 +23,8 @@ class Tinebase_Frontend_AllTests
         $suite->addTestSuite('Tinebase_Frontend_JsonTest');
         $suite->addTestSuite('Tinebase_Frontend_CliTest');
         $suite->addTestSuite('Tinebase_Frontend_HttpTest');
+        $suite->addTestSuite('Tinebase_Frontend_WebDAV_RecordTest');
         
         return $suite;
     }
 }
-
-if (PHPUnit_MAIN_METHOD == 'Tinebase_Frontend_AllTests::main') {
-    Tinebase_Frontend_AllTests::main();
-}
diff --git a/tests/tine20/Tinebase/Frontend/WebDAV/RecordTest.php b/tests/tine20/Tinebase/Frontend/WebDAV/RecordTest.php
new file mode 100644 (file)
index 0000000..2afe534
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Calendar
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ */
+
+/**
+ * Test class for Tinebase_Frontend_WebDAV_Record
+ */
+class Tinebase_Frontend_WebDAV_RecordTest extends TestCase
+{
+    /**
+     * testDownloadAttachment
+     */
+    public function testDownloadAttachment()
+    {
+        if (! Setup_Controller::getInstance()->isInstalled('Calendar')) {
+            $this->markTestSkipped('Calendar not installed');
+        }
+        
+        $event = new Calendar_Model_Event(array(
+            'summary'     => 'Wakeup',
+            'dtstart'     => '2009-03-25 06:00:00',
+            'dtend'       => '2009-03-25 06:15:00',
+        ));
+        $tempFile = $this->_getTempFile();
+        $event->attachments = array(array('tempFile' => array('id' => $tempFile->getId())));
+        $savedEvent = Calendar_Controller_Event::getInstance()->create($event);
+        
+        $this->assertTrue(isset($savedEvent->attachments) && count($savedEvent->attachments) === 1);
+        
+        // try to fetch attachment of event
+        $recordCollection = new Tinebase_Frontend_WebDAV_Record('Calendar/records/Calendar_Model_Event/' . $savedEvent->getId());
+        $file = $recordCollection->getChild($tempFile->name);
+        $this->assertEquals('text/plain', $file->getContentType());
+        $this->assertEquals(17, $file->getSize());
+    }
+}
index 43623ec..beae4d8 100644 (file)
@@ -107,20 +107,20 @@ class Calendar_Frontend_WebDAV_Container extends Tinebase_WebDav_Container_Abstr
                 )
             )
         ));
-    
+        
         /**
          * see http://forge.tine20.org/mantisbt/view.php?id=5122
          * we must use action 'sync' and not 'get' as
          * otherwise the calendar also return events the user only can see because of freebusy
          */
         $objects = $this->_getController()->search($filter, null, false, false, 'sync');
-    
+        
         $children = array();
-    
+        
         foreach ($objects as $object) {
             $children[] = $this->getChild($object);
         }
-    
+        
         return $children;
     }
     
index 0f48cee..110d8f1 100644 (file)
@@ -1832,6 +1832,10 @@ class Setup_Controller
             } else {
                 $this->_isFileSystemAvailable = $session->filesystemAvailable;
             }
+            Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Filesystem available: ' . ($result ? 'yes' : 'no'));
+            
+        } else {
+            $result = $session->filesystemAvailable;
         }
         
         return $this->_isFileSystemAvailable;
index 8fd0dc8..20e4617 100644 (file)
@@ -13,6 +13,8 @@
  * class to handle webdav requests for Tinebase
  * 
  * @package     Tinebase
+ * 
+ * @todo extend Tinebase_Frontend_WebDAV_Record? or maybe add a common ancestor
  */
 abstract class Tinebase_Frontend_WebDAV_Node implements Sabre\DAV\INode
 {
diff --git a/tine20/Tinebase/Frontend/WebDAV/Record.php b/tine20/Tinebase/Frontend/WebDAV/Record.php
new file mode 100644 (file)
index 0000000..2ebf7a4
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Tine 2.0
+ * 
+ * @package     Tinebase
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ * 
+ */
+
+/**
+ * class to handle webdav requests for Tine records
+ * 
+ * @package     Tinebase
+ */
+class Tinebase_Frontend_WebDAV_Record implements Sabre\DAV\ICollection
+{
+    protected $_record = null;
+    protected $_appName = null;
+    
+    /**
+     * the constructor
+     * 
+     * @param string $_path
+     * @throws Sabre\DAV\Exception\NotFound
+     */
+    public function __construct($path) 
+    {
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . ' Record path: ' . $path);
+        
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . ' ' . print_r(Sabre\DAV\URLUtil::splitPath($path), true));
+        
+        try {
+            list($appModel, $id) = Sabre\DAV\URLUtil::splitPath($path);
+            list($appName, $records, $model) = explode('/', $appModel);
+            $this->_record = Tinebase_Core::getApplicationInstance($appName, $model)->get($id);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            throw new Sabre\DAV\Exception\NotFound('Record ' . $path . ' not found');
+        }
+        
+        $this->_appName   = $appName;
+        $this->_path      = $path;
+    }
+    
+    /**
+     * return list of children
+     * @return array list of children (record attachments)
+     */
+    public function getChildren() 
+    {
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
+            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' path: ' . $this->_path);
+        
+        $children = array();
+        
+        // TODO throw exception?
+            
+        // Loop through record attachments / record data
+//         foreach ($this->_record->attachments as $attachment) {
+//             $children[] = $this->getChild($attachment->path);
+//         }
+        
+        return $children;
+    }
+    
+    /**
+     * get child by name
+     * 
+     * @param  string $name
+     * @throws Sabre\DAV\Exception\NotFound
+     * @return Tinebase_Frontend_WebDAV_File
+     */
+    public function getChild($path) 
+    {
+        $basePath = Tinebase_FileSystem::getInstance()->getApplicationBasePath($this->_appName, Tinebase_FileSystem::FOLDER_TYPE_RECORDS);
+        $filePath = preg_replace('@^' . $this->_appName . '/' . Tinebase_FileSystem::FOLDER_TYPE_RECORDS . '@', $basePath, $this->_path);
+        return new Tinebase_Frontend_WebDAV_File($filePath . '/' . $path);
+    }
+    
+    public function childExists($name) 
+    {
+        // TODO implement
+    }
+
+    /**
+     * Creates a new file in the directory
+     *
+     * @param string $name Name of the file
+     * @param resource|string $data Initial payload
+     * @return null|string
+     */
+    function createFile($name, $data = null)
+    {
+        // TODO throw exception?
+    }
+
+    /**
+     * Creates a new subdirectory
+     *
+     * @param string $name
+     * @return void
+     */
+    function createDirectory($name)
+    {
+        // TODO throw exception?
+    }
+
+    /**
+     * Deleted the current node
+     *
+     * @return void
+     */
+    function delete()
+    {
+        // TODO throw exception?
+    }
+    
+    /**
+     * Returns the name of the node.
+     *
+     * This is used to generate the url.
+     *
+     * @return string
+     * 
+     * @todo DRY (@see Tinebase_Frontend_WebDAV_Node::getName())
+     */
+    function getName()
+    {
+        list(, $basename) = Sabre\DAV\URLUtil::splitPath($this->_path);
+        
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
+            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' name: ' . $basename);
+        
+        return $basename;
+    }
+
+    /**
+     * Renames the node
+     *
+     * @param string $name The new name
+     * @return void
+     */
+    function setName($name)
+    {
+        // TODO throw exception?
+    }
+
+    /**
+     * Returns the last modification time, as a unix timestamp
+     *
+     * @return int
+     * 
+     * @todo DRY (@see Tinebase_Frontend_WebDAV_Node::getName())
+     */
+    function getLastModified()
+    {
+        if ($this->_record instanceof Tinebase_Model_Tree_Node) {
+            if ($this->_record->last_modified_time instanceof Tinebase_DateTime) {
+                $timestamp = $this->_record->last_modified_time->getTimestamp();
+            } else {
+                $timestamp = $this->_record->creation_time->getTimestamp();
+            }
+        } else {
+            $timestamp = Tinebase_DateTime::now()->getTimestamp();
+        }
+        
+        return $timestamp;
+    }
+}
diff --git a/tine20/Tinebase/Frontend/WebDAV/RecordCollection.php b/tine20/Tinebase/Frontend/WebDAV/RecordCollection.php
new file mode 100644 (file)
index 0000000..5fd0ba1
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Tine 2.0
+ * 
+ * @package     Tinebase
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ * 
+ */
+
+/**
+ * class to handle WebDAV record collection  tree
+ *
+ * @package     Tinebase
+ * @subpackage  Frontend
+ */
+class Tinebase_Frontend_WebDAV_RecordCollection extends Tinebase_WebDav_Collection_Abstract
+{
+    /**
+     * (non-PHPdoc)
+     * @see Sabre\DAV\Collection::getChild()
+     */
+    public function getChild($_name)
+    {
+        return new Tinebase_Frontend_WebDAV_Record($this->_path . '/' . $_name);
+    }
+    
+    /**
+     * Returns an array with all the child nodes
+     *
+     * @return Sabre\DAV\INode[]
+     */
+    function getChildren()
+    {
+        return array();
+    }
+}
index eef23aa..e918bbe 100644 (file)
@@ -126,22 +126,20 @@ abstract class Tinebase_WebDav_Collection_Abstract extends DAV\Collection implem
      */
     public function getChild($_name)
     {
-        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .' path: ' . $this->_path . ' name: ' . $_name);
+        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .' path: ' . $this->_path . ' name: ' . $_name . ' path parts: ' . count($this->_pathParts));
     
         switch (count($this->_pathParts)) {
             # path == ApplicationName
             # return personal and shared folder
             case 1:
-                if (!in_array($_name, array(Tinebase_Model_Container::TYPE_PERSONAL, Tinebase_Model_Container::TYPE_SHARED))) {
+                if (!in_array($_name, array(Tinebase_Model_Container::TYPE_PERSONAL, Tinebase_Model_Container::TYPE_SHARED, Tinebase_FileSystem::FOLDER_TYPE_RECORDS))) {
                     throw new Sabre\DAV\Exception\NotFound('Directory not found');
                 }
                 
                 $className = $this->_applicationName . '_Frontend_WebDAV';
                 return new $className($this->_path . '/' . $_name);
-        
-                break;
             
-            # path == ApplicationName/{personal|shared}
+            # path == ApplicationName/{personal|shared|records}
             # list container
             case 2:
                 if ($this->_pathParts[1] == Tinebase_Model_Container::TYPE_SHARED) {
@@ -157,6 +155,7 @@ abstract class Tinebase_WebDav_Collection_Abstract extends DAV\Collection implem
                     $objectClass = $this->_applicationName . '_Frontend_WebDAV_Container';
                     
                     return new $objectClass($container);
+                    
                 } elseif ($this->_pathParts[1] == Tinebase_Model_Container::TYPE_PERSONAL) {
                     if ($_name != Tinebase_Core::getUser()->accountLoginName && $_name != 'currentUser') {
                         throw new Sabre\DAV\Exception\NotFound('Child not found');
@@ -165,6 +164,11 @@ abstract class Tinebase_WebDav_Collection_Abstract extends DAV\Collection implem
                     $className = $this->_applicationName . '_Frontend_WebDAV';
                     
                     return new $className($this->_path . '/' . $_name);
+                    
+                } elseif ($this->_pathParts[1] == Tinebase_FileSystem::FOLDER_TYPE_RECORDS) {
+                    $className = 'Tinebase_Frontend_WebDAV_RecordCollection';
+                    
+                    return new $className($this->_path . '/' . $_name);
                 }
                 
                 break;
@@ -185,8 +189,6 @@ abstract class Tinebase_WebDav_Collection_Abstract extends DAV\Collection implem
                 
                 return new $objectClass($container);
                 
-                break;
-                
             default:
                 throw new Sabre\DAV\Exception\NotFound('Child not found');