6124: Add CalDAV client for scheduled calendar imports
authorMichael Spahn <m.spahn@metaways.de>
Mon, 8 Sep 2014 12:45:25 +0000 (14:45 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 16 Oct 2014 08:08:54 +0000 (10:08 +0200)
 - uses credential cache for login
 - import a caldav calender into Tine 2.0
 - add user agent for Tine 2.0 caldav client

TODO:
* test needs to be improved

Change-Id: I5b1cb4742b9c17cd2b6af726fe589a789621d436
Reviewed-on: http://gerrit.tine20.com/customers/1105
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Jenkins CI (http://ci.tine20.com/)
15 files changed:
.gitignore
tests/tine20/Calendar/JsonTests.php
tests/tine20/TestServer.php
tine20/Calendar/Convert/Event/VCalendar/Abstract.php
tine20/Calendar/Convert/Event/VCalendar/Factory.php
tine20/Calendar/Convert/Event/VCalendar/Tine.php [new file with mode: 0644]
tine20/Calendar/Frontend/Json.php
tine20/Calendar/Frontend/WebDAV/Event.php
tine20/Calendar/Import/CalDAV.php [new file with mode: 0644]
tine20/Calendar/Import/CalDav/Client.php
tine20/Calendar/Import/CalDav/Decorator/Generic.php [new file with mode: 0644]
tine20/Calendar/Import/Ical.php
tine20/Calendar/js/ImportDialog.js
tine20/Tinebase/Auth/CredentialCache.php
tine20/Tinebase/Controller/ScheduledImport.php

index 4d02f88..94bc4bd 100644 (file)
@@ -1,3 +1,4 @@
+.idea
 .buildpath
 .externalToolBuilders
 .project
index b1bc51f..3fdd0a2 100644 (file)
@@ -1489,4 +1489,49 @@ class Calendar_JsonTests extends Calendar_TestCase
         $event = $this->_uit->saveEvent($eventData);
         $this->assertEquals(1, $event['attendee'][1]['quantity'], 'The invalid quantity should be saved as 1');
     }
+
+    /**
+     * trigger caldav import by json frontend
+     * 
+     * @todo use mock as fallback (if server can not be reached by curl)
+     * @todo get servername from unittest config / skip or mock if no servername found
+     */
+    public function testCalDAVImport()
+    {
+        // Skip if tine20.com.local could not be resolved
+        if (gethostbyname('tine20.com.local') == 'tine20.com.local') {
+            $this->markTestSkipped('Can\'t perform test, because instance is not reachable.');
+        }
+
+        $this->_testNeedsTransaction();
+        
+        $event = $this->testCreateEvent(/* $now = */ true);
+        
+        $fe = new Calendar_Frontend_Json();
+        $testUserCredentials = TestServer::getInstance()->getTestCredentials();
+        $fe->importRemoteEvents(
+            'http://tine20.com.local/calendars/' . Tinebase_Core::getUser()->contact_id . '/' . $event['container_id']['id'],
+            Tinebase_Model_Import::INTERVAL_DAILY,
+            array(
+                'container_id'          => 'remote_caldav_calendar',
+                'sourceType'            => 'remote_caldav',
+                'importFileByScheduler' => false,
+                'allowDuplicateEvents'  => true,
+                'username'              => $testUserCredentials['username'],
+                'password'              => $testUserCredentials['password'],
+            ));
+
+        $importScheduler = Tinebase_Controller_ScheduledImport::getInstance();
+        $record = $importScheduler->runNextScheduledImport();
+
+        $container = Tinebase_Container::getInstance()->getContainerByName('Calendar', 'remote_caldav_calendar', Tinebase_Model_Container::TYPE_PERSONAL, Tinebase_Core::getUser()->getId());
+        $this->_testCalendars[] = $container;
+        $this->assertTrue($container instanceof Tinebase_Model_Container, 'Container was not created');
+
+        $this->assertNotEquals($record, null, 'The import could not start!');
+        
+        $filter = $this->_getEventFilterArray($container->getId());
+        $result = $this->_uit->searchEvents($filter, array());
+        $this->assertEquals(1, $result['totalcount']);
+    }
 }
index cb85d8a..c4ed0ec 100644 (file)
@@ -259,12 +259,29 @@ class TestServer
     public function login()
     {
         $tinebaseController = Tinebase_Controller::getInstance();
+        $credentials = $this->getTestCredentials();
+        
         $config = $this->getConfig();
-        $username = isset($config->login->username) ? $config->login->username : $config->username;
-        $password = isset($config->login->password) ? $config->login->password : $config->password;
         $ip = $config->ip ? $config->ip : '127.0.0.1';
-        if (! $tinebaseController->login($username, $password, $ip, 'TineUnittest')){
+        if (! $tinebaseController->login($credentials['username'], $credentials['password'], $ip, 'TineUnittest')){
             throw new Exception("Couldn't login, user session required for tests! \n");
         }
     }
+    
+    /**
+     * fetch test user credentials
+     * 
+     * @return array
+     */
+    public function getTestCredentials()
+    {
+        $config = $this->getConfig();
+        $username = isset($config->login->username) ? $config->login->username : $config->username;
+        $password = isset($config->login->password) ? $config->login->password : $config->password;
+        
+        return array(
+            'username' => $username,
+            'password' => $password
+        );
+    }
 }
index fc1ee17..d253c1a 100644 (file)
@@ -942,7 +942,6 @@ class Calendar_Convert_Event_VCalendar_Abstract extends Tinebase_Convert_VCalend
                     if($readFromURL) {
                         if (preg_match('#^(https?://)(.*)$#', str_replace(array("\n","\r"), '', $url), $matches)) {
                             // we are client and found an external hosted attachment that we need to import
-                            $user = Tinebase_Core::getUser();
                             $userCredentialCache = Tinebase_Core::get(Tinebase_Core::USERCREDENTIALCACHE);
                             $url = $matches[1] . $userCredentialCache->username . ':' . $userCredentialCache->password . '@' . $matches[2];
                             $attachmentInfo = $matches[1] . $matches[2]. ' ' . $name . ' ' . $managedId;
index c792c8a..07609fc 100644 (file)
@@ -23,6 +23,7 @@ class Calendar_Convert_Event_VCalendar_Factory
     const CLIENT_MACOSX      = 'macosx';
     const CLIENT_THUNDERBIRD = 'thunderbird';
     const CLIENT_EMCLIENT    = 'emclient';
+    const CLIENT_TINE        = 'tine';
     
     /**
      * factory function to return a selected vcalendar backend class
@@ -36,34 +37,30 @@ class Calendar_Convert_Event_VCalendar_Factory
         switch ($_backend) {
             case Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC:
                 return new Calendar_Convert_Event_VCalendar_Generic($_version);
-                
                 break;
                 
             case Calendar_Convert_Event_VCalendar_Factory::CLIENT_IPHONE:
                 return new Calendar_Convert_Event_VCalendar_Iphone($_version);
-                
                 break;
                 
             case Calendar_Convert_Event_VCalendar_Factory::CLIENT_KDE:
                 return new Calendar_Convert_Event_VCalendar_KDE($_version);
-                
                 break;
                 
             case Calendar_Convert_Event_VCalendar_Factory::CLIENT_MACOSX:
                 return new Calendar_Convert_Event_VCalendar_MacOSX($_version);
-                
                 break;
                 
             case Calendar_Convert_Event_VCalendar_Factory::CLIENT_THUNDERBIRD:
                 return new Calendar_Convert_Event_VCalendar_Thunderbird($_version);
-                 
                 break;
  
             case Calendar_Convert_Event_VCalendar_Factory::CLIENT_EMCLIENT:
                 return new Calendar_Convert_Event_VCalendar_EMClient($_version);
-                 
                 break;
+
+            case Calendar_Convert_Event_VCalendar_Factory::CLIENT_TINE:
+                return new Calendar_Convert_Event_VCalendar_Tine($_version);
         }
     }
     
@@ -98,7 +95,13 @@ class Calendar_Convert_Event_VCalendar_Factory
         } elseif (preg_match(Calendar_Convert_Event_VCalendar_EMClient::HEADER_MATCH, $_userAgent, $matches)) {
             $backend = Calendar_Convert_Event_VCalendar_Factory::CLIENT_EMCLIENT;
             $version = $matches['version'];
-        
+
+        // Tine 2.0
+        } elseif (preg_match(Calendar_Convert_Event_VCalendar_Tine::HEADER_MATCH, $_userAgent, $matches)) {
+            $backend = Calendar_Convert_Event_VCalendar_Factory::CLIENT_TINE;
+            $version = $matches['version'];
+
+
         } else {
             $backend = Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC;
             $version = null;
diff --git a/tine20/Calendar/Convert/Event/VCalendar/Tine.php b/tine20/Calendar/Convert/Event/VCalendar/Tine.php
new file mode 100644 (file)
index 0000000..4d1fdc6
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Calendar
+ * @subpackage  Convert
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+/**
+ * class to convert a Tine 2.0 VCALENDAR to Tine 2.0 Calendar_Model_Event and back again
+ *
+ * @package     Calendar
+ * @subpackage  Convert
+ */
+class Calendar_Convert_Event_VCalendar_Tine extends Calendar_Convert_Event_VCalendar_Abstract
+{
+    const HEADER_MATCH = '/Tine20\/(?P<version>.+)/';
+    
+    protected $_supportedFields = array(
+        'seq',
+        'dtend',
+        'transp',
+        'class',
+        'description',
+        'location',
+        'priority',
+        'summary',
+        'url',
+        'alarms',
+        'tags',
+        'dtstart',
+        'exdate',
+        'rrule',
+        'recurid',
+        'is_all_day_event',
+        'originator_tz'
+    );
+}
index b33f8d1..ca8d20b 100644 (file)
@@ -172,15 +172,44 @@ class Calendar_Frontend_Json extends Tinebase_Frontend_Json_Abstract
      */
     public function importRemoteEvents($remoteUrl, $interval, $importOptions)
     {
+        // Determine which plugin should be used to import
+        switch ($importOptions['sourceType']) {
+            case 'remote_caldav':
+                $plugin = 'Calendar_Import_CalDAV';
+                break;
+            default:
+                $plugin = 'Calendar_Import_Ical';
+        }
+
+        $credentialCache = Tinebase_Auth_CredentialCache::getInstance();
+        $credentials = $credentialCache->cacheCredentials(
+            $importOptions['username'],
+            $importOptions['password'],
+            null,
+            /* persist */       true,
+            /* valid until */   Tinebase_DateTime::now()->addYear(100)
+        );
+
         $record = Tinebase_Controller_ScheduledImport::getInstance()->createRemoteImportEvent(array(
             'source'            => $remoteUrl,
             'interval'          => $interval,
-            'options'           => array_replace($importOptions, array('plugin' => 'Calendar_Import_Ical')),
+            'options'           => array_replace($importOptions, array(
+                'plugin' => $plugin,
+                'importFileByScheduler' => $importOptions['sourceType'] != 'remote_caldav',
+                'cid' => $credentials->getId(),
+                'ckey' => $credentials->key
+            )),
             'model'             => 'Calendar_Model_Event',
+            'user_id'           => Tinebase_Core::getUser()->getId(),
             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
         ));
-        
-        return $this->_recordToJson($record);
+
+        Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . print_r($record->options, true));
+
+        $result = $this->_recordToJson($record);
+        Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . 'container_id:'  .  print_r($result['container_id'], true));
+
+        return $result;
     }
     
     /**
index de7b476..f1c13f9 100644 (file)
@@ -58,7 +58,12 @@ class Calendar_Frontend_WebDAV_Event extends Sabre\DAV\File implements Sabre\Cal
             $this->_event = ($pos = strpos($this->_event, '.')) === false ? $this->_event : substr($this->_event, 0, $pos);
         }
         
-        list($backend, $version) = Calendar_Convert_Event_VCalendar_Factory::parseUserAgent($_SERVER['HTTP_USER_AGENT']);
+        if (isset($_SERVER['HTTP_USER_AGENT'])) {
+            list($backend, $version) = Calendar_Convert_Event_VCalendar_Factory::parseUserAgent($_SERVER['HTTP_USER_AGENT']);
+        } else {
+            $backend = Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC;
+            $version = null;
+        }
         
         $this->_assertEventFacadeParams($this->_container);
         
diff --git a/tine20/Calendar/Import/CalDAV.php b/tine20/Calendar/Import/CalDAV.php
new file mode 100644 (file)
index 0000000..44dd6ae
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Tine 2.0
+ * 
+ * @package     Calendar
+ * @subpackage  Import
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+/**
+ * Calendar_Import_CalDAV
+ * 
+ * @package     Calendar
+ * @subpackage  Import
+ */
+class Calendar_Import_CalDAV extends Tinebase_Import_Abstract
+{
+    /**
+     * config options
+     * 
+     * @var array
+     */
+    protected $_options = array(
+        /**
+         * force update of existing events 
+         * @var boolean
+         */
+        'updateExisting'        => true,
+        /**
+         * updates exiting events if sequence number is higher
+         * @var boolean
+         */
+        'forceUpdateExisting'   => false,
+        /**
+         * container the events should be imported in
+         * @var string
+         */
+        'container_id'     => null,
+        
+        /**
+         * Model to be used for import
+         * @var string
+         */
+        'model' => 'Calendar_Model_Event'
+    );
+    
+    protected $_calDAVClient = null;
+
+    /**
+     * creates a new importer from an importexport definition
+     * 
+     * @param  Tinebase_Model_ImportExportDefinition $_definition
+     * @param  array                                 $_options
+     * @return Calendar_Import_CalDAV
+     */
+    public static function createFromDefinition(Tinebase_Model_ImportExportDefinition $_definition, array $_options = array())
+    {
+        return new Calendar_Import_CalDAV(self::getOptionsArrayFromDefinition($_definition, $_options));
+    }
+
+    /**
+     * Splits Uri into protocol + host, path
+     *
+     * @param $uri
+     * @return array
+     */
+    protected function _splitUri($uri)
+    {
+        $uri = parse_url($uri);
+
+        return array(
+            'host' => $uri['scheme'] . '://' . $uri['host'],
+            'path' => $uri['path']
+        );
+    }
+
+    /**
+     * import the data
+     */
+    public function import($_resource = NULL, $_clientRecordData = array())
+    {
+        $_resource['options'] = Zend_Json::decode($_resource['options']);
+
+        $credentials = new Tinebase_Model_CredentialCache(array(
+            'id'    => $_resource['options']['cid'],
+            'key'   => $_resource['options']['ckey']
+        ));
+        Tinebase_Auth_CredentialCache::getInstance()->getCachedCredentials($credentials);
+
+        $uri = $this->_splitUri($_resource['remoteUrl']);
+
+        $caldavClientOptions = array(
+            'baseUri' => $uri['host'],
+            'calenderUri' => $uri['path'],
+            'userName' => $credentials->username,
+            'password' => $credentials->password,
+            'allowDuplicateEvents' => $_resource['options']['allowDuplicateEvents'],
+        );
+
+        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . ' Trying to get calendar container Name:' . $_resource['options']['container_id']);
+        $container = Tinebase_Container::getInstance()->getContainerById($this->_getImportCalendarByName($_resource['options']['container_id']));
+        
+        if ($container === false) {
+            throw new Tinebase_Exception('Could not import, aborting ..');
+            return false;
+        }
+
+        $credentialCache = Tinebase_Auth_CredentialCache::getInstance()->cacheCredentials($credentials->username, $credentials->password);
+        Tinebase_Core::set(Tinebase_Core::USERCREDENTIALCACHE, $credentialCache);
+
+        $this->_calDAVClient = new Calendar_Import_CalDav_Client($caldavClientOptions, 'Generic', $container->name);
+        $this->_calDAVClient->setVerifyPeer(false);
+        $this->_calDAVClient->getDecorator()->initCalendarImport();
+
+        $this->_calDAVClient->updateAllCalendarData();
+    }
+    
+    /**
+     * Return container id of newly created container for import
+     * 
+     *  - the given container name is not allowed to exist
+     * 
+     * @param type $containerName
+     * @return boolean
+     * @throws Tinebase_Exception_InvalidArgument
+     */
+    protected function _getImportCalendarByName ($containerName)
+    {
+        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . ' Get calendar by name for CalDAV import. Name:' . $containerName);
+
+        $filter = new Tinebase_Model_ContainerFilter(array(
+            array(
+                'field' => 'name', 
+                'operator' => 'equals', 
+                'value' => $containerName
+            ),
+            array(
+                'field' => 'application_id',
+                'operator' => 'equals',
+                'value' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()
+            )
+        ));
+        $search = Tinebase_Container::getInstance()->search($filter);
+
+        if ($search->count() > 0) {
+            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+                . ' Calendar already exists ' . $containerName . ' ID: ' . $search->getFirstRecord()->getId());
+            return $search->getFirstRecord()->getId();
+        }
+            
+        $container = new Tinebase_Model_Container(array(
+            'name'              => $containerName,
+            'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
+            'backend'           => Tinebase_User::SQL,
+            'color'             => '#ff0000',
+            'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
+            'owner_id'          => Tinebase_Core::getUser()->getId(),
+            'model'             => 'Calendar_Model_Event',
+        ));
+
+        $container = Tinebase_Container::getInstance()->addContainer($container);
+
+        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . ' Added calendar ' . $containerName . ' with ID: ' . $container->getId());
+
+        return $container->getId();
+    }
+}
index 2b26499..2bdcb8f 100644 (file)
@@ -33,6 +33,10 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
     protected $webdavFrontend = 'Calendar_Frontend_WebDAV_Event';
     protected $_uuidPrefix = '';
     
+    protected $_enforceContainerName = null;
+    protected $_calToImport = null;
+    protected $_allowDuplicateEvents = false;
+    
     /**
      * skip those ics
      * 
@@ -80,10 +84,27 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
   </a:prop>
 ';
     
-    public function __construct(array $a, $flavor)
+    public function __construct(array $a, $flavor, $forceContainerByName = null)
     {
         parent::__construct($a);
         
+        $this->_enforceContainerName = $forceContainerByName;
+
+        if (isset($a['calenderUri'])) {
+            $this->calendarHomeSet = $a['calenderUri'];
+            //$this->calendarICSs = array($a['calenderUri']);
+            $this->calendars = array(
+                $a['calenderUri'] => array(
+                    'displayname' => $forceContainerByName
+                    // @todo add more properties?
+                )
+            );
+        }
+        
+        if (isset($a['allowDuplicateEvents'])) {
+            $this->_allowDuplicateEvents = $a['allowDuplicateEvents'];
+        }
+
         $flavor = 'Calendar_Import_CalDav_Decorator_' . $flavor;
         $this->decorator = new $flavor($this);
     }
@@ -105,7 +126,7 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
             Tinebase_Exception::log($te);
             return false;
         }
-        
+
         foreach ($result as $uri => $response) {
             if (isset($response['{DAV:}resourcetype']) &&
                     isset($response['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']) && 
@@ -113,7 +134,9 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
                     in_array($this->component, $response['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue())
             ) {
                 $this->calendars[$uri]['acl'] = $response['{DAV:}acl'];
-                $this->calendars[$uri]['displayname'] = $response['{DAV:}displayname'];
+                // The container is identified by this displayname, to define an own calendar we need to overwrite it here
+                //  otherwise use the dav displayname
+                $this->calendars[$uri]['displayname'] = $this->_enforceContainerName ?: $response['{DAV:}displayname'];
                 $this->decorator->processAdditionalCalendarProperties($this->calendars[$uri], $response);
                 $this->resolvePrincipals($this->calendars[$uri]['acl']->getPrivileges());
             }
@@ -277,7 +300,7 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
      */
     protected function _getDefaultCalendarsName() 
     {
-        $defaultCalendarsName = '';
+        $defaultCalendarsName = $this->_enforceContainerName ?: '';
         foreach ($this->calendarICSs as $calUri => $calICSs) {
             if ($this->mapToDefaultContainer == $this->calendars[$calUri]['displayname']) {
                 return $this->calendars[$calUri]['displayname'];
@@ -417,10 +440,12 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
                 
             } else {
                 try {
-                    $recordBackend->checkETag($data['id'], $data['etag']);
-                    if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' '
-                            . ' Ignoring delegated event from another container/organizer: ' . $data['id']);
-                    continue;
+                    if (! $this->_allowDuplicateEvents) {
+                        $recordBackend->checkETag($data['id'], $data['etag']);
+                        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' '
+                                . ' Ignoring event from another container/organizer: ' . $data['id']);
+                        continue;
+                    }
                 } catch (Tinebase_Exception_NotFound $tenf) {
                     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' '
                             . ' Found new record: ' . $data['id']);
@@ -882,4 +907,9 @@ class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
         $this->calendars = array();
         $this->calendarICSs = array();
     }
+
+    public function getDecorator()
+    {
+        return $this->decorator;
+    }
 }
diff --git a/tine20/Calendar/Import/CalDav/Decorator/Generic.php b/tine20/Calendar/Import/CalDav/Decorator/Generic.php
new file mode 100644 (file)
index 0000000..5143a60
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Generic decorator for caldav
+ *
+ * Uses Calendar_Convert_Event_VCalendar_Tine for import.
+ */
+class Calendar_Import_CalDav_Decorator_Generic extends Calendar_Import_CalDav_Decorator_Abstract
+{
+    public function initCalendarImport()
+    {
+        $tbjf = new Tinebase_Frontend_Json();
+        $registry = $tbjf->getRegistryData();
+
+        $_SERVER['HTTP_USER_AGENT'] = 'Tine20/' . $registry['version']['packageString'];
+    }
+}
\ No newline at end of file
index 2b2d5bb..0793b59 100644 (file)
@@ -136,7 +136,7 @@ class Calendar_Import_Ical extends Tinebase_Import_Abstract
                 'color'             => '#ffffff',
                 'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
                 'owner_id'          => Tinebase_Core::getUser()->getId(),
-                'model'             => 'Calendar_Model_Event',
+                'model'             => 'Calendar_Model_Event'
             ));
             $container = Tinebase_Container::getInstance()->addContainer($container);
         }
index 2fa565d..67a3c15 100644 (file)
@@ -49,10 +49,19 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
         
         var params = {
             importOptions: Ext.apply({
-                container_id: targetContainer
+                container_id: targetContainer,
+                sourceType: this.typeCombo.getValue(),
+                importFileByScheduler: (this.typeCombo.getValue() == 'remote_caldav') ? true : false
             }, importOptions || {})
         };
-        
+
+        if (this.typeCombo.getValue() == 'remote_caldav') {
+            params.importOptions = Ext.apply({
+                password: this.remotePassword.getValue(),
+                username: this.remoteUsername.getValue()
+            }, params.importOptions);
+        }
+
         if (type == 'upload') {
             params = Ext.apply(params, {
                 clientRecordData: clientRecordData,
@@ -167,7 +176,7 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
             id: 'remotePanel',
             hidden: false,
             title: this.app.i18n._('Choose Remote Location'),
-            height: 150,
+            //height: 230,
             items: [{
                 xtype: 'label',
                 html: '<p>' + this.app.i18n._('Please choose a remote location you want to add to Tine 2.0').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
@@ -185,6 +194,39 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
                 }
             }, {
                 xtype: 'label',
+                html: '<p><br />' + this.app.i18n._('Username (CalDAV only)') + '</p><br />'
+            }, {
+                ref: '../../remoteUsername',
+                xtype: 'textfield',
+                scope: this,
+                disabled: true,
+                enableKeyEvents: true,
+                width: 400,
+                listeners: {
+                    scope: this,
+                    keyup: function() {
+                        this.manageButtons();
+                    }
+                }
+            }, {
+                xtype: 'label',
+                html: '<p><br />' + this.app.i18n._('Password (CalDAV only)') + '</p><br />'
+            }, {
+                ref: '../../remotePassword',
+                xtype: 'textfield',
+                inputType: 'password',
+                scope: this,
+                disabled: true,
+                enableKeyEvents: true,
+                width: 400,
+                listeners: {
+                    scope: this,
+                    keyup: function() {
+                        this.manageButtons();
+                    }
+                }
+            }, {
+                xtype: 'label',
                 html: '<p><br />' + this.app.i18n._('Refresh time').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
             }, {
                 xtype: 'combo',
@@ -231,7 +273,6 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
                     id: this.app.appName + 'ContainerName',
                     xtype: 'textfield',
                     ref: '../../../containerField',
-                    disabled: false,
                     enableKeyEvents: true,
                     listeners: {
                         scope: this,
@@ -338,7 +379,7 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
         
         var types = [
             ['remote_ics', this.app.i18n._('Remote / ICS')],
-//            ['remote_caldav', _('Remote / CALDav')],
+            ['remote_caldav', _('Remote / CALDav')],
             ['upload', this.app.i18n._('Upload')]
         ]
         
@@ -382,16 +423,19 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
                                 Ext.getCmp('uploadPanel').hide();
                                 Ext.getCmp('definitionPanel').hide();
                                 Ext.getCmp('remotePanel').show();
+                                if (combo.getValue() == 'remote_caldav') {
+                                    this.remoteUsername.enable();
+                                    this.remotePassword.enable();
+                                } else {
+                                    this.remoteUsername.disable();
+                                    this.remotePassword.disable();
+                                }
                             }
                             
                             this.doLayout();
                             this.manageButtons();
                         },
                         'render': function (combo) {
-                            /**
-                            * @todo enable and allow remotes
-                            *  ~mspahn
-                            */
                             combo.setValue('upload');
                             Ext.getCmp('uploadPanel').show();
                             Ext.getCmp('definitionPanel').show();
@@ -436,14 +480,22 @@ Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
                 }, importOptions, clientRecordData);
             }).createDelegate(this),
 
-            /**
-             * @todo fix later
-             */
             finishIsAllowed: (function() {
+                var credentialsCheck = false;
+
+                if (this.typeCombo.getValue() == 'remote_caldav') {
+                    if (this.remoteUsername.getValue() != '' && this.remotePassword.getValue() != '') {
+                        credentialsCheck = true;
+                    }
+                } else if (this.typeCombo.getValue() == 'remote_ics') {
+                    credentialsCheck = true;
+                }
+
                 return (
                     ((this.typeCombo && (this.typeCombo.getValue() == 'remote_ics' || this.typeCombo.getValue() == 'remote_caldav'))
                     && (this.remoteLocation && this.remoteLocation.getValue())
                     && (this.ttlCombo && (this.ttlCombo.getValue() || this.ttlCombo.getValue() === 0))))
+                    && credentialsCheck
                     || ((this.typeCombo && (this.typeCombo.getValue() == 'upload'))
                     && (this.definitionCombo && this.definitionCombo.getValue())
                     && (this.uploadButton && this.uploadButton.upload))
index 82dffcb..71a6a98 100644 (file)
@@ -123,7 +123,7 @@ class Tinebase_Auth_CredentialCache extends Tinebase_Backend_Sql_Abstract implem
      * @param  boolean $_saveInSession
      * @return Tinebase_Model_CredentialCache
      */
-    public function cacheCredentials($username, $password, $key = NULL, $persist = FALSE)
+    public function cacheCredentials($username, $password, $key = NULL, $persist = FALSE, $validUntil = null)
     {
         $key = ($key !== NULL) ? $key : $this->_cacheAdapter->getDefaultKey();
         
@@ -133,7 +133,7 @@ class Tinebase_Auth_CredentialCache extends Tinebase_Backend_Sql_Abstract implem
             'username'      => $username,
             'password'      => $password,
             'creation_time' => Tinebase_DateTime::now(),
-            'valid_until'   => Tinebase_DateTime::now()->addMonth(1)
+            'valid_until'   => $validUntil ?: Tinebase_DateTime::now()->addMonth(1)
         ), true, false);
         $cache->convertDates = true;
         
index f728a82..815069e 100644 (file)
@@ -139,6 +139,22 @@ class Tinebase_Controller_ScheduledImport extends Tinebase_Controller_Record_Abs
     }
     
     /**
+     * Downloads file to memory
+     * 
+     * @param String $source
+     * @return String
+     */
+    protected function _getFileToImport($source)
+    {
+        if (strpos($source, 'http') === 0) {
+            $client = new Zend_Http_Client($source);
+            return $client->request()->getBody();
+        } else {
+            return file_get_contents($source);
+        }
+    }
+    
+    /**
      * Execute scheduled import
      * @param Tinebase_Model_Import $record
      * @return Tinebase_Model_Import
@@ -158,15 +174,14 @@ class Tinebase_Controller_ScheduledImport extends Tinebase_Controller_Record_Abs
             'importContainerId' => $record->container_id,
         );
         
-        try {
-            if (strpos($record->source, 'http') === 0) {
-                $client = new Zend_Http_Client($record->source);
-                $toImport = $client->request()->getBody();
-            } else {
-                $toImport = file_get_contents($record->source);
-            }
-        } catch (Exception $e) {
-            throw new Tinebase_Exception_NotFound('Could not load file to import: ' . $record->source);
+        if ($record->getOption('importFileByScheduler') === true) {
+            $toImport = $this->_getFileToImport($record->source);
+        } else {
+            $toImport = array(
+                'remoteUrl'    => $record->source,
+                'container_id' => $record->container_id,
+                'options'      => $record->options
+            );
         }
         
         $importer = new $importer($options);
@@ -227,18 +242,6 @@ class Tinebase_Controller_ScheduledImport extends Tinebase_Controller_Record_Abs
             $data['interval'] = Tinebase_Model_Import::INTERVAL_ONCE;
         }
         
-        $url = filter_var($data['source'], FILTER_VALIDATE_URL);
-        if ($url) {
-            if (strpos($data['source'], 'http') === 0) {
-                $client = new Zend_Http_Client($data['source']);
-                $toImport = $client->request()->getBody();
-            } else {
-                $toImport = file_get_contents($data['source']);
-            }
-        } else {
-            throw new Calendar_Exception_InvalidUrl();
-        }
-        
         // find container or create a new one
         $containerId = $data['options']['container_id'];