diff --git a/.gitignore b/.gitignore
index e2ff07d14d8109ef278b1e89b78191933cb418c8..ae636931706f08267bb177a07215387d55f4e2ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,9 @@ RCS/*
 # netbeans 
 nbproject
 
+# phpStorm
+.idea
+
 # geany
 *.geany
 
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..0f4ad588071602da7c1f39b2b4cab095ac206d8e
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "3rdparty/Symfony/Component/Routing"]
+	path = 3rdparty/Symfony/Component/Routing
+	url = git://github.com/symfony/Routing.git
diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
old mode 100644
new mode 100755
index 1bb6b5d53faf4f6848d2675c701554558d525269..4bcd32cdf885c55af54ee023acc232506bd79143
--- a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
+++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
@@ -294,6 +294,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
                 // in the VALARM component code, so this is a hack, and an
                 // expensive one too.
                 if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
+
                     // Fire up the iterator!
                     $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
                     while($it->valid()) {
@@ -304,14 +305,35 @@ class Sabre_CalDAV_CalendarQueryValidator {
                         // determine if we can 'give up' expanding events.
                         $firstAlarm = null;
                         foreach($expandedEvent->VALARM as $expandedAlarm) {
+
                             $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
-                            if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
-                                $firstAlarm = $effectiveTrigger;
-                            }
                             if ($expandedAlarm->isInTimeRange($start, $end)) {
                                 return true;
                             }
 
+                            if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
+                                // This is an alarm with a non-relative trigger
+                                // time, likely created by a buggy client. The
+                                // implication is that every alarm in this
+                                // recurring event trigger at the exact same
+                                // time. It doesn't make sense to traverse
+                                // further.
+                            } else {
+                                // We store the first alarm as a means to
+                                // figure out when we can stop traversing.
+                                if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
+                                    $firstAlarm = $effectiveTrigger;
+                                }
+                            }
+
+                        }
+                        if (is_null($firstAlarm)) {
+                            // No alarm was found.
+                            //
+                            // Or technically: No alarm that will change for
+                            // every instance of the recurrence was found,
+                            // which means we can assume there was no match.
+                            return false;
                         }
                         if ($firstAlarm > $end) {
                             return false;
diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php
old mode 100644
new mode 100755
index d7d1d970518a8f32e1cdcfe9feed27a930804b6e..5903968c003af3363afc9640f0387215ea6cad25
--- a/3rdparty/Sabre/CalDAV/Plugin.php
+++ b/3rdparty/Sabre/CalDAV/Plugin.php
@@ -672,6 +672,42 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
 
         }
 
+        if ($vobj->name !== 'VCALENDAR') {
+            throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
+        }
+
+        $foundType = null;
+        $foundUID = null;
+        foreach($vobj->getComponents() as $component) {
+            switch($component->name) {
+                case 'VTIMEZONE' :
+                    continue 2;
+                case 'VEVENT' :
+                case 'VTODO' :
+                case 'VJOURNAL' :
+                    if (is_null($foundType)) {
+                        $foundType = $component->name;
+                        if (!isset($component->UID)) {
+                            throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
+                        }
+                        $foundUID = (string)$component->UID;
+                    } else {
+                        if ($foundType !== $component->name) {
+                            throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
+                        }
+                        if ($foundUID !== (string)$component->UID) {
+                            throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
+                        }
+                    }
+                    break;
+                default :
+                    throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
+
+            }
+        }
+        if (!$foundType)
+            throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
+
     }
 
     /**
diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php
old mode 100644
new mode 100755
index 939e903c89f87830b572678b5619f413127c0f78..289a0c83a343b9db916295595001efd1673c47fe
--- a/3rdparty/Sabre/CalDAV/Version.php
+++ b/3rdparty/Sabre/CalDAV/Version.php
@@ -14,7 +14,7 @@ class Sabre_CalDAV_Version {
     /**
      * Full version number
      */
-    const VERSION = '1.6.2';
+    const VERSION = '1.6.3';
 
     /**
      * Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/AddressBook.php b/3rdparty/Sabre/CardDAV/AddressBook.php
old mode 100644
new mode 100755
index 3b381e1eea3a27073fe0b28e60ca777ef16e3039..12297175a85327aef5c69c665fd7047cef0f63ae
--- a/3rdparty/Sabre/CardDAV/AddressBook.php
+++ b/3rdparty/Sabre/CardDAV/AddressBook.php
@@ -108,7 +108,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca
      */
     public function createFile($name,$vcardData = null) {
 
-        $vcardData = stream_get_contents($vcardData);
+        if (is_resource($vcardData)) {
+            $vcardData = stream_get_contents($vcardData);
+        }
         // Converting to UTF-8, if needed
         $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
 
diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
old mode 100644
new mode 100755
index 85a4963127bff4ef3c1e227525ffdcddff7e01e6..46bb8ff18dd48847ae94bec334d605a96b021962
--- a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
+++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
@@ -9,7 +9,7 @@
  * @package Sabre
  * @subpackage CardDAV
  * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_CardDAV_AddressBookQueryParser {
@@ -88,12 +88,22 @@ class Sabre_CardDAV_AddressBookQueryParser {
         if (is_nan($limit)) $limit = null;
 
         $filter = $this->xpath->query('/card:addressbook-query/card:filter');
-        if ($filter->length !== 1) {
+
+        // According to the CardDAV spec there needs to be exactly 1 filter
+        // element. However, KDE 4.8.2 contains a bug that will encode 0 filter
+        // elements, so this is a workaround for that.
+        //
+        // See: https://bugs.kde.org/show_bug.cgi?id=300047
+        if ($filter->length === 0) {
+            $test = null;
+            $filter = null;
+        } elseif ($filter->length === 1) {
+            $filter = $filter->item(0);
+            $test = $this->xpath->evaluate('string(@test)', $filter);
+        } else {
             throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
         }
 
-        $filter = $filter->item(0);
-        $test = $this->xpath->evaluate('string(@test)', $filter);
         if (!$test) $test = self::TEST_ANYOF;
         if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
             throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
diff --git a/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Backend/Abstract.php b/3rdparty/Sabre/CardDAV/Backend/Abstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Backend/PDO.php b/3rdparty/Sabre/CardDAV/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/IDirectory.php b/3rdparty/Sabre/CardDAV/IDirectory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php
old mode 100644
new mode 100755
index 9ebec243eb0c88ee4f569ccf85e0a5aeedc42e4b..ca20e46849755fb138d763135403e7a96aaba045
--- a/3rdparty/Sabre/CardDAV/Plugin.php
+++ b/3rdparty/Sabre/CardDAV/Plugin.php
@@ -52,6 +52,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
         $server->subscribeEvent('report', array($this,'report'));
         $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
         $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
+        $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
+        $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
 
         /* Namespaces */
         $server->xmlNamespaces[self::NS_CARDDAV] = 'card';
@@ -283,6 +285,81 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
 
     }
 
+    /**
+     * This method is triggered before a file gets updated with new content.
+     *
+     * This plugin uses this method to ensure that Card nodes receive valid
+     * vcard data.
+     *
+     * @param string $path
+     * @param Sabre_DAV_IFile $node
+     * @param resource $data
+     * @return void
+     */
+    public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
+
+        if (!$node instanceof Sabre_CardDAV_ICard)
+            return;
+
+        $this->validateVCard($data);
+
+    }
+
+    /**
+     * This method is triggered before a new file is created.
+     *
+     * This plugin uses this method to ensure that Card nodes receive valid
+     * vcard data.
+     *
+     * @param string $path
+     * @param resource $data
+     * @param Sabre_DAV_ICollection $parentNode
+     * @return void
+     */
+    public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
+
+        if (!$parentNode instanceof Sabre_CardDAV_IAddressBook)
+            return;
+
+        $this->validateVCard($data);
+
+    }
+
+    /**
+     * Checks if the submitted iCalendar data is in fact, valid.
+     *
+     * An exception is thrown if it's not.
+     *
+     * @param resource|string $data
+     * @return void
+     */
+    protected function validateVCard(&$data) {
+
+        // If it's a stream, we convert it to a string first.
+        if (is_resource($data)) {
+            $data = stream_get_contents($data);
+        }
+
+        // Converting the data to unicode, if needed.
+        $data = Sabre_DAV_StringUtil::ensureUTF8($data);
+
+        try {
+
+            $vobj = Sabre_VObject_Reader::read($data);
+
+        } catch (Sabre_VObject_ParseException $e) {
+
+            throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
+
+        }
+
+        if ($vobj->name !== 'VCARD') {
+            throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
+        }
+
+    }
+
+
     /**
      * This function handles the addressbook-query REPORT
      *
@@ -362,6 +439,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
 
         $vcard = Sabre_VObject_Reader::read($vcardData);
 
+        if (!$filters) return true;
+
         foreach($filters as $filter) {
 
             $isDefined = isset($vcard->{$filter['name']});
diff --git a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php
old mode 100644
new mode 100755
index 811b929e3978d11661901239b228573a3f0f2ab8..d0623f0d3e8658b89f747e619ad5547655d4ed93
--- a/3rdparty/Sabre/CardDAV/Version.php
+++ b/3rdparty/Sabre/CardDAV/Version.php
@@ -16,7 +16,7 @@ class Sabre_CardDAV_Version {
     /**
      * Full version number
      */
-    const VERSION = '1.6.1';
+    const VERSION = '1.6.3';
 
     /**
      * Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/CardDAV/includes.php b/3rdparty/Sabre/CardDAV/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php
old mode 100644
new mode 100755
index 075e84caa1d2f75761712c44be3d16a9d4aa06bd..9a428765e904bc8e5e92b05d7f4299890e7f150d
--- a/3rdparty/Sabre/DAV/Client.php
+++ b/3rdparty/Sabre/DAV/Client.php
@@ -11,7 +11,7 @@
  * @package Sabre
  * @subpackage DAVClient
  * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_DAV_Client {
@@ -23,6 +23,28 @@ class Sabre_DAV_Client {
     protected $password;
     protected $proxy;
 
+    /**
+     * Basic authentication
+     */
+    const AUTH_BASIC = 1;
+
+    /**
+     * Digest authentication
+     */
+    const AUTH_DIGEST = 2;
+
+    /**
+     * The authentication type we're using.
+     *
+     * This is a bitmask of AUTH_BASIC and AUTH_DIGEST.
+     *
+     * If DIGEST is used, the client makes 1 extra request per request, to get
+     * the authentication tokens.
+     *
+     * @var int
+     */
+    protected $authType;
+
     /**
      * Constructor
      *
@@ -46,16 +68,21 @@ class Sabre_DAV_Client {
             'baseUri',
             'userName',
             'password',
-            'proxy'
+            'proxy',
         );
 
-
         foreach($validSettings as $validSetting) {
             if (isset($settings[$validSetting])) {
                 $this->$validSetting = $settings[$validSetting];
             }
         }
 
+        if (isset($settings['authType'])) {
+            $this->authType = $settings['authType'];
+        } else {
+            $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST;
+        }
+
         $this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
 
     }
@@ -250,14 +277,9 @@ class Sabre_DAV_Client {
             // Automatically follow redirects
             CURLOPT_FOLLOWLOCATION => true,
             CURLOPT_MAXREDIRS => 5,
-        	CURLOPT_SSL_VERIFYPEER => true,
-        	//CURLOPT_SSL_VERIFYPEER	=> false,
         );
 
         switch ($method) {
-            case 'PUT':
-                $curlSettings[CURLOPT_PUT] = true;
-                break;
             case 'HEAD' :
 
                 // do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
@@ -288,8 +310,15 @@ class Sabre_DAV_Client {
             $curlSettings[CURLOPT_PROXY] = $this->proxy;
         }
 
-        if ($this->userName) {
-            $curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
+        if ($this->userName && $this->authType) {
+            $curlType = 0;
+            if ($this->authType & self::AUTH_BASIC) {
+                $curlType |= CURLAUTH_BASIC;
+            }
+            if ($this->authType & self::AUTH_DIGEST) {
+                $curlType |= CURLAUTH_DIGEST;
+            }
+            $curlSettings[CURLOPT_HTTPAUTH] = $curlType;
             $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
         }
 
diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/Locked.php b/3rdparty/Sabre/DAV/Exception/Locked.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/NotFound.php b/3rdparty/Sabre/DAV/Exception/NotFound.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/NotImplemented.php b/3rdparty/Sabre/DAV/Exception/NotImplemented.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/PaymentRequired.php b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FS/Directory.php b/3rdparty/Sabre/DAV/FS/Directory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/INode.php b/3rdparty/Sabre/DAV/INode.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IProperties.php b/3rdparty/Sabre/DAV/IProperties.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IQuota.php b/3rdparty/Sabre/DAV/IQuota.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/FS.php b/3rdparty/Sabre/DAV/Locks/Backend/FS.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/SupportedLock.php b/3rdparty/Sabre/DAV/Property/SupportedLock.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php
old mode 100644
new mode 100755
index 50b190e8fabf834a9c421b9638eadb71156e20b6..0dfac8b0c711fdb8f7b8e60e2d89c23389aa976f
--- a/3rdparty/Sabre/DAV/Server.php
+++ b/3rdparty/Sabre/DAV/Server.php
@@ -215,7 +215,7 @@ class Sabre_DAV_Server {
             $DOM->appendChild($error);
 
             $error->appendChild($DOM->createElement('s:exception',get_class($e)));
-            $error->appendChild($DOM->createElement('s:message',htmlentities($e->getMessage())));
+            $error->appendChild($DOM->createElement('s:message',$e->getMessage()));
             if ($this->debugExceptions) {
                 $error->appendChild($DOM->createElement('s:file',$e->getFile()));
                 $error->appendChild($DOM->createElement('s:line',$e->getLine()));
@@ -1784,7 +1784,14 @@ class Sabre_DAV_Server {
                     $etag = $node->getETag();
                     if ($etag===$ifMatchItem) {
                         $haveMatch = true;
+                    } else {
+                        // Evolution has a bug where it sometimes prepends the "
+                        // with a \. This is our workaround.
+                        if (str_replace('\\"','"', $ifMatchItem) === $etag) {
+                            $haveMatch = true;
+                        }
                     }
+
                 }
                 if (!$haveMatch) {
                      throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match');
diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/SimpleFile.php b/3rdparty/Sabre/DAV/SimpleFile.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php
old mode 100644
new mode 100755
index 85a9ee317be3c43daa2495908e2800d119ddc11c..40580ae366fe235173661769fd408461e976e999
--- a/3rdparty/Sabre/DAV/Tree/Filesystem.php
+++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php
@@ -42,9 +42,9 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree {
         $realPath = $this->getRealPath($path);
         if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found');
         if (is_dir($realPath)) {
-            return new Sabre_DAV_FS_Directory($path);
+            return new Sabre_DAV_FS_Directory($realPath);
         } else {
-            return new Sabre_DAV_FS_File($path);
+            return new Sabre_DAV_FS_File($realPath);
         }
 
     }
diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php
old mode 100644
new mode 100755
index 5e5d15e40397e700ab6d821c7862b3a7a026f9e5..40cfe81b34f180fae3b36ece0e2d390536fc88ca
--- a/3rdparty/Sabre/DAV/Version.php
+++ b/3rdparty/Sabre/DAV/Version.php
@@ -14,7 +14,7 @@ class Sabre_DAV_Version {
     /**
      * Full version number
      */
-    const VERSION = '1.6.2';
+    const VERSION = '1.6.3';
 
     /**
      * Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/includes.php b/3rdparty/Sabre/DAV/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/includes.php b/3rdparty/Sabre/DAVACL/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/AWSAuth.php b/3rdparty/Sabre/HTTP/AWSAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/includes.php b/3rdparty/Sabre/HTTP/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php
old mode 100644
new mode 100755
index 1e2d54ef3a9b3e115dd608e5e7a91c3b6c4ae74a..23a4bb69916cca390f4d697acb0c93f72756e1ab
--- a/3rdparty/Sabre/VObject/DateTimeParser.php
+++ b/3rdparty/Sabre/VObject/DateTimeParser.php
@@ -125,6 +125,9 @@ class Sabre_VObject_DateTimeParser {
 
             }
 
+            if ($duration==='P') {
+                $duration = 'PT0S';
+            }
             $iv = new DateInterval($duration);
             if ($invert) $iv->invert = true;
 
@@ -150,6 +153,7 @@ class Sabre_VObject_DateTimeParser {
         }
 
         $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
+        if ($newDur === '+') { $newDur = '+0 seconds'; };
         return $newDur;
 
     }
diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Property/MultiDateTime.php b/3rdparty/Sabre/VObject/Property/MultiDateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php
old mode 100644
new mode 100755
index 00110febc075802e5e595460530ba9f68f08a3a5..2617c7b129d620196f425083b2b149b11c5327f9
--- a/3rdparty/Sabre/VObject/Version.php
+++ b/3rdparty/Sabre/VObject/Version.php
@@ -14,7 +14,7 @@ class Sabre_VObject_Version {
     /**
      * Full version number
      */
-    const VERSION = '1.3.2';
+    const VERSION = '1.3.3';
 
     /**
      * Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/autoload.php b/3rdparty/Sabre/autoload.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Symfony/Component/Routing b/3rdparty/Symfony/Component/Routing
new file mode 160000
index 0000000000000000000000000000000000000000..d72483890880a987afa679503af096d2aaf7d2ee
--- /dev/null
+++ b/3rdparty/Symfony/Component/Routing
@@ -0,0 +1 @@
+Subproject commit d72483890880a987afa679503af096d2aaf7d2ee
diff --git a/3rdparty/miniColors/GPL-LICENSE.txt b/3rdparty/miniColors/GPL-LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..11dddd00ef0e91a0bce53b034d6b5b318a84e690
--- /dev/null
+++ b/3rdparty/miniColors/GPL-LICENSE.txt
@@ -0,0 +1,278 @@
+        GNU GENERAL PUBLIC LICENSE
+           Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+          Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+        GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+          NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/3rdparty/miniColors/MIT-LICENSE.txt b/3rdparty/miniColors/MIT-LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ec6f75815765c81e5b652b2846a4bb5ee9e6a8b6
--- /dev/null
+++ b/3rdparty/miniColors/MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) Cory LaViska
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/3rdparty/miniColors/css/images/colors.png b/3rdparty/miniColors/css/images/colors.png
new file mode 100755
index 0000000000000000000000000000000000000000..1b4f819d8d9367147e8b1d2145e5fa1c5d6a48d3
Binary files /dev/null and b/3rdparty/miniColors/css/images/colors.png differ
diff --git a/3rdparty/miniColors/css/images/trigger.png b/3rdparty/miniColors/css/images/trigger.png
new file mode 100755
index 0000000000000000000000000000000000000000..8c169fd6053009030232ef865a61876992b68589
Binary files /dev/null and b/3rdparty/miniColors/css/images/trigger.png differ
diff --git a/3rdparty/miniColors/css/jquery.miniColors.css b/3rdparty/miniColors/css/jquery.miniColors.css
new file mode 100755
index 0000000000000000000000000000000000000000..381bc1dc06510c4731379a9df5e34318029f6da7
--- /dev/null
+++ b/3rdparty/miniColors/css/jquery.miniColors.css
@@ -0,0 +1,81 @@
+.miniColors-trigger {
+	height: 22px;
+	width: 22px;
+	background: url(images/trigger.png) center no-repeat;
+	vertical-align: middle;
+	margin: 0 .25em;
+	display: inline-block;
+	outline: none;
+}
+
+.miniColors-selector {
+	position: absolute;
+	width: 175px;
+	height: 150px;
+	background: #FFF;
+	border: solid 1px #BBB;
+	-moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+	-webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+	box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+	-moz-border-radius: 5px;
+	-webkit-border-radius: 5px;
+	border-radius: 5px;
+	padding: 5px;
+	z-index: 999999;
+}
+
+.miniColors-selector.black {
+	background: #000;
+	border-color: #000;
+}
+
+.miniColors-colors {
+	position: absolute;
+	top: 5px;
+	left: 5px;
+	width: 150px;
+	height: 150px;
+	background: url(images/colors.png) right no-repeat;
+	cursor: crosshair;
+}
+
+.miniColors-hues {
+	position: absolute;
+	top: 5px;
+	left: 160px;
+	width: 20px;
+	height: 150px;
+	background: url(images/colors.png) left no-repeat;
+	cursor: crosshair;
+}
+
+.miniColors-colorPicker {
+	position: absolute;
+	width: 9px;
+	height: 9px;
+	border: 1px solid #fff;
+	-moz-border-radius: 11px;
+	-webkit-border-radius: 11px;
+	border-radius: 11px;
+}
+.miniColors-colorPicker-inner {
+	position: absolute;
+	top: 0;
+	left: 0; 
+	width: 7px;
+	height: 7px;
+	border: 1px solid #000;
+	-moz-border-radius: 9px;
+	-webkit-border-radius: 9px;
+	border-radius: 9px;
+}
+
+.miniColors-huePicker {
+	position: absolute;
+	left: -3px;
+	width: 24px;
+	height: 1px;
+	border: 1px solid #fff;
+	border-radius: 2px;
+	background: #000;
+}
\ No newline at end of file
diff --git a/3rdparty/miniColors/js/jquery.miniColors.js b/3rdparty/miniColors/js/jquery.miniColors.js
new file mode 100755
index 0000000000000000000000000000000000000000..187db3fa84e5dd95eb8395a2cffa5828df6a3d16
--- /dev/null
+++ b/3rdparty/miniColors/js/jquery.miniColors.js
@@ -0,0 +1,580 @@
+/*
+ * jQuery miniColors: A small color selector
+ *
+ * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/)
+ *
+ * Dual licensed under the MIT or GPL Version 2 licenses
+ *
+*/
+if(jQuery) (function($) {
+	
+	$.extend($.fn, {
+		
+		miniColors: function(o, data) {
+			
+			var create = function(input, o, data) {
+				//
+				// Creates a new instance of the miniColors selector
+				//
+				
+				// Determine initial color (defaults to white)
+				var color = expandHex(input.val());
+				if( !color ) color = 'ffffff';
+				var hsb = hex2hsb(color);
+				
+				// Create trigger
+				var trigger = $('<a class="miniColors-trigger" style="background-color: #' + color + '" href="#"></a>');
+				trigger.insertAfter(input);
+				
+				// Set input data and update attributes
+				input
+					.addClass('miniColors')
+					.data('original-maxlength', input.attr('maxlength') || null)
+					.data('original-autocomplete', input.attr('autocomplete') || null)
+					.data('letterCase', 'uppercase')
+					.data('trigger', trigger)
+					.data('hsb', hsb)
+					.data('change', o.change ? o.change : null)
+					.data('close', o.close ? o.close : null)
+					.data('open', o.open ? o.open : null)
+					.attr('maxlength', 7)
+					.attr('autocomplete', 'off')
+					.val('#' + convertCase(color, o.letterCase));
+				
+				// Handle options
+				if( o.readonly ) input.prop('readonly', true);
+				if( o.disabled ) disable(input);
+				
+				// Show selector when trigger is clicked
+				trigger.bind('click.miniColors', function(event) {
+					event.preventDefault();
+					if( input.val() === '' ) input.val('#');
+					show(input);
+
+				});
+				
+				// Show selector when input receives focus
+				input.bind('focus.miniColors', function(event) {
+					if( input.val() === '' ) input.val('#');
+					show(input);
+				});
+				
+				// Hide on blur
+				input.bind('blur.miniColors', function(event) {
+					var hex = expandHex( hsb2hex(input.data('hsb')) );
+					input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' );
+				});
+				
+				// Hide when tabbing out of the input
+				input.bind('keydown.miniColors', function(event) {
+					if( event.keyCode === 9 ) hide(input);
+				});
+				
+				// Update when color is typed in
+				input.bind('keyup.miniColors', function(event) {
+					setColorFromInput(input);
+				});
+				
+				// Handle pasting
+				input.bind('paste.miniColors', function(event) {
+					// Short pause to wait for paste to complete
+					setTimeout( function() {
+						setColorFromInput(input);
+					}, 5);
+				});
+				
+			};
+			
+			var destroy = function(input) {
+				//
+				// Destroys an active instance of the miniColors selector
+				//
+				
+				hide();
+				input = $(input);
+				
+				// Restore to original state
+				input.data('trigger').remove();
+				input
+					.attr('autocomplete', input.data('original-autocomplete'))
+					.attr('maxlength', input.data('original-maxlength'))
+					.removeData()
+					.removeClass('miniColors')
+					.unbind('.miniColors');
+				$(document).unbind('.miniColors');
+			};
+			
+			var enable = function(input) {
+				//
+				// Enables the input control and the selector
+				//
+				input
+					.prop('disabled', false)
+					.data('trigger')
+					.css('opacity', 1);
+			};
+			
+			var disable = function(input) {
+				//
+				// Disables the input control and the selector
+				//
+				hide(input);
+				input
+					.prop('disabled', true)
+					.data('trigger')
+					.css('opacity', 0.5);
+			};
+			
+			var show = function(input) {
+				//
+				// Shows the miniColors selector
+				//
+				if( input.prop('disabled') ) return false;
+				
+				// Hide all other instances 
+				hide();				
+				
+				// Generate the selector
+				var selector = $('<div class="miniColors-selector"></div>');
+				selector
+					.append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"><div class="miniColors-colorPicker-inner"></div></div>')
+					.append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>')
+					.css({
+						top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(),
+						left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left,
+						display: 'none'
+					})
+					.addClass( input.attr('class') );
+				
+				// Set background for colors
+				var hsb = input.data('hsb');
+				selector
+					.find('.miniColors-colors')
+					.css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 }));
+				
+				// Set colorPicker position
+				var colorPosition = input.data('colorPosition');
+				if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb);
+				selector.find('.miniColors-colorPicker')
+					.css('top', colorPosition.y + 'px')
+					.css('left', colorPosition.x + 'px');
+				
+				// Set huePicker position
+				var huePosition = input.data('huePosition');
+				if( !huePosition ) huePosition = getHuePositionFromHSB(hsb);
+				selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px');
+				
+				// Set input data
+				input
+					.data('selector', selector)
+					.data('huePicker', selector.find('.miniColors-huePicker'))
+					.data('colorPicker', selector.find('.miniColors-colorPicker'))
+					.data('mousebutton', 0);
+					
+				$('BODY').append(selector);
+				selector.fadeIn(100);
+				
+				// Prevent text selection in IE
+				selector.bind('selectstart', function() { return false; });
+				
+				$(document).bind('mousedown.miniColors touchstart.miniColors', function(event) {
+					
+					input.data('mousebutton', 1);
+					var testSubject = $(event.target).parents().andSelf();
+					
+					if( testSubject.hasClass('miniColors-colors') ) {
+						event.preventDefault();
+						input.data('moving', 'colors');
+						moveColor(input, event);
+					}
+					
+					if( testSubject.hasClass('miniColors-hues') ) {
+						event.preventDefault();
+						input.data('moving', 'hues');
+						moveHue(input, event);
+					}
+					
+					if( testSubject.hasClass('miniColors-selector') ) {
+						event.preventDefault();
+						return;
+					}
+					
+					if( testSubject.hasClass('miniColors') ) return;
+					
+					hide(input);
+				});
+				
+				$(document)
+					.bind('mouseup.miniColors touchend.miniColors', function(event) {
+					    event.preventDefault();
+						input.data('mousebutton', 0).removeData('moving');
+					})
+					.bind('mousemove.miniColors touchmove.miniColors', function(event) {
+						event.preventDefault();
+						if( input.data('mousebutton') === 1 ) {
+							if( input.data('moving') === 'colors' ) moveColor(input, event);
+							if( input.data('moving') === 'hues' ) moveHue(input, event);
+						}
+					});
+				
+				// Fire open callback
+				if( input.data('open') ) {
+					input.data('open').call(input.get(0), '#' + hsb2hex(hsb), hsb2rgb(hsb));
+				}
+				
+			};
+			
+			var hide = function(input) {
+				
+				//
+				// Hides one or more miniColors selectors
+				//
+				
+				// Hide all other instances if input isn't specified
+				if( !input ) input = '.miniColors';
+				
+				$(input).each( function() {
+					var selector = $(this).data('selector');
+					$(this).removeData('selector');
+					$(selector).fadeOut(100, function() {
+						// Fire close callback
+						if( input.data('close') ) {
+							var hsb = input.data('hsb'), hex = hsb2hex(hsb);	
+							input.data('close').call(input.get(0), '#' + hex, hsb2rgb(hsb));
+						}
+						$(this).remove();
+					});
+				});
+				
+				$(document).unbind('.miniColors');
+				
+			};
+			
+			var moveColor = function(input, event) {
+
+				var colorPicker = input.data('colorPicker');
+				
+				colorPicker.hide();
+				
+				var position = {
+					x: event.pageX,
+					y: event.pageY
+				};
+				
+				// Touch support
+				if( event.originalEvent.changedTouches ) {
+					position.x = event.originalEvent.changedTouches[0].pageX;
+					position.y = event.originalEvent.changedTouches[0].pageY;
+				}
+				position.x = position.x - input.data('selector').find('.miniColors-colors').offset().left - 5;
+				position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 5;
+				if( position.x <= -5 ) position.x = -5;
+				if( position.x >= 144 ) position.x = 144;
+				if( position.y <= -5 ) position.y = -5;
+				if( position.y >= 144 ) position.y = 144;
+				
+				input.data('colorPosition', position);
+				colorPicker.css('left', position.x).css('top', position.y).show();
+				
+				// Calculate saturation
+				var s = Math.round((position.x + 5) * 0.67);
+				if( s < 0 ) s = 0;
+				if( s > 100 ) s = 100;
+				
+				// Calculate brightness
+				var b = 100 - Math.round((position.y + 5) * 0.67);
+				if( b < 0 ) b = 0;
+				if( b > 100 ) b = 100;
+				
+				// Update HSB values
+				var hsb = input.data('hsb');
+				hsb.s = s;
+				hsb.b = b;
+				
+				// Set color
+				setColor(input, hsb, true);
+			};
+			
+			var moveHue = function(input, event) {
+				
+				var huePicker = input.data('huePicker');
+				
+				huePicker.hide();
+				
+				var position = {
+					y: event.pageY
+				};
+				
+				// Touch support
+				if( event.originalEvent.changedTouches ) {
+					position.y = event.originalEvent.changedTouches[0].pageY;
+				}
+				
+				position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 1;
+				if( position.y <= -1 ) position.y = -1;
+				if( position.y >= 149 ) position.y = 149;
+				input.data('huePosition', position);
+				huePicker.css('top', position.y).show();
+				
+				// Calculate hue
+				var h = Math.round((150 - position.y - 1) * 2.4);
+				if( h < 0 ) h = 0;
+				if( h > 360 ) h = 360;
+				
+				// Update HSB values
+				var hsb = input.data('hsb');
+				hsb.h = h;
+				
+				// Set color
+				setColor(input, hsb, true);
+				
+			};
+			
+			var setColor = function(input, hsb, updateInput) {
+				input.data('hsb', hsb);
+				var hex = hsb2hex(hsb);	
+				if( updateInput ) input.val( '#' + convertCase(hex, input.data('letterCase')) );
+				input.data('trigger').css('backgroundColor', '#' + hex);
+				if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 }));
+				
+				// Fire change callback
+				if( input.data('change') ) {
+					if( hex === input.data('lastChange') ) return;
+					input.data('change').call(input.get(0), '#' + hex, hsb2rgb(hsb));
+					input.data('lastChange', hex);
+				}
+				
+			};
+			
+			var setColorFromInput = function(input) {
+				
+				input.val('#' + cleanHex(input.val()));
+				var hex = expandHex(input.val());
+				if( !hex ) return false;
+				
+				// Get HSB equivalent
+				var hsb = hex2hsb(hex);
+				
+				// If color is the same, no change required
+				var currentHSB = input.data('hsb');
+				if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true;
+				
+				// Set colorPicker position
+				var colorPosition = getColorPositionFromHSB(hsb);
+				var colorPicker = $(input.data('colorPicker'));
+				colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px');
+				input.data('colorPosition', colorPosition);
+				
+				// Set huePosition position
+				var huePosition = getHuePositionFromHSB(hsb);
+				var huePicker = $(input.data('huePicker'));
+				huePicker.css('top', huePosition.y + 'px');
+				input.data('huePosition', huePosition);
+				
+				setColor(input, hsb);
+				
+				return true;
+				
+			};
+			
+			var convertCase = function(string, letterCase) {
+				if( letterCase === 'lowercase' ) return string.toLowerCase();
+				if( letterCase === 'uppercase' ) return string.toUpperCase();
+				return string;
+			};
+			
+			var getColorPositionFromHSB = function(hsb) {				
+				var x = Math.ceil(hsb.s / 0.67);
+				if( x < 0 ) x = 0;
+				if( x > 150 ) x = 150;
+				var y = 150 - Math.ceil(hsb.b / 0.67);
+				if( y < 0 ) y = 0;
+				if( y > 150 ) y = 150;
+				return { x: x - 5, y: y - 5 };
+			};
+			
+			var getHuePositionFromHSB = function(hsb) {
+				var y = 150 - (hsb.h / 2.4);
+				if( y < 0 ) h = 0;
+				if( y > 150 ) h = 150;				
+				return { y: y - 1 };
+			};
+			
+			var cleanHex = function(hex) {
+				return hex.replace(/[^A-F0-9]/ig, '');
+			};
+			
+			var expandHex = function(hex) {
+				hex = cleanHex(hex);
+				if( !hex ) return null;
+				if( hex.length === 3 ) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
+				return hex.length === 6 ? hex : null;
+			};			
+			
+			var hsb2rgb = function(hsb) {
+				var rgb = {};
+				var h = Math.round(hsb.h);
+				var s = Math.round(hsb.s*255/100);
+				var v = Math.round(hsb.b*255/100);
+				if(s === 0) {
+					rgb.r = rgb.g = rgb.b = v;
+				} else {
+					var t1 = v;
+					var t2 = (255 - s) * v / 255;
+					var t3 = (t1 - t2) * (h % 60) / 60;
+					if( h === 360 ) h = 0;
+					if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; }
+					else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; }
+					else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; }
+					else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; }
+					else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; }
+					else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; }
+					else { rgb.r = 0; rgb.g = 0; rgb.b = 0; }
+				}
+				return {
+					r: Math.round(rgb.r),
+					g: Math.round(rgb.g),
+					b: Math.round(rgb.b)
+				};
+			};
+			
+			var rgb2hex = function(rgb) {
+				var hex = [
+					rgb.r.toString(16),
+					rgb.g.toString(16),
+					rgb.b.toString(16)
+				];
+				$.each(hex, function(nr, val) {
+					if (val.length === 1) hex[nr] = '0' + val;
+				});
+				return hex.join('');
+			};
+			
+			var hex2rgb = function(hex) {
+				hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
+				
+				return {
+					r: hex >> 16,
+					g: (hex & 0x00FF00) >> 8,
+					b: (hex & 0x0000FF)
+				};
+			};
+			
+			var rgb2hsb = function(rgb) {
+				var hsb = { h: 0, s: 0, b: 0 };
+				var min = Math.min(rgb.r, rgb.g, rgb.b);
+				var max = Math.max(rgb.r, rgb.g, rgb.b);
+				var delta = max - min;
+				hsb.b = max;
+				hsb.s = max !== 0 ? 255 * delta / max : 0;
+				if( hsb.s !== 0 ) {
+					if( rgb.r === max ) {
+						hsb.h = (rgb.g - rgb.b) / delta;
+					} else if( rgb.g === max ) {
+						hsb.h = 2 + (rgb.b - rgb.r) / delta;
+					} else {
+						hsb.h = 4 + (rgb.r - rgb.g) / delta;
+					}
+				} else {
+					hsb.h = -1;
+				}
+				hsb.h *= 60;
+				if( hsb.h < 0 ) {
+					hsb.h += 360;
+				}
+				hsb.s *= 100/255;
+				hsb.b *= 100/255;
+				return hsb;
+			};			
+			
+			var hex2hsb = function(hex) {
+				var hsb = rgb2hsb(hex2rgb(hex));
+				// Zero out hue marker for black, white, and grays (saturation === 0)
+				if( hsb.s === 0 ) hsb.h = 360;
+				return hsb;
+			};
+			
+			var hsb2hex = function(hsb) {
+				return rgb2hex(hsb2rgb(hsb));
+			};
+
+			
+			// Handle calls to $([selector]).miniColors()
+			switch(o) {
+			
+				case 'readonly':
+					
+					$(this).each( function() {
+						if( !$(this).hasClass('miniColors') ) return;
+						$(this).prop('readonly', data);
+					});
+					
+					return $(this);
+				
+				case 'disabled':
+					
+					$(this).each( function() {
+						if( !$(this).hasClass('miniColors') ) return;
+						if( data ) {
+							disable($(this));
+						} else {
+							enable($(this));
+						}
+					});
+										
+					return $(this);
+			
+				case 'value':
+					
+					// Getter
+					if( data === undefined ) {
+						if( !$(this).hasClass('miniColors') ) return;
+						var input = $(this),
+							hex = expandHex(input.val());
+						return hex ? '#' + convertCase(hex, input.data('letterCase')) : null;
+					}
+					
+					// Setter
+					$(this).each( function() {
+						if( !$(this).hasClass('miniColors') ) return;
+						$(this).val(data);
+						setColorFromInput($(this));
+					});
+					
+					return $(this);
+					
+				case 'destroy':
+					
+					$(this).each( function() {
+						if( !$(this).hasClass('miniColors') ) return;
+						destroy($(this));
+					});
+										
+					return $(this);
+				
+				default:
+					
+					if( !o ) o = {};
+					
+					$(this).each( function() {
+						
+						// Must be called on an input element
+						if( $(this)[0].tagName.toLowerCase() !== 'input' ) return;
+						
+						// If a trigger is present, the control was already created
+						if( $(this).data('trigger') ) return;
+						
+						// Create the control
+						create($(this), o, data);
+						
+					});
+					
+					return $(this);
+					
+			}
+			
+		}
+			
+	});
+	
+})(jQuery);
\ No newline at end of file
diff --git a/3rdparty/miniColors/js/jquery.miniColors.min.js b/3rdparty/miniColors/js/jquery.miniColors.min.js
new file mode 100755
index 0000000000000000000000000000000000000000..c00e0ace6b57b2e3e94b6190d6183f9e2df060b9
--- /dev/null
+++ b/3rdparty/miniColors/js/jquery.miniColors.min.js
@@ -0,0 +1,9 @@
+/*
+ * jQuery miniColors: A small color selector
+ *
+ * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/)
+ *
+ * Dual licensed under the MIT or GPL Version 2 licenses
+ *
+*/
+if(jQuery)(function($){$.extend($.fn,{miniColors:function(o,data){var create=function(input,o,data){var color=expandHex(input.val());if(!color)color='ffffff';var hsb=hex2hsb(color);var trigger=$('<a class="miniColors-trigger" style="background-color: #'+color+'" href="#"></a>');trigger.insertAfter(input);input.addClass('miniColors').data('original-maxlength',input.attr('maxlength')||null).data('original-autocomplete',input.attr('autocomplete')||null).data('letterCase','uppercase').data('trigger',trigger).data('hsb',hsb).data('change',o.change?o.change:null).data('close',o.close?o.close:null).data('open',o.open?o.open:null).attr('maxlength',7).attr('autocomplete','off').val('#'+convertCase(color,o.letterCase));if(o.readonly)input.prop('readonly',true);if(o.disabled)disable(input);trigger.bind('click.miniColors',function(event){event.preventDefault();if(input.val()==='')input.val('#');show(input)});input.bind('focus.miniColors',function(event){if(input.val()==='')input.val('#');show(input)});input.bind('blur.miniColors',function(event){var hex=expandHex(hsb2hex(input.data('hsb')));input.val(hex?'#'+convertCase(hex,input.data('letterCase')):'')});input.bind('keydown.miniColors',function(event){if(event.keyCode===9)hide(input)});input.bind('keyup.miniColors',function(event){setColorFromInput(input)});input.bind('paste.miniColors',function(event){setTimeout(function(){setColorFromInput(input)},5)})};var destroy=function(input){hide();input=$(input);input.data('trigger').remove();input.attr('autocomplete',input.data('original-autocomplete')).attr('maxlength',input.data('original-maxlength')).removeData().removeClass('miniColors').unbind('.miniColors');$(document).unbind('.miniColors')};var enable=function(input){input.prop('disabled',false).data('trigger').css('opacity',1)};var disable=function(input){hide(input);input.prop('disabled',true).data('trigger').css('opacity',0.5)};var show=function(input){if(input.prop('disabled'))return false;hide();var selector=$('<div class="miniColors-selector"></div>');selector.append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"><div class="miniColors-colorPicker-inner"></div></div>').append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>').css({top:input.is(':visible')?input.offset().top+input.outerHeight():input.data('trigger').offset().top+input.data('trigger').outerHeight(),left:input.is(':visible')?input.offset().left:input.data('trigger').offset().left,display:'none'}).addClass(input.attr('class'));var hsb=input.data('hsb');selector.find('.miniColors-colors').css('backgroundColor','#'+hsb2hex({h:hsb.h,s:100,b:100}));var colorPosition=input.data('colorPosition');if(!colorPosition)colorPosition=getColorPositionFromHSB(hsb);selector.find('.miniColors-colorPicker').css('top',colorPosition.y+'px').css('left',colorPosition.x+'px');var huePosition=input.data('huePosition');if(!huePosition)huePosition=getHuePositionFromHSB(hsb);selector.find('.miniColors-huePicker').css('top',huePosition.y+'px');input.data('selector',selector).data('huePicker',selector.find('.miniColors-huePicker')).data('colorPicker',selector.find('.miniColors-colorPicker')).data('mousebutton',0);$('BODY').append(selector);selector.fadeIn(100);selector.bind('selectstart',function(){return false});$(document).bind('mousedown.miniColors touchstart.miniColors',function(event){input.data('mousebutton',1);var testSubject=$(event.target).parents().andSelf();if(testSubject.hasClass('miniColors-colors')){event.preventDefault();input.data('moving','colors');moveColor(input,event)}if(testSubject.hasClass('miniColors-hues')){event.preventDefault();input.data('moving','hues');moveHue(input,event)}if(testSubject.hasClass('miniColors-selector')){event.preventDefault();return}if(testSubject.hasClass('miniColors'))return;hide(input)});$(document).bind('mouseup.miniColors touchend.miniColors',function(event){event.preventDefault();input.data('mousebutton',0).removeData('moving')}).bind('mousemove.miniColors touchmove.miniColors',function(event){event.preventDefault();if(input.data('mousebutton')===1){if(input.data('moving')==='colors')moveColor(input,event);if(input.data('moving')==='hues')moveHue(input,event)}});if(input.data('open')){input.data('open').call(input.get(0),'#'+hsb2hex(hsb),hsb2rgb(hsb))}};var hide=function(input){if(!input)input='.miniColors';$(input).each(function(){var selector=$(this).data('selector');$(this).removeData('selector');$(selector).fadeOut(100,function(){if(input.data('close')){var hsb=input.data('hsb'),hex=hsb2hex(hsb);input.data('close').call(input.get(0),'#'+hex,hsb2rgb(hsb))}$(this).remove()})});$(document).unbind('.miniColors')};var moveColor=function(input,event){var colorPicker=input.data('colorPicker');colorPicker.hide();var position={x:event.pageX,y:event.pageY};if(event.originalEvent.changedTouches){position.x=event.originalEvent.changedTouches[0].pageX;position.y=event.originalEvent.changedTouches[0].pageY}position.x=position.x-input.data('selector').find('.miniColors-colors').offset().left-5;position.y=position.y-input.data('selector').find('.miniColors-colors').offset().top-5;if(position.x<=-5)position.x=-5;if(position.x>=144)position.x=144;if(position.y<=-5)position.y=-5;if(position.y>=144)position.y=144;input.data('colorPosition',position);colorPicker.css('left',position.x).css('top',position.y).show();var s=Math.round((position.x+5)*0.67);if(s<0)s=0;if(s>100)s=100;var b=100-Math.round((position.y+5)*0.67);if(b<0)b=0;if(b>100)b=100;var hsb=input.data('hsb');hsb.s=s;hsb.b=b;setColor(input,hsb,true)};var moveHue=function(input,event){var huePicker=input.data('huePicker');huePicker.hide();var position={y:event.pageY};if(event.originalEvent.changedTouches){position.y=event.originalEvent.changedTouches[0].pageY}position.y=position.y-input.data('selector').find('.miniColors-colors').offset().top-1;if(position.y<=-1)position.y=-1;if(position.y>=149)position.y=149;input.data('huePosition',position);huePicker.css('top',position.y).show();var h=Math.round((150-position.y-1)*2.4);if(h<0)h=0;if(h>360)h=360;var hsb=input.data('hsb');hsb.h=h;setColor(input,hsb,true)};var setColor=function(input,hsb,updateInput){input.data('hsb',hsb);var hex=hsb2hex(hsb);if(updateInput)input.val('#'+convertCase(hex,input.data('letterCase')));input.data('trigger').css('backgroundColor','#'+hex);if(input.data('selector'))input.data('selector').find('.miniColors-colors').css('backgroundColor','#'+hsb2hex({h:hsb.h,s:100,b:100}));if(input.data('change')){if(hex===input.data('lastChange'))return;input.data('change').call(input.get(0),'#'+hex,hsb2rgb(hsb));input.data('lastChange',hex)}};var setColorFromInput=function(input){input.val('#'+cleanHex(input.val()));var hex=expandHex(input.val());if(!hex)return false;var hsb=hex2hsb(hex);var currentHSB=input.data('hsb');if(hsb.h===currentHSB.h&&hsb.s===currentHSB.s&&hsb.b===currentHSB.b)return true;var colorPosition=getColorPositionFromHSB(hsb);var colorPicker=$(input.data('colorPicker'));colorPicker.css('top',colorPosition.y+'px').css('left',colorPosition.x+'px');input.data('colorPosition',colorPosition);var huePosition=getHuePositionFromHSB(hsb);var huePicker=$(input.data('huePicker'));huePicker.css('top',huePosition.y+'px');input.data('huePosition',huePosition);setColor(input,hsb);return true};var convertCase=function(string,letterCase){if(letterCase==='lowercase')return string.toLowerCase();if(letterCase==='uppercase')return string.toUpperCase();return string};var getColorPositionFromHSB=function(hsb){var x=Math.ceil(hsb.s/0.67);if(x<0)x=0;if(x>150)x=150;var y=150-Math.ceil(hsb.b/0.67);if(y<0)y=0;if(y>150)y=150;return{x:x-5,y:y-5}};var getHuePositionFromHSB=function(hsb){var y=150-(hsb.h/2.4);if(y<0)h=0;if(y>150)h=150;return{y:y-1}};var cleanHex=function(hex){return hex.replace(/[^A-F0-9]/ig,'')};var expandHex=function(hex){hex=cleanHex(hex);if(!hex)return null;if(hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];return hex.length===6?hex:null};var hsb2rgb=function(hsb){var rgb={};var h=Math.round(hsb.h);var s=Math.round(hsb.s*255/100);var v=Math.round(hsb.b*255/100);if(s===0){rgb.r=rgb.g=rgb.b=v}else{var t1=v;var t2=(255-s)*v/255;var t3=(t1-t2)*(h%60)/60;if(h===360)h=0;if(h<60){rgb.r=t1;rgb.b=t2;rgb.g=t2+t3}else if(h<120){rgb.g=t1;rgb.b=t2;rgb.r=t1-t3}else if(h<180){rgb.g=t1;rgb.r=t2;rgb.b=t2+t3}else if(h<240){rgb.b=t1;rgb.r=t2;rgb.g=t1-t3}else if(h<300){rgb.b=t1;rgb.g=t2;rgb.r=t2+t3}else if(h<360){rgb.r=t1;rgb.g=t2;rgb.b=t1-t3}else{rgb.r=0;rgb.g=0;rgb.b=0}}return{r:Math.round(rgb.r),g:Math.round(rgb.g),b:Math.round(rgb.b)}};var rgb2hex=function(rgb){var hex=[rgb.r.toString(16),rgb.g.toString(16),rgb.b.toString(16)];$.each(hex,function(nr,val){if(val.length===1)hex[nr]='0'+val});return hex.join('')};var hex2rgb=function(hex){hex=parseInt(((hex.indexOf('#')>-1)?hex.substring(1):hex),16);return{r:hex>>16,g:(hex&0x00FF00)>>8,b:(hex&0x0000FF)}};var rgb2hsb=function(rgb){var hsb={h:0,s:0,b:0};var min=Math.min(rgb.r,rgb.g,rgb.b);var max=Math.max(rgb.r,rgb.g,rgb.b);var delta=max-min;hsb.b=max;hsb.s=max!==0?255*delta/max:0;if(hsb.s!==0){if(rgb.r===max){hsb.h=(rgb.g-rgb.b)/delta}else if(rgb.g===max){hsb.h=2+(rgb.b-rgb.r)/delta}else{hsb.h=4+(rgb.r-rgb.g)/delta}}else{hsb.h=-1}hsb.h*=60;if(hsb.h<0){hsb.h+=360}hsb.s*=100/255;hsb.b*=100/255;return hsb};var hex2hsb=function(hex){var hsb=rgb2hsb(hex2rgb(hex));if(hsb.s===0)hsb.h=360;return hsb};var hsb2hex=function(hsb){return rgb2hex(hsb2rgb(hsb))};switch(o){case'readonly':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;$(this).prop('readonly',data)});return $(this);case'disabled':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;if(data){disable($(this))}else{enable($(this))}});return $(this);case'value':if(data===undefined){if(!$(this).hasClass('miniColors'))return;var input=$(this),hex=expandHex(input.val());return hex?'#'+convertCase(hex,input.data('letterCase')):null}$(this).each(function(){if(!$(this).hasClass('miniColors'))return;$(this).val(data);setColorFromInput($(this))});return $(this);case'destroy':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;destroy($(this))});return $(this);default:if(!o)o={};$(this).each(function(){if($(this)[0].tagName.toLowerCase()!=='input')return;if($(this).data('trigger'))return;create($(this),o,data)});return $(this)}}})})(jQuery);
\ No newline at end of file
diff --git a/apps/user_openid/class.openid.v3.php b/3rdparty/openid/class.openid.v3.php
similarity index 100%
rename from apps/user_openid/class.openid.v3.php
rename to 3rdparty/openid/class.openid.v3.php
diff --git a/apps/user_openid/phpmyid.php b/3rdparty/openid/phpmyid.php
similarity index 99%
rename from apps/user_openid/phpmyid.php
rename to 3rdparty/openid/phpmyid.php
index 5aaab64285649274fb23fbe94858fbf8678b9264..13fd31c47ca2e2c95cfe217c11024bfc02c8fcf3 100644
--- a/apps/user_openid/phpmyid.php
+++ b/3rdparty/openid/phpmyid.php
@@ -1705,4 +1705,3 @@ $run_mode = (isset($_REQUEST['openid_mode'])
 debug("Run mode: $run_mode at: " . time());
 debug($_REQUEST, 'Request params');
 call_user_func($run_mode . '_mode');
-?>
diff --git a/apps/bookmarks/ajax/addBookmark.php b/apps/bookmarks/ajax/addBookmark.php
index 6b5a0f71d4e8bb72c80bde82d0f109f4c437264a..483716405a1e6f92ccf2e3de6d9c32e92acc0810 100644
--- a/apps/bookmarks/ajax/addBookmark.php
+++ b/apps/bookmarks/ajax/addBookmark.php
@@ -28,6 +28,8 @@ $RUNTIME_NOSETUPFS=true;
 
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
 OCP\JSON::checkAppEnabled('bookmarks');
 
 require_once(OC_App::getAppPath('bookmarks').'/bookmarksHelper.php');
diff --git a/apps/bookmarks/ajax/delBookmark.php b/apps/bookmarks/ajax/delBookmark.php
index 5a067701c9fb165ba9e870a9b7f23680cd829e47..f40f02ebab7ccd0b786bb768df5ead839c645157 100644
--- a/apps/bookmarks/ajax/delBookmark.php
+++ b/apps/bookmarks/ajax/delBookmark.php
@@ -28,6 +28,8 @@ $RUNTIME_NOSETUPFS=true;
 
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
 OCP\JSON::checkAppEnabled('bookmarks');
 
 $id = $_POST['id'];
diff --git a/apps/bookmarks/ajax/editBookmark.php b/apps/bookmarks/ajax/editBookmark.php
index 439b680dc20ae9088089a4d67b2f17a02767f893..0b37d161af1c772041bb07caa9a539df4b62de16 100644
--- a/apps/bookmarks/ajax/editBookmark.php
+++ b/apps/bookmarks/ajax/editBookmark.php
@@ -28,6 +28,8 @@ $RUNTIME_NOSETUPFS=true;
 
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
 OCP\JSON::checkAppEnabled('bookmarks');
 
 $CONFIG_DBTYPE = OCP\Config::getSystemValue( "dbtype", "sqlite" );
diff --git a/apps/bookmarks/lib/bookmarks.php b/apps/bookmarks/lib/bookmarks.php
index e0005968f31525d0c2e326979fadade63cb6ebe1..86fba45a50ce57c1ab132ebb751d389cc1e322f0 100644
--- a/apps/bookmarks/lib/bookmarks.php
+++ b/apps/bookmarks/lib/bookmarks.php
@@ -145,5 +145,4 @@ class OC_Bookmarks_Bookmarks{
 		$result = $query->execute();
 		return true;
 	}
-}
-?>
+}
\ No newline at end of file
diff --git a/apps/calendar/ajax/calendar/activation.php b/apps/calendar/ajax/calendar/activation.php
index e31908beb14c28d8ef5c9be678d52f78acdac9e7..f4aadc5b017c1d4f33da7c849c68b37b554ed657 100644
--- a/apps/calendar/ajax/calendar/activation.php
+++ b/apps/calendar/ajax/calendar/activation.php
@@ -9,6 +9,8 @@
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
+
 $calendarid = $_POST['calendarid'];
 $calendar = OC_Calendar_App::getCalendar($calendarid, true);
 if(!$calendar){
diff --git a/apps/calendar/ajax/calendar/delete.php b/apps/calendar/ajax/calendar/delete.php
index 4d6706f60028e8ef90dcb55c407f8e19f447e6df..089255cae39c4cfacfe0446c8c777b3ecce0cc6f 100644
--- a/apps/calendar/ajax/calendar/delete.php
+++ b/apps/calendar/ajax/calendar/delete.php
@@ -9,6 +9,7 @@
 
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 $cal = $_POST["calendarid"];
 $calendar = OC_Calendar_App::getCalendar($cal, true);
diff --git a/apps/calendar/ajax/calendar/new.php b/apps/calendar/ajax/calendar/new.php
index e77d4ebff03cc6bd55dfaef0a5433a2f5442a3b7..67d128223783172d5839c99991159fc8d9c834db 100644
--- a/apps/calendar/ajax/calendar/new.php
+++ b/apps/calendar/ajax/calendar/new.php
@@ -6,11 +6,10 @@
  * See the COPYING-README file.
  */
 
- 
-
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 if(trim($_POST['name']) == ''){
 	OCP\JSON::error(array('message'=>'empty'));
diff --git a/apps/calendar/ajax/calendar/update.php b/apps/calendar/ajax/calendar/update.php
index a2c898c8075c2e08ac8eefe5f33eea0d5d4b7726..c09b1008c9c2ca145fee1239cbdbf2e11d15d6e4 100644
--- a/apps/calendar/ajax/calendar/update.php
+++ b/apps/calendar/ajax/calendar/update.php
@@ -11,6 +11,7 @@
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 if(trim($_POST['name']) == ''){
 	OCP\JSON::error(array('message'=>'empty'));
diff --git a/apps/calendar/ajax/categories/rescan.php b/apps/calendar/ajax/categories/rescan.php
index f0060cb23b29e65b66ea58c0caf8ccc0e91d3ff7..08c32865b6f1c82a29e28f23921d1effef6c490e 100644
--- a/apps/calendar/ajax/categories/rescan.php
+++ b/apps/calendar/ajax/categories/rescan.php
@@ -9,6 +9,7 @@
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 foreach ($_POST as $key=>$element) {
 	debug('_POST: '.$key.'=>'.print_r($element, true));
diff --git a/apps/calendar/ajax/event/delete.php b/apps/calendar/ajax/event/delete.php
index f183d431afaddad95a33a8b272467263b5d31bef..17e45c001e8093f974db3e3eb58d335fcc976a2d 100644
--- a/apps/calendar/ajax/event/delete.php
+++ b/apps/calendar/ajax/event/delete.php
@@ -9,6 +9,7 @@
 
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 $id = $_POST['id'];
 $access = OC_Calendar_App::getaccess($id, OC_Calendar_App::EVENT);
diff --git a/apps/calendar/ajax/event/edit.php b/apps/calendar/ajax/event/edit.php
index 1c3babc3d90909ed0bde5fe43f98ab9da4b5ab32..db78bf6e5e0c6603a6ac5c958570abd40cf2d947 100644
--- a/apps/calendar/ajax/event/edit.php
+++ b/apps/calendar/ajax/event/edit.php
@@ -9,6 +9,7 @@
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 $id = $_POST['id'];
 
diff --git a/apps/calendar/ajax/event/move.php b/apps/calendar/ajax/event/move.php
index 04cf2fb0513065d65a2c07fcf659e31e01a91105..f4e2b36376d415c1c87803ce63d777dbee90d7e8 100644
--- a/apps/calendar/ajax/event/move.php
+++ b/apps/calendar/ajax/event/move.php
@@ -7,6 +7,7 @@
  */
  
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 $id = $_POST['id'];
 $access = OC_Calendar_App::getaccess($id, OC_Calendar_App::EVENT);
diff --git a/apps/calendar/ajax/event/new.form.php b/apps/calendar/ajax/event/new.form.php
index 0b19e7e92f96495b90588cb148a2ce77538b750b..db04cdf2d495cf6720ffadedfc10dce294c371ca 100644
--- a/apps/calendar/ajax/event/new.form.php
+++ b/apps/calendar/ajax/event/new.form.php
@@ -27,7 +27,7 @@ if (!$end){
 }
 $start = new DateTime('@'.$start);
 $end = new DateTime('@'.$end);
-$timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+$timezone = OC_Calendar_App::getTimezone();
 $start->setTimezone(new DateTimeZone($timezone));
 $end->setTimezone(new DateTimeZone($timezone));
 
diff --git a/apps/calendar/ajax/event/new.php b/apps/calendar/ajax/event/new.php
index 30e2b0cae36f428df2cc01472c810eb0dd994c3c..bc0439cc3155b794cd8b9e2eb724245fc0d09588 100644
--- a/apps/calendar/ajax/event/new.php
+++ b/apps/calendar/ajax/event/new.php
@@ -10,6 +10,7 @@
 
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 $errarr = OC_Calendar_Object::validateRequest($_POST);
 if($errarr){
diff --git a/apps/calendar/ajax/event/resize.php b/apps/calendar/ajax/event/resize.php
index 56b83205e851522e8238e61b151b2a4565a0749f..15b687b55da62b484c79477a99906449b2d49a58 100644
--- a/apps/calendar/ajax/event/resize.php
+++ b/apps/calendar/ajax/event/resize.php
@@ -7,6 +7,7 @@
  */
  
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 $id = $_POST['id'];
 
diff --git a/apps/calendar/ajax/import/calendarcheck.php b/apps/calendar/ajax/import/calendarcheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..a91bab7057375255ed636fba22847b2afb397047
--- /dev/null
+++ b/apps/calendar/ajax/import/calendarcheck.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+OCP\JSON::checkLoggedIn();
+OCP\App::checkAppEnabled('calendar');
+$calname = strip_tags($_POST['calname']);
+$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser());
+foreach($calendars as $calendar){
+	if($calendar['displayname'] == $calname){
+		OCP\JSON::success(array('message'=>'exists'));
+		exit;
+	}
+}
+OCP\JSON::error();
\ No newline at end of file
diff --git a/apps/calendar/ajax/import/dialog.php b/apps/calendar/ajax/import/dialog.php
index b99c32278c46c3c02f0f3c131966e1fff574935a..18fe226172cafc3838e496cb5bd5e89d486da514 100644
--- a/apps/calendar/ajax/import/dialog.php
+++ b/apps/calendar/ajax/import/dialog.php
@@ -5,8 +5,6 @@
  * later.
  * See the COPYING-README file.
  */
-
- 
 OCP\JSON::checkLoggedIn();
 OCP\App::checkAppEnabled('calendar');
 $tmpl = new OCP\Template('calendar', 'part.import');
diff --git a/apps/calendar/ajax/import/dropimport.php b/apps/calendar/ajax/import/dropimport.php
index 87667d4de685d71d1a781c37b6bf0577f21a216a..f46e731409855f6f11b994007fba5c2032fb3fb9 100644
--- a/apps/calendar/ajax/import/dropimport.php
+++ b/apps/calendar/ajax/import/dropimport.php
@@ -1,73 +1,32 @@
 <?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
 $data = $_POST['data'];
 $data = explode(',', $data);
 $data = end($data);
 $data = base64_decode($data);
 OCP\JSON::checkLoggedIn();
 OCP\App::checkAppEnabled('calendar');
-$nl="\r\n";
-$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true);
-$data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
-$lines = explode("\n", $data);
-unset($data);
-$comp=$uid=$cal=false;
-$cals=$uids=array();
-$i = 0;
-foreach($lines as $line) {
-	if(strpos($line, ':')!==false) {
-		list($attr, $val) = explode(':', strtoupper($line));
-		if ($attr == 'BEGIN' && $val == 'VCALENDAR') {
-			$cal = $i;
-			$cals[$cal] = array('first'=>$i,'last'=>$i,'end'=>$i);
-		} elseif ($attr =='BEGIN' && $cal!==false && isset($comps[$val])) {
-			$comp = $val;
-			$beginNo = $i;
-		} elseif ($attr == 'END' && $cal!==false && $val == 'VCALENDAR') {
-			if($comp!==false) {
-				unset($cals[$cal]); // corrupt calendar, unset it
-			} else {
-				$cals[$cal]['end'] = $i;
-			}
-			$comp=$uid=$cal=false; // reset calendar
-		} elseif ($attr == 'END' && $comp!==false && $val == $comp) {
-			if(! $uid) {
-				$uid = OC_Calendar_Object::createUID();
-			}
-			$uids[$uid][$beginNo] = array('end'=>$i, 'cal'=>$cal);
-			if ($cals[$cal]['first'] == $cal) {
-				$cals[$cal]['first'] = $beginNo;
-			}
-			$cals[$cal]['last'] = $i;
-			$comp=$uid=false; // reset component
-		} elseif ($attr =="UID" && $comp!==false) {
-			list($attr, $uid) = explode(':', $line);
-		}
-	}
-	$i++;
+$import = new OC_Calendar_Import($data);
+$import->setUserID(OCP\User::getUser());
+$import->setTimeZone(OC_Calendar_App::$tz);
+$import->disableProgressCache();
+if(!$import->isValid()){
+	OCP\JSON::error();
+	exit;
 }
-$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), true);
-$id = $calendars[0]['id'];
-foreach($uids as $uid) {
-	$prefix=$suffix=$content=array();
-	foreach($uid as $begin=>$details) {
-		$cal = $details['cal'];
-		if(!isset($cals[$cal])) {
-			continue; // from corrupt/incomplete calendar
-		}
-		$cdata = $cals[$cal];
-		// if we have multiple components from different calendar objects,
-		// we should really merge their elements (enhancement?) -- 1st one wins for now.
-		if(! count($prefix)) {
-			$prefix = array_slice($lines, $cal, $cdata['first'] - $cal);
-		}
-		if(! count($suffix)) {
-			$suffix = array_slice($lines, $cdata['last']+1, $cdata['end'] - $cdata['last']);
-		}
-		$content = array_merge($content, array_slice($lines, $begin, $details['end'] - $begin + 1));
-	}
-	if(count($content)) {
-		$import = join($nl, array_merge($prefix, $content, $suffix)) . $nl;
-		OC_Calendar_Object::add($id, $import);
-	}
-}
-OCP\JSON::success();
\ No newline at end of file
+$newcalendarname = strip_tags($import->createCalendarName());
+$newid = OC_Calendar_Calendar::addCalendar(OCP\User::getUser(),$newcalendarname,'VEVENT,VTODO,VJOURNAL',null,0,$import->createCalendarColor());
+$import->setCalendarID($newid);
+$import->import();
+$count = $import->getCount();
+if($count == 0){
+	OC_Calendar_Calendar::deleteCalendar($newid);
+	OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.')));
+}else{
+	OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . $newcalendarname, 'eventSource'=>OC_Calendar_Calendar::getEventSourceInfo(OC_Calendar_Calendar::find($newid))));
+}
\ No newline at end of file
diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php
index 1facedfe0dae2b522eb760e476904387dbe67233..b1dfc464d009044f814b54288b507e430c09dd07 100644
--- a/apps/calendar/ajax/import/import.php
+++ b/apps/calendar/ajax/import/import.php
@@ -5,41 +5,71 @@
  * later.
  * See the COPYING-README file.
  */
-//check for calendar rights or create new one
-ob_start();
-
 OCP\JSON::checkLoggedIn();
 OCP\App::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 session_write_close();
-
-$nl="\r\n";
-$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true);
-
-global $progresskey;
-$progresskey = 'calendar.import-' . $_POST['progresskey'];
-
-if (isset($_POST['progress']) && $_POST['progress']) {
-	echo OC_Cache::get($progresskey);
-	die;
+if (isset($_POST['progresskey']) && isset($_POST['getprogress'])) {
+	echo OCP\JSON::success(array('percent'=>OC_Cache::get($_POST['progresskey'])));
+	exit;
 }
-
-function writeProgress($pct) {
-	global $progresskey;
-	OC_Cache::set($progresskey, $pct, 300);
-}
-writeProgress('10');
 $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']);
+if(!$file){
+	OCP\JSON::error(array('error'=>'404'));
+}
+$import = new OC_Calendar_Import($file);
+$import->setUserID(OCP\User::getUser());
+$import->setTimeZone(OC_Calendar_App::$tz);
+$import->enableProgressCache();
+$import->setProgresskey($_POST['progresskey']);
+if(!$import->isValid()){
+	OCP\JSON::error(array('error'=>'notvalid'));
+	exit;
+}
+$newcal = false;
 if($_POST['method'] == 'new'){
-	$id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), $_POST['calname']);
-	OC_Calendar_Calendar::setCalendarActive($id, 1);
+	$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser());
+	foreach($calendars as $calendar){
+		if($calendar['displayname'] == $_POST['calname']){
+			$id = $calendar['id'];
+			$newcal = false;
+			break;
+		}
+		$newcal = true;
+	}
+	if($newcal){
+		$id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), strip_tags($_POST['calname']),'VEVENT,VTODO,VJOURNAL',null,0,strip_tags($_POST['calcolor']));
+		OC_Calendar_Calendar::setCalendarActive($id, 1);
+	}
 }else{
 	$calendar = OC_Calendar_App::getCalendar($_POST['id']);
 	if($calendar['userid'] != OCP\USER::getUser()){
-		OCP\JSON::error();
+		OCP\JSON::error(array('error'=>'missingcalendarrights'));
 		exit();
 	}
 	$id = $_POST['id'];
 }
+$import->setCalendarID($id);
+try{
+	$import->import();
+}catch (Exception $e) {
+	OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('Import failed'), 'debug'=>$e->getMessage()));
+	//write some log
+}
+$count = $import->getCount();
+if($count == 0){
+	if($newcal){
+		OC_Calendar_Calendar::deleteCalendar($id);
+	}
+	OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.')));
+}else{
+	if($newcal){
+		OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' .  strip_tags($_POST['calname'])));
+	}else{
+		OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in your calendar')));
+	}
+}
+/*		//////////////////////////// Attention: following code is quite painfull !!! ///////////////////////
 writeProgress('20');
 // normalize the newlines
 $file = str_replace(array("\r","\n\n"), array("\n","\n"), $file);
@@ -91,7 +121,6 @@ foreach($lines as $line) {
 // import the calendar
 writeProgress('60');
 foreach($uids as $uid) {
-	
 	$prefix=$suffix=$content=array();
 	foreach($uid as $begin=>$details) {
 		
@@ -119,4 +148,4 @@ foreach($uids as $uid) {
 writeProgress('100');
 sleep(3);
 OC_Cache::remove($progresskey);
-OCP\JSON::success();
\ No newline at end of file
+OCP\JSON::success();*/
diff --git a/apps/calendar/ajax/settings/setfirstday.php b/apps/calendar/ajax/settings/setfirstday.php
index 056a60375247cd22a0734718b5dba5de9d4e56cb..73cf0c19b78fb11cddb1914a8ae579a4ddf67682 100644
--- a/apps/calendar/ajax/settings/setfirstday.php
+++ b/apps/calendar/ajax/settings/setfirstday.php
@@ -7,6 +7,7 @@
  */
  
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 if(isset($_POST["firstday"])){
 	OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'firstday', $_POST["firstday"]);
 	OCP\JSON::success();
diff --git a/apps/calendar/ajax/settings/settimeformat.php b/apps/calendar/ajax/settings/settimeformat.php
index 8e95f6f3bf53142ca853d7b5ee7ab303e812b51f..6136857e2fefdfea75359646ac271c5b8fa8404b 100644
--- a/apps/calendar/ajax/settings/settimeformat.php
+++ b/apps/calendar/ajax/settings/settimeformat.php
@@ -7,6 +7,7 @@
  */
  
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 if(isset($_POST["timeformat"])){
 	OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', $_POST["timeformat"]);
 	OCP\JSON::success();
diff --git a/apps/calendar/ajax/settings/settimezone.php b/apps/calendar/ajax/settings/settimezone.php
index 6d029a6643a506f9a8a71d799e061f587452f3a5..06db66d578e5b590906a4c04c25699e60a97f4e5 100644
--- a/apps/calendar/ajax/settings/settimezone.php
+++ b/apps/calendar/ajax/settings/settimezone.php
@@ -14,6 +14,7 @@ $l=OC_L10N::get('calendar');
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('calendar');
+OCP\JSON::callCheck();
 
 // Get data
 if( isset( $_POST['timezone'] ) ){
diff --git a/apps/calendar/ajax/share/changepermission.php b/apps/calendar/ajax/share/changepermission.php
index e807c164a236926b3a66fc2513cafae661a3c523..5aff7666f79449fd7b9f625be2a59cb36363b5f0 100644
--- a/apps/calendar/ajax/share/changepermission.php
+++ b/apps/calendar/ajax/share/changepermission.php
@@ -5,7 +5,9 @@
  * later.
  * See the COPYING-README file.
  */
- 
+
+OCP\JSON::callCheck();
+
 $id = strip_tags($_POST['id']);
 $idtype = strip_tags($_POST['idtype']);
 $permission = (int) strip_tags($_POST['permission']);
diff --git a/apps/calendar/ajax/share/share.php b/apps/calendar/ajax/share/share.php
index 838db619f62c55d36bed3cfe1ee87d6b959109f3..77e1ab9d657efa8a0bd0bf4bcb32b4a209d4b0f7 100644
--- a/apps/calendar/ajax/share/share.php
+++ b/apps/calendar/ajax/share/share.php
@@ -6,6 +6,8 @@
  * See the COPYING-README file.
  */
  
+OCP\JSON::callCheck();
+
 $id = strip_tags($_POST['id']);
 $idtype = strip_tags($_POST['idtype']);
 switch($idtype){
diff --git a/apps/calendar/ajax/share/unshare.php b/apps/calendar/ajax/share/unshare.php
index 1ce04677fb13c3c1bf37f2ec0f8240b099b3c883..c7c0611318918b387da3bfab9f54bb9f0b5fbe2d 100644
--- a/apps/calendar/ajax/share/unshare.php
+++ b/apps/calendar/ajax/share/unshare.php
@@ -5,7 +5,9 @@
  * later.
  * See the COPYING-README file.
  */
- 
+
+OCP\JSON::callCheck();
+
 $id = strip_tags($_POST['id']);
 $idtype = strip_tags($_POST['idtype']);
 switch($idtype){
diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php
index 3c8cc76133e5976f9219dd53c0ea6993ff44f5e6..4fdba29126285dc07a23ef300b119a8958e7e3f5 100644
--- a/apps/calendar/appinfo/app.php
+++ b/apps/calendar/appinfo/app.php
@@ -9,7 +9,9 @@ OC::$CLASSPATH['OC_Calendar_Repeat'] = 'apps/calendar/lib/repeat.php';
 OC::$CLASSPATH['OC_Calendar_Share'] = 'apps/calendar/lib/share.php';
 OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php';
 OC::$CLASSPATH['OC_Calendar_Export'] = 'apps/calendar/lib/export.php';
+OC::$CLASSPATH['OC_Calendar_Import'] = 'apps/calendar/lib/import.php';
 //General Hooks
+OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'createUser');
 OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser');
 //Repeating Events Hooks
 OCP\Util::connectHook('OC_Calendar', 'addEvent', 'OC_Calendar_Repeat', 'generate');
@@ -23,6 +25,8 @@ OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Share', 'pos
 OCP\Util::addscript('calendar','loader');
 OCP\Util::addscript("3rdparty", "chosen/chosen.jquery.min");
 OCP\Util::addStyle("3rdparty", "chosen/chosen");
+OCP\Util::addStyle('3rdparty/miniColors', 'jquery.miniColors');
+OCP\Util::addscript('3rdparty/miniColors', 'jquery.miniColors.min');
 OCP\App::addNavigationEntry( array(
   'id' => 'calendar_index',
   'order' => 10,
diff --git a/apps/calendar/appinfo/remote.php b/apps/calendar/appinfo/remote.php
index 7ab546245f6668271d9df31296f232dc0d556448..e8f9e80c7a83a52b6f937395410c8d30a9cd1dd9 100644
--- a/apps/calendar/appinfo/remote.php
+++ b/apps/calendar/appinfo/remote.php
@@ -34,6 +34,6 @@ $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
 $server->addPlugin(new Sabre_CalDAV_Plugin());
 $server->addPlugin(new Sabre_DAVACL_Plugin());
 $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload
-
+$server->addPlugin(new Sabre_CalDAV_ICSExportPlugin());
 // And off we go!
 $server->exec();
diff --git a/apps/calendar/appinfo/version b/apps/calendar/appinfo/version
index 2eb3c4fe4eebcdea3da0790cc0ba74cb286ec4f4..cb0c939a936f142669d38cf1ece330266d730965 100644
--- a/apps/calendar/appinfo/version
+++ b/apps/calendar/appinfo/version
@@ -1 +1 @@
-0.5
+0.5.2
diff --git a/apps/calendar/css/import.css b/apps/calendar/css/import.css
new file mode 100644
index 0000000000000000000000000000000000000000..8abdc3aecd156a028ae9a994cf2c8e0df88c9d6f
--- /dev/null
+++ b/apps/calendar/css/import.css
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+#calendar_import_newcalform, #calendar_import_mergewarning, #calendar_import_process, #calendar_import_done{display:none;}
+#calendar_import_process_message, #calendar_import_status, #calendar_import_form_message, #calendar_import_mergewarning{text-align:center;}
+#calendar_import_form_message{font-weight: bold;}
+#calendar_import_newcalendar{width:415px;float:right;}
+#calendar_import_mergewarning{clear: both;}
+#calendar_import_defaultcolors{clear:both;margin: 0 auto;text-align: center;}
+.calendar_import_warning{border-color: #fc3333;}
+.calendar-colorpicker-color{display:inline-block;width:20px;height:5px;margin: 0 auto;cursor:pointer;border:2px solid transparent;}
\ No newline at end of file
diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js
index 004b2386fb19ad6bfea21306cb0aebdf5d70a8e6..25311fa0df49aaf1f7851a701925356f15f87e60 100644
--- a/apps/calendar/js/calendar.js
+++ b/apps/calendar/js/calendar.js
@@ -622,18 +622,11 @@ Calendar={
 			drop:function(e){
 				var files = e.dataTransfer.files;
 				for(var i = 0;i < files.length;i++){
-					var file = files[i]
+					var file = files[i];
 					reader = new FileReader();
 					reader.onload = function(event){
-						if(file.type != 'text/calendar'){
-							$('#notification').html('At least one file don\'t seems to be a calendar file. File skipped.');
-							$('#notification').slideDown();
-							window.setTimeout(function(){$('#notification').slideUp();}, 5000);
-							return false;
-						}else{
-							Calendar.UI.Drop.import(event.target.result);
-							$('#calendar_holder').fullCalendar('refetchEvents');
-						}
+						Calendar.UI.Drop.import(event.target.result);
+						$('#calendar_holder').fullCalendar('refetchEvents');
 					}
 					reader.readAsDataURL(file);
 				}
@@ -641,9 +634,13 @@ Calendar={
 			import:function(data){
 				$.post(OC.filePath('calendar', 'ajax/import', 'dropimport.php'), {'data':data},function(result) {
 					if(result.status == 'success'){
+						$('#calendar_holder').fullCalendar('addEventSource', result.eventSource);
+						$('#notification').html(result.message);
+						$('#notification').slideDown();
+						window.setTimeout(function(){$('#notification').slideUp();}, 5000);
 						return true;
 					}else{
-						$('#notification').html('ownCloud wasn\'t able to import at least one file. File skipped.');
+						$('#notification').html(result.message);
 						$('#notification').slideDown();
 						window.setTimeout(function(){$('#notification').slideUp();}, 5000);
 					}
diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js
index cef95afc3aa46d7aaf1c9e85e09a05f61eb06934..b28d19ab00e6dd6fecb1b8ec78abb8c89b511252 100644
--- a/apps/calendar/js/loader.js
+++ b/apps/calendar/js/loader.js
@@ -5,77 +5,175 @@
  * See the COPYING-README file.
  */
 Calendar_Import={
-	importdialog: function(filename){
-		var path = $('#dir').val();
-		$('body').append('<div id="calendar_import"></div>');
-		$('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:filename, path:path},	function(){Calendar_Import.initdialog(filename);});
+	Store:{
+		file: '',
+		path: '',
+		id: 0,
+		method: '',
+		calname: '',
+		calcolor: '',
+		progresskey: '',
+		percentage: 0
 	},
-	initdialog: function(filename){
-		$('#calendar_import_dialog').dialog({
-			width : 500,
-			close : function() {
-				$(this).dialog('destroy').remove();
-				$('#calendar_import').remove();
+	Dialog:{
+		open: function(filename){
+			OC.addStyle('calendar', 'import');
+			Calendar_Import.Store.file = filename;
+			Calendar_Import.Store.path = $('#dir').val();
+			$('body').append('<div id="calendar_import"></div>');
+			$('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:Calendar_Import.Store.file, path:Calendar_Import.Store.path},function(){
+					Calendar_Import.Dialog.init();
+			});
+		},
+		close: function(){
+			Calendar_Import.reset();
+			$(this).dialog('destroy').remove();
+			$('#calendar_import_dialog').remove();
+		},
+		init: function(){
+			//init dialog
+			$('#calendar_import_dialog').dialog({
+				width : 500,
+				resizable: false,
+				close : function() {
+					Calendar_Import.Dialog.close();
+				}
+			});
+			//init buttons
+			$('#calendar_import_done').click(function(){
+				Calendar_Import.Dialog.close();
+			});
+			$('#calendar_import_submit').click(function(){
+				Calendar_Import.Core.process();
+			});
+			$('#calendar_import_mergewarning').click(function(){
+				$('#calendar_import_newcalendar').attr('value', $('#calendar_import_availablename').val());
+				Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val());
+			});
+			$('#calendar_import_calendar').change(function(){
+				if($('#calendar_import_calendar option:selected').val() == 'newcal'){
+					$('#calendar_import_newcalform').slideDown('slow');
+					Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val());
+				}else{
+					$('#calendar_import_newcalform').slideUp('slow');
+					$('#calendar_import_mergewarning').slideUp('slow');
+				}
+			});
+			$('#calendar_import_newcalendar').keyup(function(){
+				Calendar_Import.Dialog.mergewarning($.trim($('#calendar_import_newcalendar').val()));
+			});
+			$('#calendar_import_newcalendar_color').miniColors({
+				letterCase: 'uppercase'
+			});
+			$('.calendar-colorpicker-color').click(function(){
+				var str = $(this).attr('rel');
+				str = str.substr(1);
+				$('#calendar_import_newcalendar_color').attr('value', str);
+				$(".color-picker").miniColors('value', '#' + str);
+			});
+			//init progressbar
+			$('#calendar_import_progressbar').progressbar({value: Calendar_Import.Store.percentage});
+			Calendar_Import.Store.progresskey = $('#calendar_import_progresskey').val();
+		},
+		mergewarning: function(newcalname){
+			$.post(OC.filePath('calendar', 'ajax/import', 'calendarcheck.php'), {calname: newcalname}, function(data){
+				if(data.message == 'exists'){
+					$('#calendar_import_mergewarning').slideDown('slow');
+				}else{
+					$('#calendar_import_mergewarning').slideUp('slow');
+				}
+			});
+		},
+		update: function(){
+			if(Calendar_Import.Store.percentage == 100){
+				return false;
 			}
-		});
-		$('#import_done_button').click(function(){
-			$('#calendar_import_dialog').dialog('destroy').remove();
-			$('#calendar_import').remove();
-		});
-		$('#progressbar').progressbar({value: 0});
-		$('#startimport').click(function(){
-			var filename = $('#filename').val();
-			var path = $('#path').val();
-			var calid = $('#calendar option:selected').val();
-			if($('#calendar option:selected').val() == 'newcal'){
-				var method = 'new';
-				var calname = $('#newcalendar').val();
-				var calname = $.trim(calname);
-				if(calname == ''){
-					$('#newcalendar').css('background-color', '#FF2626');
-					$('#newcalendar').focus(function(){
-						$('#newcalendar').css('background-color', '#F8F8F8');
-					});
-					return false;
+			$.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: Calendar_Import.Store.progresskey, getprogress: true}, function(data){
+ 				if(data.status == 'success'){
+ 					if(data.percent == null){
+	 					return false;
+ 					}
+ 					Calendar_Import.Store.percentage = parseInt(data.percent);
+					$('#calendar_import_progressbar').progressbar('option', 'value', parseInt(data.percent));
+					if(data.percent < 100 ){
+						window.setTimeout('Calendar_Import.Dialog.update()', 250);
+					}else{
+						$('#calendar_import_done').css('display', 'block');
+					}
+				}else{
+					$('#calendar_import_progressbar').progressbar('option', 'value', 100);
+					$('#calendar_import_progressbar > div').css('background-color', '#FF2626');
+					$('#calendar_import_status').html(data.message);
 				}
-			}else{
-				var method = 'old';
+			});
+			return 0;
+		},
+		warning: function(selector){
+			$(selector).addClass('calendar_import_warning');
+			$(selector).focus(function(){
+				$(selector).removeClass('calendar_import_warning');
+			});
+		}
+	},
+	Core:{
+		process: function(){
+			var validation = Calendar_Import.Core.prepare();
+			if(validation){
+				$('#calendar_import_form').css('display', 'none');
+				$('#calendar_import_process').css('display', 'block');
+				$('#calendar_import_newcalendar').attr('readonly', 'readonly');
+				$('#calendar_import_calendar').attr('disabled', 'disabled');
+				Calendar_Import.Core.send();
+				window.setTimeout('Calendar_Import.Dialog.update()', 250);
 			}
-			$('#newcalendar').attr('readonly', 'readonly');
-			$('#calendar').attr('disabled', 'disabled');
-			var progresskey = $('#progresskey').val();
-			$.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: progresskey, method: String (method), calname: String (calname), path: String (path), file: String (filename), id: String (calid)}, function(data){
+		},
+		send: function(){
+			$.post(OC.filePath('calendar', 'ajax/import', 'import.php'), 
+			{progresskey: Calendar_Import.Store.progresskey, method: String (Calendar_Import.Store.method), calname: String (Calendar_Import.Store.calname), path: String (Calendar_Import.Store.path), file: String (Calendar_Import.Store.file), id: String (Calendar_Import.Store.id), calcolor: String (Calendar_Import.Store.calcolor)}, function(data){
 				if(data.status == 'success'){
-					$('#progressbar').progressbar('option', 'value', 100);
-					$('#import_done').css('display', 'block');
+					$('#calendar_import_progressbar').progressbar('option', 'value', 100);
+					Calendar_Import.Store.percentage = 100;
+					$('#calendar_import_done').css('display', 'block');
+					$('#calendar_import_status').html(data.message);
+				}else{
+					$('#calendar_import_progressbar').progressbar('option', 'value', 100);
+					$('#calendar_import_progressbar > div').css('background-color', '#FF2626');
+					$('#calendar_import_status').html(data.message);
 				}
 			});
-			$('#form_container').css('display', 'none');
-			$('#progressbar_container').css('display', 'block');
-			window.setTimeout('Calendar_Import.getimportstatus(\'' + progresskey + '\')', 500);
-		});
-		$('#calendar').change(function(){
-			if($('#calendar option:selected').val() == 'newcal'){
-				$('#newcalform').slideDown('slow');
+		},
+		prepare: function(){
+			Calendar_Import.Store.id = $('#calendar_import_calendar option:selected').val();
+			if($('#calendar_import_calendar option:selected').val() == 'newcal'){
+				Calendar_Import.Store.method = 'new';
+				Calendar_Import.Store.calname = $.trim($('#calendar_import_newcalendar').val());
+				if(Calendar_Import.Store.calname == ''){
+					Calendar_Import.Dialog.warning('#calendar_import_newcalendar');
+					return false;
+				}
+				Calendar_Import.Store.calcolor = $.trim($('#calendar_import_newcalendar_color').val());
+				if(Calendar_Import.Store.calcolor == ''){
+					Calendar_Import.Store.calcolor = $('.calendar-colorpicker-color:first').attr('rel');
+				}
 			}else{
-				$('#newcalform').slideUp('slow');
+				Calendar_Import.Store.method = 'old';
 			}
-		});
+			return true;
+		}
 	},
-	getimportstatus: function(progresskey){
-		$.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progress:1,progresskey: progresskey}, function(percent){
-			$('#progressbar').progressbar('option', 'value', parseInt(percent));
-			if(percent < 100){
-				window.setTimeout('Calendar_Import.getimportstatus(\'' + progresskey + '\')', 500);
-			}else{
-				$('#import_done').css('display', 'block');
-			}
-		});
+	reset: function(){
+		Calendar_Import.Store.file = '';
+		Calendar_Import.Store.path = '';
+		Calendar_Import.Store.id = 0;
+		Calendar_Import.Store.method = '';
+		Calendar_Import.Store.calname = '';
+		Calendar_Import.Store.progresskey = '';
+		Calendar_Import.Store.percentage = 0;
 	}
 }
 $(document).ready(function(){
 	if(typeof FileActions !== 'undefined'){
-		FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); 
-		FileActions.setDefault('text/calendar','importcal');
+		FileActions.register('text/calendar','importCalendar', '', Calendar_Import.Dialog.open); 
+		FileActions.setDefault('text/calendar','importCalendar');
 	};
 });
diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js
index 03e4217573dab057e295ca2d13b56ec10e39ffaa..60741f2b6fcb45df5d7658bf40d44af5272c28bd 100644
--- a/apps/calendar/js/settings.js
+++ b/apps/calendar/js/settings.js
@@ -34,6 +34,7 @@ $(document).ready(function(){
 	$.getJSON(OC.filePath('calendar', 'ajax/settings', 'timeformat.php'), function(jsondata, status) {
 		$('#' + jsondata.timeformat).attr('selected',true);
 		$('#timeformat').chosen();
+		$('#timeformat_chzn').css('width', '100px');
 	});
 	$.getJSON(OC.filePath('calendar', 'ajax/settings', 'gettimezonedetection.php'), function(jsondata, status){
 		if(jsondata.detection == 'true'){
@@ -43,6 +44,7 @@ $(document).ready(function(){
 	$.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) {
 		$('#' + jsondata.firstday).attr('selected',true);
 		$('#firstday').chosen();
+		$('#firstday_chzn').css('width', '100px');
 	});
 	$('#cleancalendarcache').click(function(){
 		$.getJSON(OC.filePath('calendar', 'ajax/cache', 'rescan.php'), function(){
@@ -55,7 +57,7 @@ function calendarcachecheck(){
 	$.getJSON(OC.filePath('calendar', 'ajax/cache', 'status.php'), function(jsondata, status) {
 		$('#cleancalendarcache').attr('title', jsondata.l10n.text);
 		if(jsondata.status == 'success'){
-			$('#cleancalendarcache').css('background', '#90EE90');
+			$('#cleancalendarcache').css('background', '#F8F8F8');
 			$('#cleancalendarcache').css('color', '#333');
 			$('#cleancalendarcache').css('text-shadow', '#fff 0 1px 0');
 		}else{
diff --git a/apps/calendar/lib/app.php b/apps/calendar/lib/app.php
index 1a13f2958c0dd1fcf61bf23618d8af44c74f790c..29e5ab5b0c8a44349b0e9d4d6a884bd6e7906868 100644
--- a/apps/calendar/lib/app.php
+++ b/apps/calendar/lib/app.php
@@ -9,7 +9,7 @@
  * This class manages our app actions
  */
 OC_Calendar_App::$l10n = new OC_L10N('calendar');
-OC_Calendar_App::$tz = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+OC_Calendar_App::$tz = OC_Calendar_App::getTimezone();
 class OC_Calendar_App{
 	const CALENDAR = 'calendar';
 	const EVENT = 'event';
@@ -282,7 +282,17 @@ class OC_Calendar_App{
 	public static function getWeekofMonth(){
 		return OC_Calendar_Object::getWeekofMonth(self::$l10n);
 	}
-	
+
+	/**
+	 * @return (string) $timezone as set by user or the default timezone
+	 */
+	public static function getTimezone() {
+		return OCP\Config::getUserValue(OCP\User::getUser(),
+						'calendar',
+						'timezone',
+						date_default_timezone_get());
+	}
+
 	/**
 	 * @brief checks the access for a calendar / an event
 	 * @param (int) $id - id of the calendar / event
diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php
index 128b55c48e95f7bb1f20556a1f8bce0e30e5b86e..7778242464ca1622a91c9d513ecf0f9411166e3d 100644
--- a/apps/calendar/lib/calendar.php
+++ b/apps/calendar/lib/calendar.php
@@ -267,8 +267,42 @@ class OC_Calendar_Calendar{
 			'url' => OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'],
 			'backgroundColor' => $calendar['calendarcolor'],
 			'borderColor' => '#888',
-			'textColor' => 'black',
+			'textColor' => self::generateTextColor($calendar['calendarcolor']),
 			'cache' => true,
 		);
 	}
+	
+	/*
+	 * @brief checks if a calendar name is available for a user
+	 * @param string $calendarname 
+	 * @param string $userid
+	 * @return boolean
+	 */
+	public static function isCalendarNameavailable($calendarname, $userid){
+		$calendars = self::allCalendars($userid);
+		foreach($calendars as $calendar){
+			if($calendar['displayname'] == $calendarname){
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	/*
+	 * @brief generates the text color for the calendar
+	 * @param string $calendarcolor rgb calendar color code in hex format (with or without the leading #)
+	 * (this function doesn't pay attention on the alpha value of rgba color codes)
+	 * @return boolean
+	 */
+	public static function generateTextColor($calendarcolor){
+		if(substr_count($calendarcolor, '#') == 1){
+			$calendarcolor = substr($calendarcolor,1);
+		}
+		$red = hexdec(substr($calendarcolor,0,2));
+		$green = hexdec(substr($calendarcolor,2,2));
+		$blue = hexdec(substr($calendarcolor,2,2));
+		//recommendation by W3C
+		$computation = ((($red * 299) + ($green * 587) + ($blue * 114)) / 1000);
+		return ($computation > 130)?'#000000':'#FAFAFA';
+	}
 }
diff --git a/apps/calendar/lib/hooks.php b/apps/calendar/lib/hooks.php
index 328d2951d231cde4669accd7da609c5b730a1449..bc0b02c62b8d851e22a17b3dfa90ff8cc157ba3b 100644
--- a/apps/calendar/lib/hooks.php
+++ b/apps/calendar/lib/hooks.php
@@ -11,7 +11,18 @@
  */
 class OC_Calendar_Hooks{
 	/**
-	 * @brief Deletes all Addressbooks of a certain user
+	 * @brief Creates default calendar for a user
+	 * @param paramters parameters from postCreateUser-Hook
+	 * @return array
+	 */
+	public static function createUser($parameters) {
+		OC_Calendar_Calendar::addCalendar($parameters['uid'],'Default calendar');
+
+		return true;
+	}
+
+	/**
+	 * @brief Deletes all calendars of a certain user
 	 * @param paramters parameters from postDeleteUser-Hook
 	 * @return array
 	 */
diff --git a/apps/calendar/lib/import.php b/apps/calendar/lib/import.php
new file mode 100644
index 0000000000000000000000000000000000000000..d36891cb2b928c02325657548bddf6d5719a5a0d
--- /dev/null
+++ b/apps/calendar/lib/import.php
@@ -0,0 +1,334 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+/*
+ * This class does import and converts all times to the users current timezone
+ */
+class OC_Calendar_Import{
+	/*
+	 * @brief counts the absolute number of parsed elements
+	 */
+	private $abscount;
+	
+	/*
+	 * @brief var saves if the percentage should be saved with OC_Cache
+	 */
+	private $cacheprogress;
+	
+	/*
+	 * @brief Sabre_VObject_Component_VCalendar object - for documentation see http://code.google.com/p/sabredav/wiki/Sabre_VObject_Component_VCalendar
+	 */
+	private $calobject;
+	
+	/*
+	 * @brief var counts the number of imported elements
+	 */
+	private $count;
+	
+	/*
+	 * @brief var to check if errors happend while initialization
+	 */
+	private $error;
+	
+	/*
+	 * @brief var saves the ical string that was submitted with the __construct function
+	 */
+	private $ical;
+	
+	/*
+	 * @brief calendar id for import
+	 */
+	private $id;
+	
+	/*
+	 * @brief var saves the percentage of the import's progress
+	 */
+	private $progress;
+	
+	/*
+	 * @brief var saves the key for the percentage of the import's progress
+	 */
+	private $progresskey;
+	
+	/*
+	 * @brief var saves the timezone the events shell converted to
+	 */
+	private $tz;
+	
+	/*
+	 * @brief var saves the userid
+	 */
+	private $userid;
+
+	/*
+	 * public methods 
+	 */
+	
+	/*
+	 * @brief does general initialization for import object
+	 * @param string $calendar content of ical file
+	 * @param string $tz timezone of the user
+	 * @return boolean
+	 */
+	public function __construct($ical){
+		$this->error = null;
+		$this->ical = $ical;
+		$this->abscount = 0;
+		$this->count = 0;
+		try{
+			$this->calobject = OC_VObject::parse($this->ical);
+		}catch(Exception $e){
+			//MISSING: write some log
+			$this->error = true;
+			return false;
+		}
+		return true;
+	}
+	
+	/*
+	 * @brief imports a calendar
+	 * @return boolean
+	 */
+	public function import(){
+		if(!$this->isValid()){
+			return false;
+		}
+		$numofcomponents = count($this->calobject->getComponents());
+		foreach($this->calobject->getComponents() as $object){
+			if(!($object instanceof Sabre_VObject_Component_VEvent) && !($object instanceof Sabre_VObject_Component_VJournal) && !($object instanceof Sabre_VObject_Component_VTodo)){
+				continue;
+			}
+			$dtend = OC_Calendar_Object::getDTEndFromVEvent($object);
+			$object->DTSTART->getDateTime()->setTimezone(new DateTimeZone($this->tz));
+			$object->DTEND->setDateTime($dtend->getDateTime(), $object->DTSTART->getDateType());
+			$object->DTEND->getDateTime()->setTimezone(new DateTimeZone($this->tz));
+			$vcalendar = $this->createVCalendar($object->serialize());
+			$insertid = OC_Calendar_Object::add($this->id, $vcalendar);
+			$this->abscount++;
+			if($this->isDuplicate($insertid)){
+				OC_Calendar_Object::delete($insertid);
+			}else{
+				$this->count++;
+			}
+			$this->updateProgress(intval(($this->abscount / $numofcomponents)*100));
+		}
+		OC_Cache::remove($this->progresskey);
+		return true;
+	}
+	
+	/*
+	 * @brief sets the timezone
+	 * @return boolean
+	 */
+	public function setTimeZone($tz){
+		$this->tz = $tz;
+		return true;
+	}
+	
+	/*
+	 * @brief sets the progresskey
+	 * @return boolean
+	 */
+	public function setProgresskey($progresskey){
+		$this->progresskey = $progresskey;
+		return true;
+	}
+	
+	/*
+	 * @brief checks if something went wrong while initialization
+	 * @return boolean
+	 */
+	public function isValid(){
+		if(is_null($this->error)){
+			return true;
+		}
+		return false;
+	}
+	
+	/*
+	 * @brief returns the percentage of progress
+	 * @return integer
+	 */
+	public function getProgress(){
+		return $this->progress;
+	}
+	
+	/*
+	 * @brief enables the cache for the percentage of progress
+	 * @return boolean
+	 */
+	public function enableProgressCache(){
+		$this->cacheprogress = true;
+		return true;
+	}
+	
+	/*
+	 * @brief disables the cache for the percentage of progress
+	 * @return boolean
+	 */
+	public function disableProgressCache(){
+		$this->cacheprogress = false;
+		return false;
+	}
+	
+	/*
+	 * @brief generates a new calendar name
+	 * @return string
+	 */
+	public function createCalendarName(){	
+		$calendars = OC_Calendar_Calendar::allCalendars($this->userid);
+		$calendarname = $guessedcalendarname = !is_null($this->guessCalendarName())?($this->guessCalendarName()):(OC_Calendar_App::$l10n->t('New Calendar'));
+		$i = 1;
+		while(!OC_Calendar_Calendar::isCalendarNameavailable($calendarname, $this->userid)){
+			$calendarname = $guessedcalendarname . ' (' . $i . ')';
+			$i++;
+		}
+		return $calendarname;
+	}
+	
+	/*
+	 * @brief generates a new calendar color
+	 * @return string
+	 */
+	public function createCalendarColor(){
+		if(is_null($this->guessCalendarColor())){
+			return '#9fc6e7';
+		}
+		return $this->guessCalendarColor();
+	}
+	
+	/*
+	 * @brief sets the id for the calendar
+	 * @param integer $id of the calendar
+	 * @return boolean
+	 */
+	public function setCalendarID($id){
+		$this->id = $id;
+		return true;
+	}
+	
+	/*
+	 * @brief sets the userid to import the calendar
+	 * @param string $id of the user
+	 * @return boolean
+	 */
+	public function setUserID($userid){
+		$this->userid = $userid;
+		return true;
+	}
+	
+	/*
+	 * @brief returns the private 
+	 * @param string $id of the user
+	 * @return boolean
+	 */
+	public function getCount(){
+		return $this->count;
+	}
+
+	/*
+	 * private methods 
+	 */
+	
+	/*
+	 * @brief generates an unique ID 
+	 * @return string 
+	 */
+	//private function createUID(){
+	//	return substr(md5(rand().time()),0,10);
+	//}
+	
+	/*
+	 * @brief checks is the UID is already in use for another event
+	 * @param string $uid uid to check
+	 * @return boolean
+	 */
+	//private function isUIDAvailable($uid){
+	//	
+	//}
+	
+	/*
+	 * @brief generates a proper VCalendar string
+	 * @param string $vobject
+	 * @return string 
+	 */
+	private function createVCalendar($vobject){
+		if(is_object($vobject)){
+			$vobject = @$vobject->serialize();
+		}
+		$vcalendar = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\n";
+		$vcalendar .= $vobject;
+		$vcalendar .= "END:VCALENDAR";
+		return $vcalendar;
+	}
+	
+	/*
+	 * @brief checks if an event already exists in the user's calendars
+	 * @param integer $insertid id of the new object
+	 * @return boolean
+	 */
+	private function isDuplicate($insertid){
+		$newobject = OC_Calendar_Object::find($insertid);
+		$stmt = OCP\DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*calendar_objects WHERE objecttype=? AND startdate=? AND enddate=? AND repeating=? AND summary=? AND calendardata=?');
+		$result = $stmt->execute(array($newobject['objecttype'],$newobject['startdate'],$newobject['enddate'],$newobject['repeating'],$newobject['summary'],$newobject['calendardata']));
+		$result = $result->fetchRow();
+		if($result['count'] >= 2){
+			return true;
+		}
+		return false;
+	}
+	
+	/*
+	 * @brief updates the progress var
+	 * @param integer $percentage
+	 * @return boolean
+	 */
+	private function updateProgress($percentage){
+		$this->progress = $percentage;
+		if($this->cacheprogress){
+			OC_Cache::set($this->progresskey, $this->progress, 300);
+		}
+		return true;
+	}
+
+	/*
+	 * public methods for (pre)rendering of X-... Attributes
+	 */
+	
+	/*
+	 * @brief guesses the calendar color
+	 * @return mixed - string or boolean
+	 */
+	public function guessCalendarColor(){
+		if(!is_null($this->calobject->__get('X-APPLE-CALENDAR-COLOR'))){
+			return $this->calobject->__get('X-APPLE-CALENDAR-COLOR');
+		}
+		return null;
+	}
+	
+	/*
+	 * @brief guesses the calendar description
+	 * @return mixed - string or boolean
+	 */
+	public function guessCalendarDescription(){
+		if(!is_null($this->calobject->__get('X-WR-CALDESC'))){
+			return $this->calobject->__get('X-WR-CALDESC');
+		}
+		return null;
+	}
+	
+	/*
+	 * @brief guesses the calendar name
+	 * @return mixed - string or boolean
+	 */
+	public function guessCalendarName(){
+		if(!is_null($this->calobject->__get('X-WR-CALNAME'))){
+			return $this->calobject->__get('X-WR-CALNAME');
+		}
+		return null;
+	}
+}
diff --git a/apps/calendar/lib/object.php b/apps/calendar/lib/object.php
index 140542bf4fb901ef44df5dad1a6d40c397d3054b..34676830205af9e6e4daa65e5beeaa8d38010222 100644
--- a/apps/calendar/lib/object.php
+++ b/apps/calendar/lib/object.php
@@ -856,7 +856,7 @@ class OC_Calendar_Object{
 			$vevent->setDateTime('DTSTART', $start, Sabre_VObject_Property_DateTime::DATE);
 			$vevent->setDateTime('DTEND', $end, Sabre_VObject_Property_DateTime::DATE);
 		}else{
-			$timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+			$timezone = OC_Calendar_App::getTimezone();
 			$timezone = new DateTimeZone($timezone);
 			$start = new DateTime($from.' '.$fromtime, $timezone);
 			$end = new DateTime($to.' '.$totime, $timezone);
diff --git a/apps/calendar/lib/search.php b/apps/calendar/lib/search.php
index 560330f65e9221afa06bfe4e0fa0e0d210488b7b..551489672b948926250d9ce4a9d5432929fed502 100644
--- a/apps/calendar/lib/search.php
+++ b/apps/calendar/lib/search.php
@@ -12,7 +12,7 @@ class OC_Search_Provider_Calendar extends OC_Search_Provider{
 		}else{
 			$searchquery[] = $query;
 		}
-		$user_timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+		$user_timezone = OC_Calendar_App::getTimezone();
 		$l = new OC_l10n('calendar');
 		foreach($calendars as $calendar){
 			$objects = OC_Calendar_Object::all($calendar['id']);
diff --git a/apps/calendar/settings.php b/apps/calendar/settings.php
index a18b1ca9f422fd3174f4f275448c77d6a30d687f..eaa20c6c9b4bf540ff83e6dc39a4addfc9f23cb1 100644
--- a/apps/calendar/settings.php
+++ b/apps/calendar/settings.php
@@ -10,6 +10,7 @@ $tmpl = new OCP\Template( 'calendar', 'settings');
 $timezone=OCP\Config::getUserValue(OCP\USER::getUser(),'calendar','timezone','');
 $tmpl->assign('timezone',$timezone);
 $tmpl->assign('timezones',DateTimeZone::listIdentifiers());
+$tmpl->assign('calendars', OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()), false);
 
 OCP\Util::addscript('calendar','settings');
 
diff --git a/apps/calendar/templates/calendar.php b/apps/calendar/templates/calendar.php
index 2246a0178e3128b52ced0de74b54fbb4572e3194..29b9bf6bc549e7a8c9f2bb157219b48a055855b2 100644
--- a/apps/calendar/templates/calendar.php
+++ b/apps/calendar/templates/calendar.php
@@ -2,10 +2,10 @@
 				var defaultView = '<?php echo OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'currentview', 'month') ?>';
 				var eventSources = <?php echo json_encode($_['eventSources']) ?>;
 				var categories = <?php echo json_encode($_['categories']); ?>;
-				var dayNames = <?php echo json_encode($l->tA(array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'))) ?>;
-				var dayNamesShort = <?php echo json_encode($l->tA(array('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'))) ?>;
-				var monthNames = <?php echo json_encode($l->tA(array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'))) ?>;
-				var monthNamesShort = <?php echo json_encode($l->tA(array('Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'))) ?>;
+				var dayNames = new Array("<?php echo $l -> t("Sunday");?>", "<?php echo $l -> t("Monday");?>", "<?php echo $l -> t("Tuesday");?>", "<?php echo $l -> t("Wednesday");?>", "<?php echo $l -> t("Thursday");?>", "<?php echo $l -> t("Friday");?>", "<?php echo $l -> t("Saturday");?>");
+				var dayNamesShort = new Array("<?php echo $l -> t("Sun.");?>", "<?php echo $l -> t("Mon.");?>", "<?php echo $l -> t("Tue.");?>", "<?php echo $l -> t("Wed.");?>", "<?php echo $l -> t("Thu.");?>", "<?php echo $l -> t("Fri.");?>", "<?php echo $l -> t("Sat.");?>");
+				var monthNames = new Array("<?php echo $l -> t("January");?>", "<?php echo $l -> t("February");?>", "<?php echo $l -> t("March");?>", "<?php echo $l -> t("April");?>", "<?php echo $l -> t("May");?>", "<?php echo $l -> t("June");?>", "<?php echo $l -> t("July");?>", "<?php echo $l -> t("August");?>", "<?php echo $l -> t("September");?>", "<?php echo $l -> t("October");?>", "<?php echo $l -> t("November");?>", "<?php echo $l -> t("December");?>");
+				var monthNamesShort = new Array("<?php echo $l -> t("Jan.");?>", "<?php echo $l -> t("Feb.");?>", "<?php echo $l -> t("Mar.");?>", "<?php echo $l -> t("Apr.");?>", "<?php echo $l -> t("May.");?>", "<?php echo $l -> t("Jun.");?>", "<?php echo $l -> t("Jul.");?>", "<?php echo $l -> t("Aug.");?>", "<?php echo $l -> t("Sep.");?>", "<?php echo $l -> t("Oct.");?>", "<?php echo $l -> t("Nov.");?>", "<?php echo $l -> t("Dec.");?>");
 				var agendatime = '<?php echo ((int) OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', '24') == 24 ? 'HH:mm' : 'hh:mm tt'); ?>{ - <?php echo ((int) OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', '24') == 24 ? 'HH:mm' : 'hh:mm tt'); ?>}';
 				var defaulttime = '<?php echo ((int) OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', '24') == 24 ? 'HH:mm' : 'hh:mm tt'); ?>';
 				var allDayText = '<?php echo addslashes($l->t('All day')) ?>';
diff --git a/apps/calendar/templates/part.import.php b/apps/calendar/templates/part.import.php
index 70ff9612157f41757e9966b3cf9d5ebdaa8732e3..2ce3cc34239034362a730aebbb866a3e225fded6 100644
--- a/apps/calendar/templates/part.import.php
+++ b/apps/calendar/templates/part.import.php
@@ -1,30 +1,58 @@
-<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file"); ?>">
-<div id="form_container">
-<input type="hidden" id="filename" value="<?php echo $_['filename'];?>">
-<input type="hidden" id="path" value="<?php echo $_['path'];?>">
-<input type="hidden" id="progresskey" value="<?php echo rand() ?>">
-<p style="text-align:center;"><b><?php echo $l->t('Please choose the calendar'); ?></b></p>
-<select style="width:100%;" id="calendar" name="calendar">
 <?php
+//Prerendering for iCalendar file
+$file = OC_Filesystem::file_get_contents($_['path'] . '/' . $_['filename']);
+if(!$file){
+	OCP\JSON::error(array('error'=>'404'));
+}
+$import = new OC_Calendar_Import($file);
+$import->setUserID(OCP\User::getUser());
+$newcalendarname = strip_tags($import->createCalendarName());
+$guessedcalendarname = strip_tags($import->guessCalendarName());
+$calendarcolor = strip_tags($import->createCalendarColor());
+//loading calendars for select box
 $calendar_options = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser());
 $calendar_options[] = array('id'=>'newcal', 'displayname'=>$l->t('create a new calendar'));
-for($i = 0;$i<count($calendar_options);$i++){
-	$calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname'];
-}
-echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname'));
+$defaultcolors = OC_Calendar_Calendar::getCalendarColorOptions();
 ?>
-</select>
-<div id="newcalform" style="display: none;">
-	<input type="text" style="width: 97%;" placeholder="<?php echo $l->t('Name of new calendar'); ?>" id="newcalendar" name="newcalendar">
-</div>
-<input type="button" value="<?php echo $l->t("Import");?>!" id="startimport">
-</div>
-<div id="progressbar_container" style="display: none">
-<p style="text-align:center;"><b><?php echo $l->t('Importing calendar'); ?></b></p>
-<div id="progressbar"></div>
-<div id="import_done" style="display: none;">
-<p style="text-align:center;"><b><?php echo $l->t('Calendar imported successfully'); ?></b></p>
-<input type="button" value="<?php echo $l->t('Close Dialog'); ?>" id="import_done_button">
+<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file");?>">
+<div id="calendar_import_form">
+	<form>
+		<input type="hidden" id="calendar_import_filename" value="<?php echo $_['filename'];?>">
+		<input type="hidden" id="calendar_import_path" value="<?php echo $_['path'];?>">
+		<input type="hidden" id="calendar_import_progresskey" value="<?php echo rand() ?>">
+		<input type="hidden" id="calendar_import_availablename" value="<?php echo $newcalendarname ?>">
+		<div id="calendar_import_form_message"><?php echo $l->t('Please choose a calendar'); ?></div>
+		<select style="width:100%;" id="calendar_import_calendar" name="calendar_import_calendar">
+		<?php
+		for($i = 0;$i<count($calendar_options);$i++){
+			$calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname'];
+		}
+		echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname'));
+		?>
+		</select>
+		<br><br>
+		<div id="calendar_import_newcalform">
+			<input id="calendar_import_newcalendar_color" class="color-picker" type="hidden" size="6" value="<?php echo substr($calendarcolor,1); ?>">
+			<input id="calendar_import_newcalendar"  class="" type="text" placeholder="<?php echo $l->t('Name of new calendar'); ?>" value="<?php echo $guessedcalendarname ?>"><br>
+			<div id="calendar_import_defaultcolors">
+				<?php
+				foreach($defaultcolors as $color){
+					echo '<span class="calendar-colorpicker-color" rel="' . $color . '" style="background-color: ' . $color .  ';"></span>';
+				}
+				?>
+			</div>
+			<!--<input id="calendar_import_generatename" type="button" class="button" value="<?php echo $l->t('Take an available name!'); ?>"><br>-->
+			<div  id="calendar_import_mergewarning" class="hint"><?php echo $l->t('A Calendar with this name already exists. If you continue anyhow, these calendars will be merged.'); ?></div>
+		</div>
+		<input id="calendar_import_submit" type="button" class="button" value="&raquo; <?php echo $l->t('Import'); ?> &raquo;" id="startimport">
+	<form>
 </div>
+<div id="calendar_import_process">
+	<div id="calendar_import_process_message"></div>
+	<div  id="calendar_import_progressbar"></div>
+	<br>
+	<div id="calendar_import_status" class="hint"></div>
+	<br>
+	<input id="calendar_import_done" type="button" value="<?php echo $l->t('Close Dialog'); ?>">
 </div>
 </div>
\ No newline at end of file
diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php
index 6d018f151101c94485313ee23a461670841ea94c..28c357621a82f7ab218b0ada9fd2affb0f22b54c 100644
--- a/apps/calendar/templates/settings.php
+++ b/apps/calendar/templates/settings.php
@@ -56,6 +56,12 @@
 		<dd><code><?php echo OCP\Util::linkToRemote('caldav'); ?></code></dd>
 		<dt><?php echo $l->t('iOS/OS X'); ?></dt>
 		<dd><code><?php echo OCP\Util::linkToRemote('caldav'); ?>principals/<?php echo OCP\USER::getUser(); ?></code>/</dd>
+		<dt><?php echo $l->t('Read only iCalendar link(s)'); ?></dt>
+		<dd>
+			<?php foreach($_['calendars'] as $calendar) { ?>
+			<a href="<?php echo OCP\Util::linkToRemote('caldav').'calendars/'.OCP\USER::getUser().'/'.rawurlencode($calendar['uri']) ?>?export"><?php echo $calendar['displayname'] ?></a><br />
+			<?php } ?>
+		</dd>
 		</dl>
         </fieldset>
 </form>
diff --git a/apps/contacts/ajax/activation.php b/apps/contacts/ajax/activation.php
index 74cb738ab8fccb39fce648f033d6692151442475..69173c54c44a4e04b13933beaf660939d0f7855a 100644
--- a/apps/contacts/ajax/activation.php
+++ b/apps/contacts/ajax/activation.php
@@ -16,8 +16,12 @@ $bookid = $_POST['bookid'];
 $book = OC_Contacts_App::getAddressbook($bookid);// is owner access check
 
 if(!OC_Contacts_Addressbook::setActive($bookid, $_POST['active'])) {
-	OCP\Util::writeLog('contacts','ajax/activation.php: Error activating addressbook: '.$bookid, OCP\Util::ERROR);
-	OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error (de)activating addressbook.'))));
+	OCP\Util::writeLog('contacts',
+		'ajax/activation.php: Error activating addressbook: '. $bookid, 
+		OCP\Util::ERROR);
+	OCP\JSON::error(array(
+		'data' => array(
+			'message' => OC_Contacts_App::$l10n->t('Error (de)activating addressbook.'))));
 	exit();
 }
 
diff --git a/apps/contacts/ajax/addaddressbook.php b/apps/contacts/ajax/addaddressbook.php
new file mode 100644
index 0000000000000000000000000000000000000000..40773704bb47da76bac054a49480ac0cdeb6833e
--- /dev/null
+++ b/apps/contacts/ajax/addaddressbook.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Copyright (c) 2011-2012 Thomas Tanghus <thomas@tanghus.net>
+ * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+ 
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('contacts');
+OCP\JSON::callCheck();
+require_once 'loghandler.php';
+
+debug('name: '.$_POST['name']);
+
+$userid = OCP\USER::getUser();
+$name = isset($_POST['name'])?trim(strip_tags($_POST['name'])):null;
+$description = isset($_POST['description'])
+	? trim(strip_tags($_POST['description']))
+	: null;
+
+if(is_null($name)) {
+	bailOut('Cannot add addressbook with an empty name.');
+}
+$bookid = OC_Contacts_Addressbook::add($userid, $name, $description);
+if(!$bookid) {
+	bailOut('Error adding addressbook: '.$name);
+}
+
+if(!OC_Contacts_Addressbook::setActive($bookid, 1)) {
+	bailOut('Error activating addressbook.');
+}
+$addressbook = OC_Contacts_App::getAddressbook($bookid);
+OCP\JSON::success(array('data' => $addressbook));
diff --git a/apps/contacts/ajax/addbook.php b/apps/contacts/ajax/addbook.php
index 70f47cc81231245c66c2924a0d546d46e232176d..751185b44fd0da4a95782a12e1f8d7a936ed9683 100644
--- a/apps/contacts/ajax/addbook.php
+++ b/apps/contacts/ajax/addbook.php
@@ -17,4 +17,3 @@ $tmpl = new OCP\Template('contacts', 'part.editaddressbook');
 $tmpl->assign('new', true);
 $tmpl->assign('addressbook', $book);
 $tmpl->printPage();
-?>
diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php
index 12f7bb9db969d705ac77fc6d8d4022ea1e0d39bc..6aaf5a9df35a6b7fca6f8a9ec5282ff25bff9dc2 100644
--- a/apps/contacts/ajax/addcontact.php
+++ b/apps/contacts/ajax/addcontact.php
@@ -37,13 +37,15 @@ $n = trim($_POST['n']);
 
 $vcard = new OC_VObject('VCARD');
 $vcard->setUID();
-$vcard->setString('FN',$fn);
-$vcard->setString('N',$n);
+$vcard->setString('FN', $fn);
+$vcard->setString('N', $n);
 
 $id = OC_Contacts_VCard::add($aid, $vcard, null, $isnew);
 if(!$id) {
-	OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.'))));
-	OCP\Util::writeLog('contacts','ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OCP\Util::ERROR);
+	OCP\JSON::error(array(
+		'data' => array(
+			'message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.'))));
+	OCP\Util::writeLog('contacts', 'ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OCP\Util::ERROR);
 	exit();
 }
 
diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/addproperty.php
index 1b6db0c8f81ac2b20e8ccbf4cec92e7623e74308..58b857547fba061ea996f0dbe34ab03c6e584bce 100644
--- a/apps/contacts/ajax/addproperty.php
+++ b/apps/contacts/ajax/addproperty.php
@@ -25,7 +25,7 @@ OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
 OCP\JSON::callCheck();
 
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 $id = isset($_POST['id'])?$_POST['id']:null;
 $name = isset($_POST['name'])?$_POST['name']:null;
@@ -33,22 +33,27 @@ $value = isset($_POST['value'])?$_POST['value']:null;
 $parameters = isset($_POST['parameters'])?$_POST['parameters']:array();
 
 $vcard = OC_Contacts_App::getContactVCard($id);
+$l10n = OC_Contacts_App::$l10n;
 
 if(!$name) {
-	bailOut(OC_Contacts_App::$l10n->t('element name is not set.'));
+	bailOut($l10n->t('element name is not set.'));
 }
 if(!$id) {
-	bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
+	bailOut($l10n->t('id is not set.'));
 }
 
 if(!$vcard) {
-	bailOut(OC_Contacts_App::$l10n->t('Could not parse contact: ').$id);
+	bailOut($l10n->t('Could not parse contact: ').$id);
 }
 
-if(!is_array($value)){
+if(!is_array($value)) {
 	$value = trim($value);
-	if(!$value && in_array($name, array('TEL', 'EMAIL', 'ORG', 'BDAY', 'URL', 'NICKNAME', 'NOTE'))) {
-		bailOut(OC_Contacts_App::$l10n->t('Cannot add empty property.'));
+	if(!$value 
+		&& in_array(
+		$name, 
+		array('TEL', 'EMAIL', 'ORG', 'BDAY', 'URL', 'NICKNAME', 'NOTE'))
+	) {
+		bailOut($l10n->t('Cannot add empty property.'));
 	}
 } elseif($name === 'ADR') { // only add if non-empty elements.
 	$empty = true;
@@ -59,7 +64,7 @@ if(!is_array($value)){
 		}
 	}
 	if($empty) {
-		bailOut(OC_Contacts_App::$l10n->t('At least one of the address fields has to be filled out.'));
+		bailOut($l10n->t('At least one of the address fields has to be filled out.'));
 	}
 }
 
@@ -68,12 +73,14 @@ $current = $vcard->select($name);
 foreach($current as $item) {
 	$tmpvalue = (is_array($value)?implode(';', $value):$value);
 	if($tmpvalue == $item->value) {
-		bailOut(OC_Contacts_App::$l10n->t('Trying to add duplicate property: '.$name.': '.$tmpvalue));
+		bailOut($l10n->t('Trying to add duplicate property: '.$name.': '.$tmpvalue));
 	}
 }
 
 if(is_array($value)) {
-	ksort($value);  // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form!
+	// NOTE: Important, otherwise the compound value will 
+	// be set in the order the fields appear in the form!
+	ksort($value);  
 	$value = array_map('strip_tags', $value);
 } else {
 	$value = strip_tags($value);
@@ -116,24 +123,25 @@ switch($name) {
 
 $line = count($vcard->children) - 1;
 
-// Apparently Sabre_VObject_Parameter doesn't do well with multiple values or I don't know how to do it. Tanghus.
+// Apparently Sabre_VObject_Parameter doesn't do well with 
+// multiple values or I don't know how to do it. Tanghus.
 foreach ($parameters as $key=>$element) {
 	if(is_array($element) && strtoupper($key) == 'TYPE') { 
 		// NOTE: Maybe this doesn't only apply for TYPE?
 		// And it probably shouldn't be done here anyways :-/
-		foreach($element as $e){
-			if($e != '' && !is_null($e)){
-				$vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key,$e);
+		foreach($element as $e) {
+			if($e != '' && !is_null($e)) {
+				$vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key, $e);
 			}
 		}
 	} else {
-			$vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key,$element);
+			$vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key, $element);
 	}
 }
 $checksum = md5($vcard->children[$line]->serialize());
 
-if(!OC_Contacts_VCard::edit($id,$vcard)) {
-	bailOut(OC_Contacts_App::$l10n->t('Error adding contact property: '.$name));
+if(!OC_Contacts_VCard::edit($id, $vcard)) {
+	bailOut($l10n->t('Error adding contact property: '.$name));
 }
 
 OCP\JSON::success(array('data' => array( 'checksum' => $checksum )));
diff --git a/apps/contacts/ajax/categories/categoriesfor.php b/apps/contacts/ajax/categories/categoriesfor.php
index 846af300de8008d2aa8cca1f5ff30e94000070e3..8391b14b5450f7ab1db0face6bc40714510da632 100644
--- a/apps/contacts/ajax/categories/categoriesfor.php
+++ b/apps/contacts/ajax/categories/categoriesfor.php
@@ -12,17 +12,23 @@ OCP\JSON::checkAppEnabled('contacts');
 
 $id = isset($_GET['id'])?$_GET['id']:null;
 if(is_null($id)) {
-	OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('No ID provided'))));
+	OCP\JSON::error(array(
+		'data' => array(
+			'message' => OC_Contacts_App::$l10n->t('No ID provided'))));
 	exit();
 }
 $vcard = OC_Contacts_App::getContactVCard( $id );
 foreach($vcard->children as $property){
-	//OCP\Util::writeLog('contacts','ajax/categories/checksumfor.php: '.$property->name, OCP\Util::DEBUG);
 	if($property->name == 'CATEGORIES') {
 		$checksum = md5($property->serialize());
-		OCP\JSON::success(array('data' => array('value'=>$property->value, 'checksum'=>$checksum)));
+		OCP\JSON::success(array(
+			'data' => array(
+				'value' => $property->value, 
+				'checksum' => $checksum,
+				)));
 		exit();
 	}
 }
-OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error setting checksum.'))));
-?>
+OCP\JSON::error(array(
+	'data' => array(
+		'message' => OC_Contacts_App::$l10n->t('Error setting checksum.'))));
diff --git a/apps/contacts/ajax/categories/delete.php b/apps/contacts/ajax/categories/delete.php
index 76c23d6487d1747980804fa138230b53b3121862..bc9f3e14e876fd1598d87d205a0c961b115d733b 100644
--- a/apps/contacts/ajax/categories/delete.php
+++ b/apps/contacts/ajax/categories/delete.php
@@ -9,8 +9,9 @@
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
+OCP\JSON::callCheck();
 
-require_once('../loghandler.php');
+require_once __DIR__.'/../loghandler.php';
 
 $categories = isset($_POST['categories'])?$_POST['categories']:null;
 
@@ -45,5 +46,3 @@ $catman->delete($categories, $cards);
 debug('After delete: '.print_r($catman->categories(), true));
 OC_Contacts_VCard::updateDataByID($cards);
 OCP\JSON::success(array('data' => array('categories'=>$catman->categories())));
-
-?>
diff --git a/apps/contacts/ajax/categories/list.php b/apps/contacts/ajax/categories/list.php
index 3ae7635390c8be9277c53b4e42e05f4ce9273a58..f234116ba8c0edc037ea37359e281ae191aaa829 100644
--- a/apps/contacts/ajax/categories/list.php
+++ b/apps/contacts/ajax/categories/list.php
@@ -13,5 +13,3 @@ OCP\JSON::checkAppEnabled('contacts');
 $categories = OC_Contacts_App::getCategories();
 
 OCP\JSON::success(array('data' => array('categories'=>$categories)));
-
-?>
diff --git a/apps/contacts/ajax/categories/rescan.php b/apps/contacts/ajax/categories/rescan.php
index 679f57aa26a271019ce48164609b2b7777cefebd..a06e7803955532caa3fa4f101d5781d2667c2bbe 100644
--- a/apps/contacts/ajax/categories/rescan.php
+++ b/apps/contacts/ajax/categories/rescan.php
@@ -9,25 +9,9 @@
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
+OCP\JSON::callCheck();
 
-require_once(__DIR__.'/../loghandler.php');
-
-$addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser());
-if(count($addressbooks) == 0) {
-	bailOut(OC_Contacts_App::$l10n->t('No address books found.'));
-}
-$addressbookids = array();
-foreach($addressbooks as $addressbook) {
-	$addressbookids[] = $addressbook['id'];
-} 
-$contacts = OC_Contacts_VCard::all($addressbookids);
-if(count($contacts) == 0) {
-	bailOut(OC_Contacts_App::$l10n->t('No contacts found.'));
-}
-
-OC_Contacts_App::scanCategories($contacts);
+OC_Contacts_App::scanCategories();
 $categories = OC_Contacts_App::getCategories();
 
 OCP\JSON::success(array('data' => array('categories'=>$categories)));
-
-?>
diff --git a/apps/contacts/ajax/contactdetails.php b/apps/contacts/ajax/contactdetails.php
index b697b1a8e5be3433244a69c4f66780758d712e0e..27d7611ade904d33cab70f9a3aa5caf1fe3dba49 100644
--- a/apps/contacts/ajax/contactdetails.php
+++ b/apps/contacts/ajax/contactdetails.php
@@ -20,7 +20,7 @@
  *
  */
  
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
@@ -30,6 +30,7 @@ $id = isset($_GET['id'])?$_GET['id']:null;
 if(is_null($id)) {
 	bailOut(OC_Contacts_App::$l10n->t('Missing ID'));
 }
+$card = OC_Contacts_VCard::find($id);
 $vcard = OC_Contacts_App::getContactVCard( $id );
 if(is_null($vcard)) {
 	bailOut(OC_Contacts_App::$l10n->t('Error parsing VCard for ID: "'.$id.'"'));
@@ -50,5 +51,7 @@ if(isset($details['PHOTO'])) {
 	$details['PHOTO'] = false;
 }
 $details['id'] = $id;
+$details['displayname'] = $card['fullname'];
+$details['addressbookid'] = $card['addressbookid'];
 OC_Contacts_App::setLastModifiedHeader($vcard);
 OCP\JSON::success(array('data' => $details));
diff --git a/apps/contacts/ajax/createaddressbook.php b/apps/contacts/ajax/createaddressbook.php
index 2ec5f542bb39805cdc3c8944e6e670f2eebf06a3..8dbd63f6425deb10695c62e4891b05c83df61ec7 100644
--- a/apps/contacts/ajax/createaddressbook.php
+++ b/apps/contacts/ajax/createaddressbook.php
@@ -12,7 +12,7 @@
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
 OCP\JSON::callCheck();
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 $userid = OCP\USER::getUser();
 $name = trim(strip_tags($_POST['name']));
diff --git a/apps/contacts/ajax/currentphoto.php b/apps/contacts/ajax/currentphoto.php
index b10e752c453159a696f0926cd2a827813d956e05..96080e661ef2012840e5cadce0801afbda2c3f05 100644
--- a/apps/contacts/ajax/currentphoto.php
+++ b/apps/contacts/ajax/currentphoto.php
@@ -24,7 +24,7 @@
 OCP\JSON::setContentTypeHeader('text/plain');
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 if (!isset($_GET['id'])) {
 	bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.'));
@@ -51,5 +51,3 @@ if( is_null($contact)) {
 		bailOut(OC_Contacts_App::$l10n->t('The loading photo is not valid.'));
 	}
 }
-
-?>
diff --git a/apps/contacts/ajax/deletecard.php b/apps/contacts/ajax/deletecard.php
index 1161c18abda22808f022333d184846d9d310d4ce..9777046fc829585ad9dd8ad8e218cc7503d2378d 100644
--- a/apps/contacts/ajax/deletecard.php
+++ b/apps/contacts/ajax/deletecard.php
@@ -23,17 +23,7 @@
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
 OCP\JSON::callCheck();
-require_once('loghandler.php');
-
-// foreach($_SERVER as $key=>$value) {
-// 	OCP\Util::writeLog('contacts','ajax/saveproperty.php: _SERVER: '.$key.'=>'.$value, OCP\Util::DEBUG);
-// }
-// foreach($_POST as $key=>$value) {
-// 	debug($key.'=>'.print_r($value, true));
-// }
-// foreach($_GET as $key=>$value) {
-// 	debug($key.'=>'.print_r($value, true));
-// }
+require_once 'loghandler.php';
 
 $id = isset($_POST['id'])?$_POST['id']:null;
 if(!$id) {
diff --git a/apps/contacts/ajax/deleteproperty.php b/apps/contacts/ajax/deleteproperty.php
index 90e5e64903e5058e75fdec6479858be6d57ee3a8..205df8bc18454bf17c5fb65310a580a3c9d00541 100644
--- a/apps/contacts/ajax/deleteproperty.php
+++ b/apps/contacts/ajax/deleteproperty.php
@@ -24,22 +24,23 @@
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
 OCP\JSON::callCheck();
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 $id = $_POST['id'];
 $checksum = $_POST['checksum'];
+$l10n = OC_Contacts_App::$l10n;
 
 $vcard = OC_Contacts_App::getContactVCard( $id );
 $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
-if(is_null($line)){
-	bailOut(OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page.'));
+if(is_null($line)) {
+	bailOut($l10n->t('Information about vCard is incorrect. Please reload the page.'));
 	exit();
 }
 
 unset($vcard->children[$line]);
 
-if(!OC_Contacts_VCard::edit($id,$vcard)) {
-	bailOut(OC_Contacts_App::$l10n->t('Error deleting contact property.'));
+if(!OC_Contacts_VCard::edit($id, $vcard)) {
+	bailOut($l10n->t('Error deleting contact property.'));
 }
 
 OCP\JSON::success(array('data' => array( 'id' => $id )));
diff --git a/apps/contacts/ajax/editaddress.php b/apps/contacts/ajax/editaddress.php
index 2d7aba11b0ef9dc4b5d8bbf7fb86d173e1cc3d47..b5e4b72ed57d85607d7ab05ad92c80d7c8954b58 100644
--- a/apps/contacts/ajax/editaddress.php
+++ b/apps/contacts/ajax/editaddress.php
@@ -34,10 +34,8 @@ if($checksum) {
 	$tmpl->assign('adr', $adr, false);
 }
 
-$tmpl->assign('id',$id);
-$tmpl->assign('adr_types',$adr_types);
+$tmpl->assign('id', $id);
+$tmpl->assign('adr_types', $adr_types);
 
 $page = $tmpl->fetchPage();
 OCP\JSON::success(array('data' => array('page'=>$page, 'checksum'=>$checksum)));
-
-?>
diff --git a/apps/contacts/ajax/editaddressbook.php b/apps/contacts/ajax/editaddressbook.php
index 7a9b757ae0db4c51921e4f663cb255c7daf91b33..4bc77302e5b11726b08d40a5581d14b78bf5d01a 100644
--- a/apps/contacts/ajax/editaddressbook.php
+++ b/apps/contacts/ajax/editaddressbook.php
@@ -14,4 +14,3 @@ $tmpl = new OCP\Template("contacts", "part.editaddressbook");
 $tmpl->assign('new', false);
 $tmpl->assign('addressbook', $addressbook);
 $tmpl->printPage();
-?>
diff --git a/apps/contacts/ajax/editname.php b/apps/contacts/ajax/editname.php
index 868ca222e0a71f28b51bf73f1ba645eecaf8dd3e..eb55634011df5b6ec00bc456bd7aaad17b2c3c9d 100644
--- a/apps/contacts/ajax/editname.php
+++ b/apps/contacts/ajax/editname.php
@@ -9,7 +9,7 @@
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 $tmpl = new OCP\Template("contacts", "part.edit_name_dialog");
 
@@ -25,12 +25,10 @@ if($id) {
 		}
 	}
 	$name = array_map('htmlspecialchars', $name['value']);
-	$tmpl->assign('name',$name, false);
-	$tmpl->assign('id',$id, false);
+	$tmpl->assign('name', $name, false);
+	$tmpl->assign('id', $id, false);
 } else {
 	bailOut(OC_Contacts_App::$l10n->t('Contact ID is missing.'));
 }
 $page = $tmpl->fetchPage();
 OCP\JSON::success(array('data' => array('page'=>$page)));
-
-?>
diff --git a/apps/contacts/ajax/importaddressbook.php b/apps/contacts/ajax/importaddressbook.php
deleted file mode 100644
index f93bbfa4d9dd2649a852511ba6589267e1e3462b..0000000000000000000000000000000000000000
--- a/apps/contacts/ajax/importaddressbook.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-OCP\JSON::checkLoggedIn();
-OCP\App::checkAppEnabled('contacts');
-$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
-$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
-$maxUploadFilesize = min($upload_max_filesize, $post_max_size);
-
-$freeSpace=OC_Filesystem::free_space('/');
-$freeSpace=max($freeSpace,0);
-$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace);
-
-$tmpl = new OCP\Template('contacts', 'part.importaddressbook');
-$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
-$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
-$tmpl->printpage();
-?>
diff --git a/apps/contacts/ajax/importdialog.php b/apps/contacts/ajax/importdialog.php
index 5f8805a6106e9c87f5cb5597f02653f14586c913..691522538fbe99b8630375982aa7a6d398544d20 100644
--- a/apps/contacts/ajax/importdialog.php
+++ b/apps/contacts/ajax/importdialog.php
@@ -13,4 +13,3 @@ $tmpl = new OCP\Template('contacts', 'part.import');
 $tmpl->assign('path', $_POST['path']);
 $tmpl->assign('filename', $_POST['filename']);
 $tmpl->printpage();
-?>
diff --git a/apps/contacts/ajax/loadcard.php b/apps/contacts/ajax/loadcard.php
index 1309faaa7a2a7a9a77beb6aa7713135eb3f3253e..75fe33ada6f06b520ebe6be694dfdda1b0d4bac9 100644
--- a/apps/contacts/ajax/loadcard.php
+++ b/apps/contacts/ajax/loadcard.php
@@ -30,20 +30,20 @@ $maxUploadFilesize = min($upload_max_filesize, $post_max_size);
 $requesttoken = $_GET['requesttoken'];
 
 $freeSpace=OC_Filesystem::free_space('/');
-$freeSpace=max($freeSpace,0);
-$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace);
+$freeSpace=max($freeSpace, 0);
+$maxUploadFilesize = min($maxUploadFilesize, $freeSpace);
 $adr_types = OC_Contacts_App::getTypesOfProperty('ADR');
 $phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
 $email_types = OC_Contacts_App::getTypesOfProperty('EMAIL');
 
-$tmpl = new OCP\Template('contacts','part.contact');
+$tmpl = new OCP\Template('contacts', 'part.contact');
 $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
 $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
-$tmpl->assign('adr_types',$adr_types);
-$tmpl->assign('phone_types',$phone_types);
-$tmpl->assign('email_types',$email_types);
+$tmpl->assign('adr_types', $adr_types);
+$tmpl->assign('phone_types', $phone_types);
+$tmpl->assign('email_types', $email_types);
 $tmpl->assign('requesttoken', $requesttoken);
-$tmpl->assign('id','');
+$tmpl->assign('id', '');
 $page = $tmpl->fetchPage();
 
 OCP\JSON::success(array('data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/loadintro.php b/apps/contacts/ajax/loadintro.php
index 6e8fcc4b04903769f8b41374263ca921af2c5391..1da08950ca0d001618aaf6d568dad8cf80cabd6f 100644
--- a/apps/contacts/ajax/loadintro.php
+++ b/apps/contacts/ajax/loadintro.php
@@ -25,7 +25,7 @@ OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
 
 
-$tmpl = new OCP\Template('contacts','part.no_contacts');
+$tmpl = new OCP\Template('contacts', 'part.no_contacts');
 $page = $tmpl->fetchPage();
 
 OCP\JSON::success(array('data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php
deleted file mode 100644
index a35631055eba7f82b657c49b13c761888f2d0eb1..0000000000000000000000000000000000000000
--- a/apps/contacts/ajax/loadphoto.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-/**
- * ownCloud - Addressbook
- *
- * @author Thomas Tanghus
- * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
- 
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('contacts');
-
-require_once('loghandler.php');
-
-$id = isset($_GET['id']) ? $_GET['id'] : '';
-$refresh = isset($_GET['refresh']) ? true : false;
-
-if($id == '') {
-	bailOut(OC_Contacts_App::$l10n->t('Missing contact id.'));
-}
-
-$checksum = '';
-$vcard = OC_Contacts_App::getContactVCard( $id );
-foreach($vcard->children as $property){
-	if($property->name == 'PHOTO') {
-		$checksum = md5($property->serialize());
-		break;
-	}
-}
-
-OCP\JSON::success(array('data' => array('checksum'=>$checksum)));
-
diff --git a/apps/contacts/ajax/loghandler.php b/apps/contacts/ajax/loghandler.php
index 831b2e50c1eb7639601d5e779ba0868ecb454c2b..be4b98c111221eddf4683d629bf05e49fe1ba4a1 100644
--- a/apps/contacts/ajax/loghandler.php
+++ b/apps/contacts/ajax/loghandler.php
@@ -20,13 +20,15 @@
  *
  */
 
-function bailOut($msg, $tracelevel=1, $debuglevel=OCP\Util::ERROR) {
+function bailOut($msg, $tracelevel=1, $debuglevel=OCP\Util::ERROR) 
+{
 	OCP\JSON::error(array('data' => array('message' => $msg)));
 	debug($msg, $tracelevel, $debuglevel);
 	exit();
 }
 
-function debug($msg, $tracelevel=0, $debuglevel=OCP\Util::DEBUG) {
+function debug($msg, $tracelevel=0, $debuglevel=OCP\Util::DEBUG) 
+{
 	if(PHP_VERSION >= "5.4") {
 		$call = debug_backtrace(false, $tracelevel+1);
 	} else {
@@ -35,6 +37,8 @@ function debug($msg, $tracelevel=0, $debuglevel=OCP\Util::DEBUG) {
 	error_log('trace: '.print_r($call, true));
 	$call = $call[$tracelevel];
 	if($debuglevel !== false) {
-		OCP\Util::writeLog('contacts', $call['file'].'. Line: '.$call['line'].': '.$msg, $debuglevel);
+		OCP\Util::writeLog('contacts', 
+			$call['file'].'. Line: '.$call['line'].': '.$msg, 
+			$debuglevel);
 	}
 }
diff --git a/apps/contacts/ajax/oc_photo.php b/apps/contacts/ajax/oc_photo.php
index 5c50ba92dbe20577916ff30ba72933a3821d2ce4..fe37b530f82501ee26bc98044083b5c1e072ad0f 100644
--- a/apps/contacts/ajax/oc_photo.php
+++ b/apps/contacts/ajax/oc_photo.php
@@ -22,7 +22,7 @@
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 if(!isset($_GET['id'])) {
 	bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.'));
@@ -50,7 +50,9 @@ if($image->width() > 400 || $image->height() > 400) {
 	$image->resize(400); // Prettier resizing than with browser and saves bandwidth.
 }
 if(!$image->fixOrientation()) { // No fatal error so we don't bail out.
-	OCP\Util::writeLog('contacts','ajax/oc_photo.php: Couldn\'t save correct image orientation: '.$localpath, OCP\Util::DEBUG);
+	OCP\Util::writeLog('contacts', 
+		'ajax/oc_photo.php: Couldn\'t save correct image orientation: '.$localpath, 
+		OCP\Util::DEBUG);
 }
 if(OC_Cache::set($tmpkey, $image->data(), 600)) {
 	OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey)));
@@ -58,5 +60,3 @@ if(OC_Cache::set($tmpkey, $image->data(), 600)) {
 } else {
 	bailOut('Couldn\'t save temporary image: '.$tmpkey);
 }
-
-?>
diff --git a/apps/contacts/ajax/savecrop.php b/apps/contacts/ajax/savecrop.php
index adce6be3b39fa2755a9594a902df6168373e3fd5..8ee2e46bf0c36da6d187eed6191b0cdbc890558b 100644
--- a/apps/contacts/ajax/savecrop.php
+++ b/apps/contacts/ajax/savecrop.php
@@ -27,7 +27,7 @@ OCP\JSON::callCheck();
 // Firefox and Konqueror tries to download application/json for me.  --Arthur
 OCP\JSON::setContentTypeHeader('text/plain');
 
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 $image = null;
 
@@ -48,7 +48,7 @@ if($id == '') {
 	bailOut('Missing contact id.');
 }
 
-OCP\Util::writeLog('contacts','savecrop.php: key: '.$tmpkey, OCP\Util::DEBUG);
+OCP\Util::writeLog('contacts', 'savecrop.php: key: '.$tmpkey, OCP\Util::DEBUG);
 
 $data = OC_Cache::get($tmpkey);
 if($data) {
@@ -56,7 +56,9 @@ if($data) {
 	if($image->loadFromdata($data)) {
 		$w = ($w != -1 ? $w : $image->width());
 		$h = ($h != -1 ? $h : $image->height());
-		OCP\Util::writeLog('contacts','savecrop.php, x: '.$x1.' y: '.$y1.' w: '.$w.' h: '.$h, OCP\Util::DEBUG);
+		OCP\Util::writeLog('contacts',
+			'savecrop.php, x: '.$x1.' y: '.$y1.' w: '.$w.' h: '.$h, 
+			OCP\Util::DEBUG);
 		if($image->crop($x1, $y1, $w, $h)) {
 			if(($image->width() <= 200 && $image->height() <= 200) || $image->resize(200)) {
 				$card = OC_Contacts_App::getContactVCard($id);
@@ -65,7 +67,9 @@ if($data) {
 					bailOut(OC_Contacts_App::$l10n->t('Error getting contact object.'));
 				}
 				if($card->__isset('PHOTO')) {
-					OCP\Util::writeLog('contacts','savecrop.php: PHOTO property exists.', OCP\Util::DEBUG);
+					OCP\Util::writeLog('contacts',
+						'savecrop.php: PHOTO property exists.', 
+						OCP\Util::DEBUG);
 					$property = $card->__get('PHOTO');
 					if(!$property) {
 						OC_Cache::remove($tmpkey);
@@ -76,12 +80,16 @@ if($data) {
 					$property->parameters[] = new Sabre_VObject_Parameter('TYPE', $image->mimeType());
 					$card->__set('PHOTO', $property);
 				} else {
-					OCP\Util::writeLog('contacts','savecrop.php: files: Adding PHOTO property.', OCP\Util::DEBUG);
-					$card->addProperty('PHOTO', $image->__toString(), array('ENCODING' => 'b', 'TYPE' => $image->mimeType()));
+					OCP\Util::writeLog('contacts',
+						'savecrop.php: files: Adding PHOTO property.', 
+						OCP\Util::DEBUG);
+					$card->addProperty('PHOTO', 
+						$image->__toString(), array('ENCODING' => 'b', 
+						'TYPE' => $image->mimeType()));
 				}
 				$now = new DateTime;
 				$card->setString('REV', $now->format(DateTime::W3C));
-				if(!OC_Contacts_VCard::edit($id,$card)) {
+				if(!OC_Contacts_VCard::edit($id, $card)) {
 					bailOut(OC_Contacts_App::$l10n->t('Error saving contact.'));
 				}
 				$tmpl = new OCP\Template("contacts", "part.contactphoto");
diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php
index 34fc3cc53512b0dfe7c31f4581e15a9d33210710..5d743c99df464a333d94b22f74e50e7d5ec5347b 100644
--- a/apps/contacts/ajax/saveproperty.php
+++ b/apps/contacts/ajax/saveproperty.php
@@ -19,7 +19,7 @@
  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-require_once('loghandler.php');
+require_once 'loghandler.php';
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
@@ -39,9 +39,11 @@ if(!$id) {
 if(!$checksum) {
 	bailOut(OC_Contacts_App::$l10n->t('checksum is not set.'));
 }
-if(is_array($value)){
+if(is_array($value)) {
 	$value = array_map('strip_tags', $value);
-	ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form!
+	// NOTE: Important, otherwise the compound value will be 
+	// set in the order the fields appear in the form!
+	ksort($value); 
 	//if($name == 'CATEGORIES') {
 	//	$value = OC_Contacts_VCard::escapeDelimiters($value, ',');
 	//} else {
@@ -66,8 +68,7 @@ if($element != $name) {
 switch($element) {
 	case 'BDAY':
 		$date = New DateTime($value);
-		//$vcard->setDateTime('BDAY', $date, Sabre_VObject_Element_DateTime::DATE);
-		$value = $date->format(DateTime::ATOM);
+		$value = $date->format('Y-m-d');
 		break;
 	case 'FN':
 		if(!$value) {
@@ -89,6 +90,14 @@ if(!$value) {
 } else {
 	/* setting value */
 	switch($element) {
+		case 'BDAY':
+			// I don't use setDateTime() because that formats it as YYYYMMDD instead of YYYY-MM-DD
+			// which is what the RFC recommends.
+			$vcard->children[$line]->setValue($value);
+			$vcard->children[$line]->parameters = array();
+			$vcard->children[$line]->add(new Sabre_VObject_Parameter('VALUE', 'DATE'));
+			debug('Setting value:'.$name.' '.$vcard->children[$line]);
+			break;
 		case 'CATEGORIES':
 			debug('Setting string:'.$name.' '.$value);
 			$vcard->children[$line]->setValue($value);
@@ -120,7 +129,7 @@ if(!$value) {
 }
 //debug('New checksum: '.$checksum);
 
-if(!OC_Contacts_VCard::edit($id,$vcard)) {
+if(!OC_Contacts_VCard::edit($id, $vcard)) {
 	bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.'));
 	exit();
 }
diff --git a/apps/contacts/ajax/selectaddressbook.php b/apps/contacts/ajax/selectaddressbook.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c35d08c82977a54cd8247eb11f6c392fa1e2f3c
--- /dev/null
+++ b/apps/contacts/ajax/selectaddressbook.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+ 
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('contacts');
+
+$addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser());
+$tmpl = new OCP\Template("contacts", "part.selectaddressbook");
+$tmpl->assign('addressbooks', $addressbooks);
+$page = $tmpl->fetchPage();
+OCP\JSON::success(array('data' => array('page' => $page )));
diff --git a/apps/contacts/ajax/updateaddressbook.php b/apps/contacts/ajax/updateaddressbook.php
index d3a772c727f71a6dd3b913caa66f5ee6ffbfbf85..a14b215843144bc105203c45bd49bcac4ad96156 100644
--- a/apps/contacts/ajax/updateaddressbook.php
+++ b/apps/contacts/ajax/updateaddressbook.php
@@ -11,7 +11,7 @@
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
-require_once('loghandler.php');
+require_once 'loghandler.php';
 
 $bookid = $_POST['id'];
 OC_Contacts_App::getAddressbook($bookid); // is owner access check
diff --git a/apps/contacts/ajax/uploadimport.php b/apps/contacts/ajax/uploadimport.php
index 9511520828f032e392faabcfc3517e27c2715cca..87032b731a5b367354efe8548fb338112222eb48 100644
--- a/apps/contacts/ajax/uploadimport.php
+++ b/apps/contacts/ajax/uploadimport.php
@@ -24,52 +24,57 @@
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('contacts');
 OCP\JSON::callCheck();
-require_once('loghandler.php');
+require_once 'loghandler.php';
+
+$l10n = OC_Contacts_App::$l10n;
 
 $view = OCP\Files::getStorage('contacts');
+if(!$view->file_exists('imports')) {
+	$view->mkdir('imports');
+}
 $tmpfile = md5(rand());
 
 // If it is a Drag'n'Drop transfer it's handled here.
 $fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
 if($fn) {
-	if($view->file_put_contents('/'.$tmpfile, file_get_contents('php://input'))) {
-		OCP\JSON::success(array('data' => array('path'=>'', 'file'=>$tmpfile)));
+	if($view->file_put_contents('/imports/'.$fn, file_get_contents('php://input'))) {
+		OCP\JSON::success(array('data' => array('file'=>$tmpfile, 'name'=>$fn)));
 		exit();
 	} else {
-		bailOut(OC_Contacts_App::$l10n->t('Error uploading contacts to storage.'));
+		bailOut($l10n->t('Error uploading contacts to storage.'));
 	}
 }
 
 // File input transfers are handled here
 if (!isset($_FILES['importfile'])) {
-	OCP\Util::writeLog('contacts','ajax/uploadphoto.php: No file was uploaded. Unknown error.', OCP\Util::DEBUG);
-	OCP\JSON::error(array('data' => array( 'message' => 'No file was uploaded. Unknown error' )));
+	OCP\Util::writeLog('contacts',
+		'ajax/uploadphoto.php: No file was uploaded. Unknown error.', 
+		OCP\Util::DEBUG);
+	OCP\JSON::error(array('
+		data' => array(
+			'message' => 'No file was uploaded. Unknown error' )));
 	exit();
 }
 $error = $_FILES['importfile']['error'];
 if($error !== UPLOAD_ERR_OK) {
 	$errors = array(
-		0=>OC_Contacts_App::$l10n->t("There is no error, the file uploaded with success"),
-		1=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
-		2=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
-		3=>OC_Contacts_App::$l10n->t("The uploaded file was only partially uploaded"),
-		4=>OC_Contacts_App::$l10n->t("No file was uploaded"),
-		6=>OC_Contacts_App::$l10n->t("Missing a temporary folder")
+		0=>$l10n->t("There is no error, the file uploaded with success"),
+		1=>$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
+		2=>$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
+		3=>$l10n->t("The uploaded file was only partially uploaded"),
+		4=>$l10n->t("No file was uploaded"),
+		6=>$l10n->t("Missing a temporary folder")
 	);
 	bailOut($errors[$error]);
 }
 $file=$_FILES['importfile'];
 
-$tmpfname = tempnam(get_temp_dir(), "occOrig");
 if(file_exists($file['tmp_name'])) {
-	if($view->file_put_contents('/'.$tmpfile, file_get_contents($file['tmp_name']))) {
-		OCP\JSON::success(array('data' => array('path'=>'', 'file'=>$tmpfile)));
+	if($view->file_put_contents('/imports/'.$file['name'], file_get_contents($file['tmp_name']))) {
+		OCP\JSON::success(array('data' => array('file'=>$file['name'], 'name'=>$file['name'])));
 	} else {
-		bailOut(OC_Contacts_App::$l10n->t('Error uploading contacts to storage.'));
+		bailOut($l10n->t('Error uploading contacts to storage.'));
 	}
 } else {
 	bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?');
 }
-
-
-?>
diff --git a/apps/contacts/ajax/uploadphoto.php b/apps/contacts/ajax/uploadphoto.php
index 8545ca84eee22653b99a5b2ce1da0faeac56d601..4cd38db8c72a8407e917ec455a4303806ad0119f 100644
--- a/apps/contacts/ajax/uploadphoto.php
+++ b/apps/contacts/ajax/uploadphoto.php
@@ -27,13 +27,13 @@ OCP\JSON::callCheck();
 
 // Firefox and Konqueror tries to download application/json for me.  --Arthur
 OCP\JSON::setContentTypeHeader('text/plain');
-require_once('loghandler.php');
-
+require_once 'loghandler.php';
+$l10n = OC_Contacts_App::$l10n;
 // If it is a Drag'n'Drop transfer it's handled here.
 $fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
 if ($fn) {
 	if (!isset($_GET['id'])) {
-		bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.'));
+		bailOut($l10n->t('No contact ID was submitted.'));
 	}
 	$id = $_GET['id'];
 	$tmpkey = 'contact-photo-'.md5($fn);
@@ -48,33 +48,38 @@ if ($fn) {
 			debug('Couldn\'t save correct image orientation: '.$tmpkey);
 		}
 		if(OC_Cache::set($tmpkey, $image->data(), 600)) {
-			OCP\JSON::success(array('data' => array('mime'=>$_SERVER['CONTENT_TYPE'], 'name'=>$fn, 'id'=>$id, 'tmp'=>$tmpkey)));
+			OCP\JSON::success(array(
+				'data' => array(
+					'mime'=>$_SERVER['CONTENT_TYPE'], 
+					'name'=>$fn, 
+					'id'=>$id, 
+					'tmp'=>$tmpkey)));
 			exit();
 		} else {
-			bailOut(OC_Contacts_App::$l10n->t('Couldn\'t save temporary image: ').$tmpkey);
+			bailOut($l10n->t('Couldn\'t save temporary image: ').$tmpkey);
 		}
 	} else {
-		bailOut(OC_Contacts_App::$l10n->t('Couldn\'t load temporary image: ').$tmpkey);
+		bailOut($l10n->t('Couldn\'t load temporary image: ').$tmpkey);
 	}
 }
 
 // Uploads from file dialog are handled here.
 if (!isset($_POST['id'])) {
-	bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.'));
+	bailOut($l10n->t('No contact ID was submitted.'));
 }
 if (!isset($_FILES['imagefile'])) {
-	bailOut(OC_Contacts_App::$l10n->t('No file was uploaded. Unknown error'));
+	bailOut($l10n->t('No file was uploaded. Unknown error'));
 }
 
 $error = $_FILES['imagefile']['error'];
 if($error !== UPLOAD_ERR_OK) {
 	$errors = array(
-		0=>OC_Contacts_App::$l10n->t("There is no error, the file uploaded with success"),
-		1=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
-		2=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
-		3=>OC_Contacts_App::$l10n->t("The uploaded file was only partially uploaded"),
-		4=>OC_Contacts_App::$l10n->t("No file was uploaded"),
-		6=>OC_Contacts_App::$l10n->t("Missing a temporary folder")
+		0=>$l10n->t("There is no error, the file uploaded with success"),
+		1=>$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
+		2=>$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
+		3=>$l10n->t("The uploaded file was only partially uploaded"),
+		4=>$l10n->t("No file was uploaded"),
+		6=>$l10n->t("Missing a temporary folder")
 	);
 	bailOut($errors[$error]);
 }
@@ -91,15 +96,21 @@ if(file_exists($file['tmp_name'])) {
 			debug('Couldn\'t save correct image orientation: '.$tmpkey);
 		}
 		if(OC_Cache::set($tmpkey, $image->data(), 600)) {
-			OCP\JSON::success(array('data' => array('mime'=>$file['type'],'size'=>$file['size'],'name'=>$file['name'], 'id'=>$_POST['id'], 'tmp'=>$tmpkey)));
+			OCP\JSON::success(array(
+				'data' => array(
+					'mime'=>$file['type'],
+					'size'=>$file['size'],
+					'name'=>$file['name'], 
+					'id'=>$_POST['id'], 
+					'tmp'=>$tmpkey,
+					)));
 			exit();
 		} else {
-			bailOut(OC_Contacts_App::$l10n->t('Couldn\'t save temporary image: ').$tmpkey);
+			bailOut($l10n->t('Couldn\'t save temporary image: ').$tmpkey);
 		}
 	} else {
-		bailOut(OC_Contacts_App::$l10n->t('Couldn\'t load temporary image: ').$file['tmp_name']);
+		bailOut($l10n->t('Couldn\'t load temporary image: ').$file['tmp_name']);
 	}
 } else {
 	bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?');
 }
-?>
diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php
index 64fe00eef10c221d8d49ec860de1e2dada47f9cd..cbbbbc79e58fbf07a026c1e191a4bc44a82ac87d 100644
--- a/apps/contacts/appinfo/app.php
+++ b/apps/contacts/appinfo/app.php
@@ -4,6 +4,7 @@ OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php';
 OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php';
 OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php';
 OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php';
+OC::$CLASSPATH['Sabre_CardDAV_VCFExportPlugin'] = 'apps/contacts/lib/VCFExportPlugin.php';
 OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php';
 OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Contacts_Hooks', 'createUser');
 OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser');
@@ -18,6 +19,6 @@ OCP\App::addNavigationEntry( array(
   'name' => OC_L10N::get('contacts')->t('Contacts') ));
 
 
-OCP\App::registerPersonal('contacts','settings');
+OCP\App::registerPersonal('contacts', 'settings');
 OCP\Util::addscript('contacts', 'loader');
 OC_Search::registerProvider('OC_Search_Provider_Contacts');
diff --git a/apps/contacts/appinfo/database.xml b/apps/contacts/appinfo/database.xml
index 9b269d765dc2082ef64be1ada8509e777e13c869..b814b0f151610e7b86f5f4f79a2ff66c536d242d 100644
--- a/apps/contacts/appinfo/database.xml
+++ b/apps/contacts/appinfo/database.xml
@@ -44,7 +44,7 @@
     <type>text</type>
     <default></default>
     <notnull>false</notnull>
-    <length>100</length>
+    <length>200</length>
    </field>
 
    <field>
@@ -118,7 +118,7 @@
     <type>text</type>
     <default></default>
     <notnull>false</notnull>
-    <length>100</length>
+    <length>200</length>
    </field>
 
    <field>
diff --git a/apps/contacts/appinfo/migrate.php b/apps/contacts/appinfo/migrate.php
index 02026c5979cc439aed89c00dc51c0a47d1e8fafd..2559b4ea456fff75fff3924fb937d1d23a6046db 100644
--- a/apps/contacts/appinfo/migrate.php
+++ b/apps/contacts/appinfo/migrate.php
@@ -2,7 +2,7 @@
 class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
 	
 	// Create the xml for the user supplied
-	function export( ){
+	function export( ) {
 		$options = array(
 			'table'=>'contacts_addressbooks',
 			'matchcol'=>'userid',
@@ -21,9 +21,8 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
 		$ids2 = $this->content->copyRows( $options );
 		
 		// If both returned some ids then they worked
-		if( is_array( $ids ) && is_array( $ids2 ) )
-		{
-			return true;	
+		if(is_array($ids) && is_array($ids2)) {
+			return true;
 		} else {
 			return false;
 		}
@@ -31,14 +30,14 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
 	}
 	
 	// Import function for contacts
-	function import( ){
-		switch( $this->appinfo->version ){
+	function import( ) {
+		switch( $this->appinfo->version ) {
 			default:
 				// All versions of the app have had the same db structure, so all can use the same import function
 				$query = $this->content->prepare( "SELECT * FROM contacts_addressbooks WHERE userid LIKE ?" );
 				$results = $query->execute( array( $this->olduid ) );
 				$idmap = array();
-				while( $row = $results->fetchRow() ){
+				while( $row = $results->fetchRow() ) {
 					// Import each addressbook	
 					$addressbookquery = OCP\DB::prepare( "INSERT INTO *PREFIX*contacts_addressbooks (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)" );
 					$addressbookquery->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) );
@@ -48,7 +47,7 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
 					OC_Contacts_Addressbook::setActive($idmap[$row['id']], true);
 				}
 				// Now tags
-				foreach($idmap as $oldid => $newid){
+				foreach($idmap as $oldid => $newid) {
 					
 					$query = $this->content->prepare( "SELECT * FROM contacts_cards WHERE addressbookid LIKE ?" );
 					$results = $query->execute( array( $oldid ) );
diff --git a/apps/contacts/appinfo/remote.php b/apps/contacts/appinfo/remote.php
index 5add3bc6889651a4a5e3c661eecfbba9e0ad4467..fd5604aec6f340b0e7b68a1418e20291491c7f59 100644
--- a/apps/contacts/appinfo/remote.php
+++ b/apps/contacts/appinfo/remote.php
@@ -22,7 +22,7 @@
 
 OCP\App::checkAppEnabled('contacts');
 
-if(substr($_SERVER["REQUEST_URI"],0,strlen(OC_App::getAppWebPath('contacts').'/carddav.php')) == OC_App::getAppWebPath('contacts').'/carddav.php'){
+if(substr($_SERVER["REQUEST_URI"], 0, strlen(OC_App::getAppWebPath('contacts').'/carddav.php')) == OC_App::getAppWebPath('contacts').'/carddav.php') {
 	$baseuri = OC_App::getAppWebPath('contacts').'/carddav.php';
 }
 
@@ -45,10 +45,11 @@ $nodes = array(
 $server = new Sabre_DAV_Server($nodes);
 $server->setBaseUri($baseuri);
 // Add plugins
-$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
+$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud'));
 $server->addPlugin(new Sabre_CardDAV_Plugin());
 $server->addPlugin(new Sabre_DAVACL_Plugin());
 $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload
+$server->addPlugin(new Sabre_CardDAV_VCFExportPlugin());
 
 // And off we go!
 $server->exec();
diff --git a/apps/contacts/appinfo/update.php b/apps/contacts/appinfo/update.php
index 873899f578bcecc44a7851c07b85b9f99695c5a8..21e736bb446d646043b1233d2a8a7789a0546ed2 100644
--- a/apps/contacts/appinfo/update.php
+++ b/apps/contacts/appinfo/update.php
@@ -1,7 +1,7 @@
 <?php
 
 $installedVersion=OCP\Config::getAppValue('contacts', 'installed_version');
-if (version_compare($installedVersion, '0.2.90', '<')) {
+if (version_compare($installedVersion, '0.2.3', '<')) {
 	// First set all address books in-active.
 	$stmt = OCP\DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET active=0' );
 	$result = $stmt->execute(array());
diff --git a/apps/contacts/appinfo/version b/apps/contacts/appinfo/version
index 7dff5b8921122a487162febe3c8e32effb7acb35..72f9fa8202040af5f91a57193e1ca2d701430618 100644
--- a/apps/contacts/appinfo/version
+++ b/apps/contacts/appinfo/version
@@ -1 +1 @@
-0.2.1
\ No newline at end of file
+0.2.4
\ No newline at end of file
diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php
index e0579e625d7d6f4603017fd0c91c6a422c75e4fb..264eb30836bfdc958b3d36cf4c06c826f8793b58 100644
--- a/apps/contacts/carddav.php
+++ b/apps/contacts/carddav.php
@@ -1,6 +1,6 @@
 <?php
-if(!file_exists('../../lib/base.php')){
+if(!file_exists('../../lib/base.php')) {
 	die('Please update the path to /lib/base.php in carddav.php or make use of /remote.php/carddav/');
 }
-require_once('../../lib/base.php');
-require_once('appinfo/remote.php');
\ No newline at end of file
+require_once '../../lib/base.php';
+require_once 'appinfo/remote.php';
\ No newline at end of file
diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
index 5b6ccb7638a681846f55b8edc79e2906297231bc..ddae27da211bf99009b270c15ca698165c6c0514 100644
--- a/apps/contacts/css/contacts.css
+++ b/apps/contacts/css/contacts.css
@@ -5,17 +5,18 @@
 #leftcontent a { padding: 0 0 0 25px; }
 #rightcontent { top: 3.5em !important; padding-top: 5px; }
 #leftcontent h3 { cursor: pointer; -moz-transition: background 300ms ease 0s; background: none no-repeat scroll 1em center #eee; border-bottom: 1px solid #ddd; border-top: 1px solid #fff; display: block; max-width: 100%; padding: 0.5em 0.8em; color: #666; text-shadow: 0 1px 0 #f8f8f8; font-size: 1.2em; }
-#leftcontent h3:hover,#leftcontent h3:active,#leftcontent h3.active { background-color: #DBDBDB; border-bottom: 1px solid #CCCCCC; border-top: 1px solid #D4D4D4; color: #333333; }
+#leftcontent h3:hover,#leftcontent h3:active,#leftcontent h3.active { background-color: #DBDBDB; border-bottom: 1px solid #CCCCCC; border-top: 1px solid #D4D4D4; color: #333333; font-weight: bold; }
 #contacts { position: fixed; background: #fff; max-width: 100%; width: 20em; left: 12.5em; top: 3.7em; bottom: 3em; overflow: auto; padding: 0; margin: 0; }
 .contacts a { height: 23px; display: block; left: 12.5em; margin: 0 0 0 0; padding: 0 0 0 25px; }
 .contacts li.ui-draggable { height: 23px; }
-.ui-draggable-dragging { width: 16em; }
+.ui-draggable-dragging { width: 17em; cursor: move; }
 .ui-state-hover { border: 1px solid dashed; }
 #bottomcontrols { padding: 0; bottom:0px; height:2.8em; width: 20em; margin:0; background:#eee; border-top:1px solid #ccc; position:fixed; -moz-box-shadow: 0 -3px 3px -3px #000; -webkit-box-shadow: 0 -3px 3px -3px #000; box-shadow: 0 -3px 3px -3px #000;}
 #bottomcontrols img { margin-top: 0.35em; }
-#contacts_newcontact { float: left; margin: 0.2em 0 0 1em; }
+#uploadprogressbar { display: none; padding: 0; bottom: 3em; height:2em; width: 20em; margin:0; background:#eee; border:1px solid #ccc; position:fixed; }
+#contacts_newcontact, #contacts_import, #chooseaddressbook { float: left; margin: 0.2em 0 0 1em; border: 0 none; border-radius: 0; -moz-box-shadow: none; box-shadow: none; outline: 0 none; }
 #chooseaddressbook { float: right; margin: 0.2em 1em 0 0; }
-#actionbar { position: relative; clear: both; height: 30px;}
+#actionbar { clear: both; height: 30px;}
 #contacts_deletecard {position:relative; float:left; background:url('%webroot%/core/img/actions/delete.svg') no-repeat center; }
 #contacts_downloadcard {position:relative; float:left; background:url('%webroot%/core/img/actions/download.svg') no-repeat center; }
 #contacts_propertymenu { clear: left; float:left; max-width: 15em; margin: 2em; }
@@ -71,7 +72,7 @@ label:hover, dt:hover { color: #333; }
 #identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ }
 /*#contact_photo { max-width: 250px; }*/
 #contact_identity { min-width: 30em; }
-.contactsection { position: relative; float: left; /*max-width: 40em;*/ padding: 0.5em; height: auto: border: thin solid lightgray;/* -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8;*/ }
+.contactsection { position: relative; float: left; padding: 0.5em; height: auto; }
 
 #cropbox { margin: auto; }
 #contacts_details_photo_wrapper { width: 200px; }
@@ -110,7 +111,7 @@ dl.addresscard .action { float: right; }
 #file_upload_form { width: 0; height: 0; }
 #file_upload_target, #import_upload_target, #crop_target { display:none; }
 #file_upload_start, #import_upload_start { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1001; width:0; height:0;}
-#import_upload_start { width: 16px; height: 16px; margin: 0 0 0 0; }
+#import_upload_start { width: 20px; height: 20px; margin: 0 0 -24px 0; padding: 0;}
 input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
 .big { font-weight:bold; font-size:1.2em; }
 .huge { font-weight:bold; font-size:1.5em; }
@@ -124,3 +125,12 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
 .typelist[type="button"] { float: left; max-width: 10em; border: 0; background-color: #fff; color: #bbb} /* for multiselect */
 .typelist[type="button"]:hover { color: #777; } /* for multiselect */
 .addresslist { clear: both; font-weight: bold; }
+#ninjahelp { position: absolute; bottom: 0; left: 0; right: 0; padding: 1em; margin: 1em; border: thin solid #eee; border-radius: 5px; background-color: #DBDBDB; opacity: 0.9; }
+#ninjahelp .close { position: absolute; top: 5px; right: 5px; height: 20px; width: 20px; }
+#ninjahelp h2, .help-section h3 { width: 100%; font-weight: bold; text-align: center; }
+#ninjahelp h2 { font-size: 1.4em; }
+.help-section { width: 45%; min-width: 35em; float: left; }
+.help-section h3 { font-size: 1.2em; }
+.help-section dl { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; }
+.help-section dt { display: table-cell; clear: left; float: left; width: 35%; margin: 0; padding: 0.2em; text-align: right; text-overflow: ellipsis; vertical-align: text-bottom; font-weight: bold: }
+.help-section dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0.2em; white-space: nowrap; vertical-align: text-bottom; }
diff --git a/apps/contacts/export.php b/apps/contacts/export.php
index 58fdb040a5cc7b32abde67f6eab2e06c95e004ca..161ca8047ac1e5bc109e3b4f0bf434538528accd 100644
--- a/apps/contacts/export.php
+++ b/apps/contacts/export.php
@@ -9,27 +9,30 @@
  
 OCP\User::checkLoggedIn();
 OCP\App::checkAppEnabled('contacts');
-$bookid = isset($_GET['bookid']) ? $_GET['bookid'] : NULL;
-$contactid = isset($_GET['contactid']) ? $_GET['contactid'] : NULL;
+$bookid = isset($_GET['bookid']) ? $_GET['bookid'] : null;
+$contactid = isset($_GET['contactid']) ? $_GET['contactid'] : null;
 $nl = "\n";
-if(isset($bookid)){
+if(isset($bookid)) {
 	$addressbook = OC_Contacts_App::getAddressbook($bookid);
 	//$cardobjects = OC_Contacts_VCard::all($bookid);
 	header('Content-Type: text/directory');
-	header('Content-Disposition: inline; filename=' . str_replace(' ', '_', $addressbook['displayname']) . '.vcf'); 
+	header('Content-Disposition: inline; filename=' 
+		. str_replace(' ', '_', $addressbook['displayname']) . '.vcf'); 
 
 	$start = 0;
-	$batchsize = OCP\Config::getUserValue(OCP\User::getUser(), 'contacts', 'export_batch_size', 20);
+	$batchsize = OCP\Config::getUserValue(OCP\User::getUser(), 
+		'contacts', 
+		'export_batch_size', 20);
 	while($cardobjects = OC_Contacts_VCard::all($bookid, $start, $batchsize)){
 		foreach($cardobjects as $card) {
 			echo $card['carddata'] . $nl;
 		}
 		$start += $batchsize;
 	}
-}elseif(isset($contactid)){
+}elseif(isset($contactid)) {
 	$data = OC_Contacts_App::getContactObject($contactid);
 	header('Content-Type: text/vcard');
-	header('Content-Disposition: inline; filename=' . str_replace(' ', '_', $data['fullname']) . '.vcf'); 
+	header('Content-Disposition: inline; filename=' 
+		. str_replace(' ', '_', $data['fullname']) . '.vcf'); 
 	echo $data['carddata'];
 }
-?>
diff --git a/apps/contacts/import.php b/apps/contacts/import.php
index c95fd970fe1018621ba61c4087cf83cda21ccdc3..1986d79f6d65a69867807f98b300416c367c1e4c 100644
--- a/apps/contacts/import.php
+++ b/apps/contacts/import.php
@@ -30,37 +30,51 @@ writeProgress('10');
 $view = $file = null;
 if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
 	$view = OCP\Files::getStorage('contacts');
-	$file = $view->file_get_contents('/' . $_POST['file']);
+	$file = $view->file_get_contents('/imports/' . $_POST['file']);
 } else {
 	$file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']);
 }
 if(!$file) {
-	OCP\JSON::error(array('message' => 'Import file was empty.'));
+	OCP\JSON::error(array('data' => array('message' => 'Import file was empty.')));
 	exit();
 }
-if(isset($_POST['method']) && $_POST['method'] == 'new'){
-	$id = OC_Contacts_Addressbook::add(OCP\USER::getUser(), $_POST['addressbookname']);
+if(isset($_POST['method']) && $_POST['method'] == 'new') {
+	$id = OC_Contacts_Addressbook::add(OCP\USER::getUser(), 
+		$_POST['addressbookname']);
 	if(!$id) {
-		OCP\JSON::error(array('message' => 'Error creating address book.'));
+		OCP\JSON::error(
+			array(
+				'data' => array('message' => 'Error creating address book.')
+			)
+		);
 		exit();
 	}
 	OC_Contacts_Addressbook::setActive($id, 1);
 }else{
 	$id = $_POST['id'];
 	if(!$id) {
-		OCP\JSON::error(array('message' => 'Error getting the ID of the address book.'));
+		OCP\JSON::error(
+			array(
+				'data' => array(
+					'message' => 'Error getting the ID of the address book.', 
+					'file'=>$_POST['file']
+				)
+			)
+		);
 		exit();
 	}
 	OC_Contacts_App::getAddressbook($id); // is owner access check
 }
 //analyse the contacts file
 writeProgress('40');
+$file = str_replace(array("\r","\n\n"), array("\n","\n"), $file);
 $lines = explode($nl, $file);
+
 $inelement = false;
 $parts = array();
 $card = array();
 foreach($lines as $line){
-	if(strtoupper(trim($line)) == 'BEGIN:VCARD'){
+	if(strtoupper(trim($line)) == 'BEGIN:VCARD') {
 		$inelement = true;
 	} elseif (strtoupper(trim($line)) == 'END:VCARD') {
 		$card[] = $line;
@@ -77,21 +91,40 @@ writeProgress('70');
 $imported = 0;
 $failed = 0;
 if(!count($parts) > 0) {
-	OCP\JSON::error(array('message' => 'No contacts to import in .'.$_POST['file'].' Please check if the file is corrupted.'));
+	OCP\JSON::error(
+		array(
+			'data' => array(
+				'message' => 'No contacts to import in '
+					. $_POST['file'].'. Please check if the file is corrupted.', 
+				'file'=>$_POST['file']
+			)
+		)
+	);
+	if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
+		if(!$view->unlink('/imports/' . $_POST['file'])) {
+			OCP\Util::writeLog('contacts', 
+				'Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], 
+				OCP\Util::ERROR);
+		}
+	}
 	exit();
 }
 foreach($parts as $part){
 	$card = OC_VObject::parse($part);
 	if (!$card) {
 		$failed += 1;
-		OCP\Util::writeLog('contacts','Import: skipping card. Error parsing VCard: '.$part, OCP\Util::ERROR);
+		OCP\Util::writeLog('contacts', 
+			'Import: skipping card. Error parsing VCard: ' . $part, 
+				OCP\Util::ERROR);
 		continue; // Ditch cards that can't be parsed by Sabre.
 	}
 	try {
 		OC_Contacts_VCard::add($id, $card);
 		$imported += 1;
 	} catch (Exception $e) {
-		OCP\Util::writeLog('contacts', 'Error importing vcard: '.$e->getMessage().$nl.$card, OCP\Util::ERROR);
+		OCP\Util::writeLog('contacts', 
+			'Error importing vcard: ' . $e->getMessage() . $nl . $card, 
+			OCP\Util::ERROR);
 		$failed += 1;
 	}
 }
@@ -100,8 +133,18 @@ writeProgress('100');
 sleep(3);
 OC_Cache::remove($progresskey);
 if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
-	if(!$view->unlink('/' . $_POST['file'])) {
-		OCP\Util::writeLog('contacts','Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], OCP\Util::ERROR);
+	if(!$view->unlink('/imports/' . $_POST['file'])) {
+		OCP\Util::writeLog('contacts', 
+			'Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], 
+			OCP\Util::ERROR);
 	}
 }
-OCP\JSON::success(array('data' => array('imported'=>$imported, 'failed'=>$failed)));
+OCP\JSON::success(
+	array(
+		'data' => array(
+			'imported'=>$imported, 
+			'failed'=>$failed, 
+			'file'=>$_POST['file'],
+		)
+	)
+);
diff --git a/apps/contacts/index.php b/apps/contacts/index.php
index 0b4f89b30c021077f68209480d9d823e6e9e173f..c35e1b85d4e09986de05fc841477a11290ced9b9 100644
--- a/apps/contacts/index.php
+++ b/apps/contacts/index.php
@@ -14,28 +14,20 @@ OCP\App::checkAppEnabled('contacts');
 
 // Get active address books. This creates a default one if none exists.
 $ids = OC_Contacts_Addressbook::activeIds(OCP\USER::getUser());
-$contacts = OC_Contacts_VCard::all($ids);
-if($contacts === false) {
-	OCP\Util::writeLog('contacts','index.html: No contacts found.',OCP\Util::DEBUG);
+$has_contacts = (count(OC_Contacts_VCard::all($ids, 0, 1)) > 0 
+	? true 
+	: false); // just to check if there are any contacts.
+if($has_contacts === false) {
+	OCP\Util::writeLog('contacts', 
+		'index.html: No contacts found.', 
+		OCP\Util::DEBUG);
 }
 
-$addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser());
-
 // Load the files we need
-OCP\App::setActiveNavigationEntry( 'contacts_index' );
+OCP\App::setActiveNavigationEntry('contacts_index');
 
 // Load a specific user?
 $id = isset( $_GET['id'] ) ? $_GET['id'] : null;
-$details = array();
-
-if(is_null($id) && count($contacts) > 0) {
-	$id = $contacts[0]['id'];
-}
-unset($contacts);
-if(!is_null($id)) {
-	$vcard = OC_Contacts_App::getContactVCard($id);
-	$details = OC_Contacts_VCard::structureContact($vcard);
-}
 $property_types = OC_Contacts_App::getAddPropertyOptions();
 $phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
 $email_types = OC_Contacts_App::getTypesOfProperty('EMAIL');
@@ -46,32 +38,31 @@ $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
 $maxUploadFilesize = min($upload_max_filesize, $post_max_size);
 
 $freeSpace=OC_Filesystem::free_space('/');
-$freeSpace=max($freeSpace,0);
-$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace);
+$freeSpace=max($freeSpace, 0);
+$maxUploadFilesize = min($maxUploadFilesize, $freeSpace);
 
-OCP\Util::addscript('','jquery.multiselect');
-OCP\Util::addscript('','oc-vcategories');
-OCP\Util::addscript('contacts','contacts');
-OCP\Util::addscript('contacts','expanding');
-OCP\Util::addscript('contacts','jquery.combobox');
-OCP\Util::addscript('contacts','jquery.inview');
-OCP\Util::addscript('contacts','jquery.Jcrop');
-OCP\Util::addscript('contacts','jquery.multi-autocomplete');
-OCP\Util::addStyle('','jquery.multiselect');
-OCP\Util::addStyle('contacts','jquery.combobox');
-OCP\Util::addStyle('contacts','jquery.Jcrop');
-OCP\Util::addStyle('contacts','contacts');
+OCP\Util::addscript('', 'jquery.multiselect');
+OCP\Util::addscript('', 'oc-vcategories');
+OCP\Util::addscript('contacts', 'contacts');
+OCP\Util::addscript('contacts', 'expanding');
+OCP\Util::addscript('contacts', 'jquery.combobox');
+OCP\Util::addscript('files', 'jquery.fileupload');
+OCP\Util::addscript('contacts', 'jquery.inview');
+OCP\Util::addscript('contacts', 'jquery.Jcrop');
+OCP\Util::addscript('contacts', 'jquery.multi-autocomplete');
+OCP\Util::addStyle('', 'jquery.multiselect');
+OCP\Util::addStyle('contacts', 'jquery.combobox');
+OCP\Util::addStyle('contacts', 'jquery.Jcrop');
+OCP\Util::addStyle('contacts', 'contacts');
 
 $tmpl = new OCP\Template( "contacts", "index", "user" );
-$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
-$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
-$tmpl->assign('property_types', $property_types);
-$tmpl->assign('phone_types', $phone_types);
-$tmpl->assign('email_types', $email_types);
-$tmpl->assign('categories', $categories);
-$tmpl->assign('addressbooks', $addressbooks);
-$tmpl->assign('details', $details );
-$tmpl->assign('id',$id);
+$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize, false);
+$tmpl->assign('uploadMaxHumanFilesize', 
+	OCP\Util::humanFileSize($maxUploadFilesize), false);
+$tmpl->assign('property_types', $property_types, false);
+$tmpl->assign('phone_types', $phone_types, false);
+$tmpl->assign('email_types', $email_types, false);
+$tmpl->assign('categories', $categories, false);
+$tmpl->assign('has_contacts', $has_contacts, false);
+$tmpl->assign('id', $id, false);
 $tmpl->printPage();
-
-?>
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
index 39ef14e506266a12efd364327577bd307f66f062..4c6c8bf3d93e61cd23b78c0d0ec6298295ad2cfc 100644
--- a/apps/contacts/js/contacts.js
+++ b/apps/contacts/js/contacts.js
@@ -12,13 +12,42 @@ String.prototype.strip_tags = function(){
 
 Contacts={
 	UI:{
-		notification:function(msg, ndata) {
-			$('#notification').text(msg);
-			if(data) {
-				$('#notification').data(ndata[0],ndata[1]);
+		/**
+		 * Arguments:
+		 * message: The text message to show. The only mandatory parameter.
+		 * timeout: The timeout in seconds before the notification disappears. Default 10.
+		 * timeouthandler: A function to run on timeout.
+		 * clickhandler: A function to run on click. If a timeouthandler is given it will be cancelled.
+		 * data: An object that will be passed as argument to the timeouthandler and clickhandler functions.
+		 */
+		notify:function(params) {
+			self = this;
+			if(!self.notifier) {
+				self.notifier = $('#notification');
+			}
+			self.notifier.text(params.message);
+			self.notifier.fadeIn();
+			self.notifier.on('click', function() { $(this).fadeOut();});
+			var timer = setTimeout(function() {
+				self.notifier.fadeOut();
+				if(params.timeouthandler && $.isFunction(params.timeouthandler)) {
+					params.timeouthandler(self.notifier.data(dataid));
+					self.notifier.off('click');
+					self.notifier.removeData(dataid);
+				}
+			}, params.timeout && $.isNumeric(params.timeout) ? parseInt(params.timeout)*1000 : 10000);
+			var dataid = timer.toString();
+			if(params.data) {
+				self.notifier.data(dataid, params.data);
+			}
+			if(params.clickhandler && $.isFunction(params.clickhandler)) {
+				self.notifier.on('click', function() {
+					clearTimeout(timer);
+					self.notifier.off('click');
+					params.clickhandler(self.notifier.data(dataid));
+					self.notifier.removeData(dataid);
+				});
 			}
-			$('#notification').fadeIn();
-			setTimeout($('#notification').fadeOut(), 10000);
 		},
 		notImplemented:function() {
 			OC.dialogs.alert(t('contacts', 'Sorry, this functionality has not been implemented yet'), t('contacts', 'Not implemented'));
@@ -99,7 +128,7 @@ Contacts={
 			$('.addresscard,.propertylist li,.propertycontainer').hover(
 				function () {
 					$(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 1.0 }, 200, function() {});
-				}, 
+				},
 				function () {
 					$(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 0.1 }, 200, function() {});
 				}
@@ -110,7 +139,7 @@ Contacts={
 				obj.tipsy('hide');
 				Contacts.UI.Card.deleteProperty(obj, 'single');
 			}
-			
+
 			var goToUrl = function(obj) {
 				var url = Contacts.UI.propertyContainerFor(obj).find('#url').val();
 				if(url != '') {
@@ -118,7 +147,7 @@ Contacts={
 					newWindow.focus();
 				}
 			}
-			
+
 			$('#identityprops a.delete').click( function() { deleteItem($(this)) });
 			$('#identityprops a.delete').keydown( function() { deleteItem($(this)) });
 			$('#categories_value a.edit').click( function() { $(this).tipsy('hide');OCCategories.edit(); } );
@@ -143,7 +172,7 @@ Contacts={
 												});
 			$('#edit_name').click(function(){Contacts.UI.Card.editName()});
 			$('#edit_name').keydown(function(){Contacts.UI.Card.editName()});
-			
+
 			$('#phototools li a').click(function() {
 				$(this).tipsy('hide');
 			});
@@ -170,13 +199,13 @@ Contacts={
 				OC.dialogs.filepicker(t('contacts', 'Select photo'), Contacts.UI.Card.cloudPhotoSelected, false, 'image', true);
 			});
 			/* Initialize the photo edit dialog */
-			$('#edit_photo_dialog').dialog({ 
+			$('#edit_photo_dialog').dialog({
 				autoOpen: false, modal: true, height: 'auto', width: 'auto'
 			});
 			$('#edit_photo_dialog' ).dialog( 'option', 'buttons', [
 				{
 					text: "Ok",
-					click: function() { 
+					click: function() {
 						Contacts.UI.Card.savePhoto(this);
 						$(this).dialog('close');
 					}
@@ -186,47 +215,28 @@ Contacts={
 					click: function() { $(this).dialog('close'); }
 				}
 			] );
-			
-			/*$('#fn').blur(function(){
-				if($('#fn').val() == '') {
-					OC.dialogs.alert(t('contacts','The name field cannot be empty. Please enter a name for this contact.'), t('contacts','Name is empty'), function() { $('#fn').focus(); });
-					$('#fn').focus();
-					return false;
-				}
-			});*/
-			
+
 			// Name has changed. Update it and reorder.
-			// TODO: Take addressbook into account
 			$('#fn').change(function(){
 				var name = $('#fn').val().strip_tags();
-				var item = $('.contacts li[data-id="'+Contacts.UI.Card.id+'"]');
+				var item = $('.contacts li[data-id="'+Contacts.UI.Card.id+'"]').detach();
 				$(item).find('a').html(name);
 				Contacts.UI.Card.fn = name;
-				var added = false;
-				$('.contacts li[data-bookid="'+Contacts.UI.Card.bookid+'"]').each(function(){
-					if ($(this).text().toLowerCase() > name.toLowerCase()) {
-						$(this).before(item).fadeIn('fast');
-						added = true;
-						return false;
-					}
-				});
-				if(!added) {
-					$('#contacts ul[data-id="'+Contacts.UI.Card.bookid+'"]').append(item);
-				}
+				Contacts.UI.Contacts.insertContact({contact:item});
 				Contacts.UI.Contacts.scrollTo(Contacts.UI.Card.id);
 			});
 
-			$('#contacts_deletecard').click( function() { Contacts.UI.Card.doDelete();return false;} );
-			$('#contacts_deletecard').keydown( function(event) { 
-				if(event.which == 13) {
-					Contacts.UI.Card.doDelete();
+			$('#contacts_deletecard').click( function() { Contacts.UI.Card.delayedDelete();return false;} );
+			$('#contacts_deletecard').keydown( function(event) {
+				if(event.which == 13 || event.which == 32) {
+					Contacts.UI.Card.delayedDelete();
 				}
 				return false;
 			});
 
 			$('#contacts_downloadcard').click( function() { Contacts.UI.Card.doExport();return false;} );
-			$('#contacts_downloadcard').keydown( function(event) { 
-				if(event.which == 13) {
+			$('#contacts_downloadcard').keydown( function(event) {
+				if(event.which == 13 || event.which == 32) {
 					Contacts.UI.Card.doExport();
 				}
 				return false;
@@ -240,12 +250,12 @@ Contacts={
 			$('#contacts_details_photo_wrapper').bind('dragover',function(event){
 				$(event.target).addClass('droppable');
 				event.stopPropagation();
-				event.preventDefault();  
+				event.preventDefault();
 			});
 			$('#contacts_details_photo_wrapper').bind('dragleave',function(event){
 				$(event.target).removeClass('droppable');
 				//event.stopPropagation();
-				//event.preventDefault();  
+				//event.preventDefault();
 			});
 			$('#contacts_details_photo_wrapper').bind('drop',function(event){
 				event.stopPropagation();
@@ -258,7 +268,7 @@ Contacts={
 			$('#contacts_deletecard').tipsy({gravity: 'ne'});
 			$('#contacts_downloadcard').tipsy({gravity: 'ne'});
 			$('#contacts_propertymenu_button').tipsy();
-			$('#contacts_newcontact, #chooseaddressbook').tipsy({gravity: 'sw'});
+			$('#contacts_newcontact, #contacts_import, #chooseaddressbook').tipsy({gravity: 'sw'});
 
 			$('body').click(function(e){
 				if(!$(e.target).is('#contacts_propertymenu_button')) {
@@ -285,48 +295,64 @@ Contacts={
 			$('#contacts_propertymenu_dropdown a').keydown(propertyMenuItem);
 		},
 		Card:{
-			id:'',
-			fn:'',
-			fullname:'',
-			shortname:'',
-			famname:'',
-			givname:'',
-			addname:'',
-			honpre:'',
-			honsuf:'',
-			data:undefined,
-			update:function(id, bookid) {
-				var newid, firstitem;
-				if(!id) {
+			update:function(params) { // params {cid:int, aid:int}
+				if(!params) { params = {}; }
+				$('#contacts li,#contacts h3').removeClass('active');
+				console.log('Card, cid: ' + params.cid + ' aid: ' + params.aid);
+				var newid, bookid, firstitem;
+				if(!parseInt(params.cid) && !parseInt(params.aid)) {
 					firstitem = $('#contacts ul').first().find('li:first-child');
 					if(firstitem.length > 0) {
-						newid = firstitem.data('id');
-						bookid = firstitem.data('bookid');
+						newid = parseInt(firstitem.data('id'));
+						bookid = parseInt(firstitem.data('bookid'));
+					}
+				} else if(!parseInt(params.cid) && parseInt(params.aid)) {
+					bookid = parseInt(params.aid);
+					newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id'));
+				} else if(parseInt(params.cid) && !parseInt(params.aid)) {
+					newid = parseInt(params.cid);
+					var listitem = Contacts.UI.Contacts.getContact(newid); //$('#contacts li[data-id="'+newid+'"]');
+					console.log('Is contact in list? ' + listitem.length);
+					if(listitem.length) {
+						//bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid'));
+						bookid = parseInt(Contacts.UI.Contacts.getContact(newid).data('bookid'));
+					} else { // contact isn't in list yet.
+						bookid = 'unknown';
 					}
 				} else {
-					newid = id;
-					bookid = bookid?bookid:$('#contacts li[data-id="'+newid+'"]').data('bookid');
+					newid = parseInt(params.cid);
+					bookid = parseInt(params.aid);
 				}
-				if(!bookid) {
-					bookid = $('#contacts h3').first().data('id');
+				if(!bookid || !newid) {
+					bookid = parseInt($('#contacts h3').first().data('id'));
+					newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id'));
 				}
-				console.log('bookid: ' +bookid);
+				console.log('newid: ' + newid + ' bookid: ' +bookid);
 				var localLoadContact = function(newid, bookid) {
 					if($('.contacts li').length > 0) {
-						$('#contacts li[data-id="'+newid+'"]').addClass('active');
 						$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':newid},function(jsondata){
 							if(jsondata.status == 'success'){
-								$('#contacts h3[data-id="'+bookid+'"]').trigger('click');
+								if(bookid == 'unknown') {
+									bookid = jsondata.data.addressbookid;
+									var contact = Contacts.UI.Contacts.insertContact({
+										contactlist:$('#contacts ul[data-id="'+bookid+'"]'),
+										data:jsondata.data
+									});
+								}
+								$('#contacts li[data-id="'+newid+'"],#contacts h3[data-id="'+bookid+'"]').addClass('active');
+								$('#contacts ul[data-id="'+bookid+'"]').slideDown(300);
 								Contacts.UI.Card.loadContact(jsondata.data, bookid);
+								Contacts.UI.Contacts.scrollTo(newid);
 							} else {
 								OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
 							}
 						});
 					}
 				}
-				
+
 				// Make sure proper DOM is loaded.
-				if(!$('#card')[0] && newid) {
+				if(!$('#card').length && newid) {
+					console.log('Loading card DOM');
 					$.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{requesttoken:requesttoken},function(jsondata){
 						if(jsondata.status == 'success'){
 							$('#rightcontent').html(jsondata.data.page).ready(function() {
@@ -339,6 +365,7 @@ Contacts={
 					});
 				}
 				else if(!newid) {
+					console.log('Loading intro');
 					// load intro page
 					$.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){
 						if(jsondata.status == 'success'){
@@ -357,19 +384,14 @@ Contacts={
 			doExport:function() {
 				document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id;
 			},
-			doImport:function(){
-				Contacts.UI.notImplemented();
-			},
 			editNew:function(){ // add a new contact
 				this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = '';
 				//Contacts.UI.Card.add(t('contacts', 'Contact')+';'+t('contacts', 'New')+';;;', t('contacts', 'New Contact'), '', true);
 				Contacts.UI.Card.add(';;;;;', '', '', true);
 				return false;
 			},
-			createEntry:function(data) {
-				return $('<li data-id="'+data.id+'" data-bookid="'+data.addressbookid+'" role="button"><a href="'+OC.linkTo('contacts', 'index.php')+'&id='+data.id+'"  style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+data.id+') no-repeat scroll 0% 0% transparent;">'+data.displayname+'</a></li>');
-			},
 			add:function(n, fn, aid, isnew){ // add a new contact
+				console.log('Adding ' + fn);
 				aid = aid?aid:$('#contacts h3.active').first().data('id');
 				var localAddcontact = function(n, fn, aid, isnew) {
 					$.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid, isnew: isnew },
@@ -381,19 +403,7 @@ Contacts={
 							$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){
 								if(jsondata.status == 'success'){
 									Contacts.UI.Card.loadContact(jsondata.data, aid);
-									$('#contacts .active').removeClass('active');
-									var item = $('<li data-id="'+jsondata.data.id+'" class="active"><a href="index.php?id='+jsondata.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+jsondata.data.id+') no-repeat scroll 0% 0% transparent;">'+Contacts.UI.Card.fn+'</a></li>');
-									var added = false;
-									$('#contacts ul[data-id="'+aid+'"] li').each(function(){
-										if ($(this).text().toLowerCase() > Contacts.UI.Card.fn.toLowerCase()) {
-											$(this).before(item).fadeIn('fast');
-											added = true;
-											return false;
-										}
-									});
-									if(!added) {
-										$('#contacts ul[data-id="'+aid+'"]').append(item);
-									}
+									var item = Contacts.UI.Contacts.insertContact({data:jsondata.data});
 									if(isnew) { // add some default properties
 										Contacts.UI.Card.addProperty('EMAIL');
 										Contacts.UI.Card.addProperty('TEL');
@@ -412,9 +422,9 @@ Contacts={
 						}
 					});
 				}
-			
-				var card = $('#card')[0];
-				if(!card) {
+
+				if(!$('#card').length) {
+					console.log('Loading card DOM');
 					$.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{'requesttoken': requesttoken},function(jsondata){
 						if(jsondata.status == 'success'){
 							$('#rightcontent').html(jsondata.data.page).ready(function() {
@@ -429,47 +439,63 @@ Contacts={
 					localAddcontact(n, fn, aid, isnew);
 				}
 			},
-			doDelete:function() {
+			delayedDelete:function() {
+				/* TODO:
+				$(window).unload(function() {
+					deleteFilesInQueue();
+				});
+				*/
 				$('#contacts_deletecard').tipsy('hide');
-				OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) {
-					if(answer == true) {
-						$.post(OC.filePath('contacts', 'ajax', 'deletecard.php'),{'id':Contacts.UI.Card.id},function(jsondata){
-							if(jsondata.status == 'success'){
-								var newid = '', bookid;
-								var curlistitem = $('#contacts li[data-id="'+jsondata.data.id+'"]');
-								var newlistitem = curlistitem.prev('li');
-								if(newlistitem == undefined) {
-									newlistitem = curlistitem.next('li');
-								}
-								curlistitem.remove();
-								if(!$(newlistitem).is('li')) {
-									newid = newlistitem.data('id');
-									bookid = newlistitem.data('id');
-								}
-								$('#rightcontent').data('id',newid);
-								this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = '';
-								this.data = undefined;
-								
-								if($('.contacts li').length > 0) { // Load first in list.
-									Contacts.UI.Card.update(newid, bookid);
-								} else {
-									// load intro page
-									$.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){
-										if(jsondata.status == 'success'){
-											id = '';
-											$('#rightcontent').data('id','');
-											$('#rightcontent').html(jsondata.data.page);
-										}
-										else{
-											OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
-										}
-									});
-								}
-							}
-							else{
-								OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
-							}
-						});
+				var newid = '', bookid;
+				var curlistitem = Contacts.UI.Contacts.getContact(this.id);
+				curlistitem.removeClass('active');
+				var newlistitem = curlistitem.prev('li');
+				if(!newlistitem) {
+					newlistitem = curlistitem.next('li');
+				}
+				curlistitem.detach();
+				if($(newlistitem).is('li')) {
+					newid = newlistitem.data('id');
+					bookid = newlistitem.data('bookid');
+				}
+				$('#rightcontent').data('id', newid);
+
+				with(this) {
+					delete id; delete fn; delete fullname; delete shortname; delete famname;
+					delete givname; delete addname; delete honpre; delete honsuf; delete data;
+				}
+
+				if($('.contacts li').length > 0) {
+					Contacts.UI.Card.update({cid:newid, aid:bookid});
+				} else {
+					// load intro page
+					$.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){
+						if(jsondata.status == 'success'){
+							id = '';
+							$('#rightcontent').html(jsondata.data.page).removeData('id');
+						}
+						else{
+							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+						}
+					});
+				}
+				Contacts.UI.notify({
+					data:curlistitem,
+					message:t('contacts','Click to undo deletion of "') + curlistitem.find('a').text() + '"',
+					timeouthandler:function(contact) {
+						Contacts.UI.Card.doDelete(contact.data('id'));
+						delete contact;
+					},
+					clickhandler:function(contact) {
+						Contacts.UI.Contacts.insertContact({contact:contact});
+						Contacts.UI.notify({message:t('contacts', 'Cancelled deletion of: "') + curlistitem.find('a').text() + '"'});
+					}
+				});
+			},
+			doDelete:function(id) {
+				$.post(OC.filePath('contacts', 'ajax', 'deletecard.php'),{'id':id},function(jsondata) {
+					if(jsondata.status == 'error'){
+						OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
 					}
 				});
 				return false;
@@ -486,21 +512,22 @@ Contacts={
 				this.loadAddresses();
 				this.loadSingleProperties();
 				Contacts.UI.loadListHandlers();
+				var note = $('#note');
 				if(this.data.NOTE) {
-					$('#note').data('checksum', this.data.NOTE[0]['checksum']);
-					var note = $('#note').find('textarea');
+					note.data('checksum', this.data.NOTE[0]['checksum']);
+					var textarea = note.find('textarea');
 					var txt = this.data.NOTE[0]['value'];
 					var nheight = txt.split('\n').length > 4 ? txt.split('\n').length+2 : 5;
-					note.css('min-height', nheight+'em');
-					note.attr('rows', nheight);
-					note.val(txt);
-					$('#note').show();
-					note.expandingTextarea();
+					textarea.css('min-height', nheight+'em');
+					textarea.attr('rows', nheight);
+					textarea.val(txt);
+					note.show();
+					textarea.expandingTextarea();
 					$('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().hide();
 				} else {
-					$('#note').data('checksum', '');
-					$('#note').find('textarea').val('');
-					$('#note').hide();
+					note.removeData('checksum');
+					note.find('textarea').val('');
+					note.hide();
 					$('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().show();
 				}
 			},
@@ -521,15 +548,16 @@ Contacts={
 						$('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().hide();
 						var property = this.data[propname][0];
 						var value = property['value'], checksum = property['checksum'];
-						
+
 						if(propname == 'BDAY') {
 							var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10));
 							value = $.datepicker.formatDate('dd-mm-yy', val);
 						}
-						$('#contact_identity').find('#'+propname.toLowerCase()).val(value);
-						$('#contact_identity').find('#'+propname.toLowerCase()+'_value').data('checksum', checksum);
-						$('#contact_identity').find('#'+propname.toLowerCase()+'_label').show();
-						$('#contact_identity').find('#'+propname.toLowerCase()+'_value').show();
+						var identcontainer = $('#contact_identity');
+						identcontainer.find('#'+propname.toLowerCase()).val(value);
+						identcontainer.find('#'+propname.toLowerCase()+'_value').data('checksum', checksum);
+						identcontainer.find('#'+propname.toLowerCase()+'_label').show();
+						identcontainer.find('#'+propname.toLowerCase()+'_value').show();
 					} else {
 						$('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().show();
 					}
@@ -544,8 +572,12 @@ Contacts={
 						$(this).find('input').val('');
 					}
 				});
-				this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = '';
-				var narray = undefined;
+
+				with(this) {
+					delete fn; delete fullname; delete givname; delete famname;
+					delete addname; delete honpre; delete honsuf;
+				}
+
 				if(this.data.FN) {
 					this.fn = this.data.FN[0]['value'];
 				}
@@ -586,7 +618,7 @@ Contacts={
 				$.each(names, function(key, value) {
 					$('#fn_select')
 						.append($('<option></option>')
-						.text(value)); 
+						.text(value));
 				});
 				$('#fn_select').combobox('value', this.fn);
 				$('#contact_identity').find('*[data-element="N"]').data('checksum', this.data.N[0]['checksum']);
@@ -660,7 +692,7 @@ Contacts={
 					return false;
 				}
 				container = $(obj).parents('.propertycontainer').first(); // get the parent holding the metadata.
-				Contacts.UI.loading(container, true);
+				Contacts.UI.loading(obj, true);
 				var checksum = container.data('checksum');
 				var name = container.data('element');
 				var fields = container.find('input.contacts_property,select.contacts_property').serializeArray();
@@ -683,7 +715,7 @@ Contacts={
 				var q = container.find('input.contacts_property,select.contacts_property,textarea.contacts_property').serialize();
 				if(q == '' || q == undefined) {
 					OC.dialogs.alert(t('contacts', 'Couldn\'t serialize elements.'), t('contacts', 'Error'));
-					Contacts.UI.loading(container, false);
+					Contacts.UI.loading(obj, false);
 					return false;
 				}
 				q = q + '&id=' + this.id + '&name=' + name;
@@ -695,13 +727,13 @@ Contacts={
 						if(jsondata.status == 'success'){
 							container.data('checksum', jsondata.data.checksum);
 							Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum);
-							Contacts.UI.loading(container, false);
+							Contacts.UI.loading(obj, false);
 							$(obj).removeAttr('disabled');
 							return true;
 						}
 						else{
 							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
-							Contacts.UI.loading(container, false);
+							Contacts.UI.loading(obj, false);
 							$(obj).removeAttr('disabled');
 							return false;
 						}
@@ -714,13 +746,13 @@ Contacts={
 							container.data('checksum', jsondata.data.checksum);
 							// TODO: savePropertyInternal doesn't know about new fields
 							//Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum);
-							Contacts.UI.loading(container, false);
+							Contacts.UI.loading(obj, false);
 							$(obj).removeAttr('disabled');
 							return true;
 						}
 						else{
 							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
-							Contacts.UI.loading(container, false);
+							Contacts.UI.loading(obj, false);
 							$(obj).removeAttr('disabled');
 							return false;
 						}
@@ -729,17 +761,9 @@ Contacts={
 			},
 			addProperty:function(type){
 				switch (type) {
-					case 'PHOTO':
-						this.loadPhoto(true);
-						$('#file_upload_form').show();
-						$('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide();
-						$('#file_upload_start').trigger('click');
-						break;
 					case 'NOTE':
-						$('#note').show();
 						$('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide();
-						$('#note').find('textarea').expandingTextarea();
-						$('#note').find('textarea').focus();
+						$('#note').find('textarea').expandingTextarea().show().focus();
 						break;
 					case 'EMAIL':
 						if($('#emaillist>li').length == 1) {
@@ -771,6 +795,7 @@ Contacts={
 				}
 			},
 			deleteProperty:function(obj, type){
+				console.log('deleteProperty');
 				Contacts.UI.loading(obj, true);
 				var checksum = Contacts.UI.checksumFor(obj);
 				if(checksum) {
@@ -793,8 +818,7 @@ Contacts={
 									}
 								} else {
 									$('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide();
-									$('dl dd[data-element="'+proptype+'"]').data('checksum', '');
-									$('dl dd[data-element="'+proptype+'"]').find('input').val('');
+									$('dl dd[data-element="'+proptype+'"]').data('checksum', '').find('input').val('');
 								}
 								$('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show();
 								Contacts.UI.loading(obj, false);
@@ -821,14 +845,14 @@ Contacts={
 					}
 				}
 			},
-			editName:function(){
+			editName:function() {
 				var params = {id: this.id};
 				/* Initialize the name edit dialog */
-				if($('#edit_name_dialog').dialog('isOpen') == true){
+				if($('#edit_name_dialog').dialog('isOpen') == true) {
 					$('#edit_name_dialog').dialog('moveToTop');
-				}else{
-					$.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata){
-						if(jsondata.status == 'success'){
+				} else {
+					$.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata) {
+						if(jsondata.status == 'success') {
 							$('body').append('<div id="name_dialog"></div>');
 							$('#name_dialog').html(jsondata.data.page).find('#edit_name_dialog' ).dialog({
 								modal: true,
@@ -836,7 +860,7 @@ Contacts={
 								title:  t('contacts', 'Edit name'),
 								height: 'auto', width: 'auto',
 								buttons: {
-									'Ok':function() { 
+									'Ok':function() {
 										Contacts.UI.Card.saveName(this);
 										$(this).dialog('close');
 									},
@@ -887,9 +911,9 @@ Contacts={
 				$.each(names, function(key, value) {
 					$('#fn_select')
 						.append($('<option></option>')
-						.text(value)); 
+						.text(value));
 				});
-				
+
 				if(this.id == '') {
 					var aid = $(dlg).find('#aid').val();
 					Contacts.UI.Card.add(n.join(';'), $('#short').text(), aid);
@@ -900,10 +924,11 @@ Contacts={
 			loadAddresses:function(){
 				$('#addresses').hide();
 				$('#addressdisplay dl.propertycontainer').remove();
+				var addresscontainer = $('#addressdisplay');
 				for(var adr in this.data.ADR) {
-					$('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show();
-					$('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer');
-					$('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']);
+					addresscontainer.find('dl').first().clone().insertAfter($('#addressdisplay dl').last()).show();
+					addresscontainer.find('dl').last().removeClass('template').addClass('propertycontainer');
+					addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']);
 					var adrarray = this.data.ADR[adr]['value'];
 					var adrtxt = '';
 					if(adrarray[0] && adrarray[0].length > 0) {
@@ -915,7 +940,7 @@ Contacts={
 					if(adrarray[2] && adrarray[2].length > 0) {
 						adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>';
 					}
-					if((adrarray[3] && adrarray[5]) && adrarray[3].length > 0 || adrarray[5].length > 0) {
+					if((3 in adrarray && 5 in adrarray) && adrarray[3].length > 0 || adrarray[5].length > 0) {
 						adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>';
 					}
 					if(adrarray[4] && adrarray[4].length > 0) {
@@ -924,7 +949,7 @@ Contacts={
 					if(adrarray[6] && adrarray[6].length > 0) {
 						adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>';
 					}
-					$('#addressdisplay dl').last().find('.addresslist').html(adrtxt);
+					addresscontainer.find('dl').last().find('.addresslist').html(adrtxt);
 					var types = new Array();
 					var ttypes = new Array();
 					for(var param in this.data.ADR[adr]['parameters']) {
@@ -933,12 +958,12 @@ Contacts={
 							ttypes.push(this.data.ADR[adr]['parameters'][param]);
 						}
 					}
-					$('#addressdisplay dl').last().find('.adr_type_label').text(types.join('/'));
-					$('#addressdisplay dl').last().find('.adr_type').val(ttypes.join(','));
-					$('#addressdisplay dl').last().find('.adr').val(adrarray.join(';'));
-					$('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']);
+					addresscontainer.find('dl').last().find('.adr_type_label').text(types.join('/'));
+					addresscontainer.find('dl').last().find('.adr_type').val(ttypes.join(','));
+					addresscontainer.find('dl').last().find('.adr').val(adrarray.join(';'));
+					addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']);
 				}
-				if($('#addressdisplay dl').length > 1) {
+				if(addresscontainer.find('dl').length > 1) {
 					$('#addresses').show();
 					$('#contact_communication').show();
 				}
@@ -953,7 +978,7 @@ Contacts={
 					container = $('#addressdisplay dl').last();
 					container.removeClass('template').addClass('propertycontainer');
 				} else {
-					params['checksum'] = Contacts.UI.checksumFor(obj); 
+					params['checksum'] = Contacts.UI.checksumFor(obj);
 				}
 				/* Initialize the address edit dialog */
 				if($('#edit_address_dialog').dialog('isOpen') == true){
@@ -983,9 +1008,6 @@ Contacts={
 								close : function(event, ui) {
 									$(this).dialog('destroy').remove();
 									$('#address_dialog').remove();
-									if(isnew) {
-										container.remove();
-									}
 								},
 								open : function(event, ui) {
 									$( "#adr_city" ).autocomplete({
@@ -1024,7 +1046,7 @@ Contacts={
 											$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
 										}
 									});
-									$( "#adr_country" ).autocomplete({
+									$('#adr_country').autocomplete({
 										source: function( request, response ) {
 											$.ajax({
 												url: "http://ws.geonames.org/searchJSON",
@@ -1075,15 +1097,23 @@ Contacts={
 			saveAddress:function(dlg, obj, isnew){
 				if(isnew) {
 					container = $('#addressdisplay dl').last();
-					obj = $('#addressdisplay dl:last-child').find('input').first();
+					obj = container.find('input').first();
 				} else {
 					checksum = Contacts.UI.checksumFor(obj);
 					container = Contacts.UI.propertyContainerFor(obj);
 				}
-				var adr = new Array($(dlg).find('#adr_pobox').val().strip_tags(),$(dlg).find('#adr_extended').val().strip_tags(),$(dlg).find('#adr_street').val().strip_tags(),$(dlg).find('#adr_city').val().strip_tags(),$(dlg).find('#adr_region').val().strip_tags(),$(dlg).find('#adr_zipcode').val().strip_tags(),$(dlg).find('#adr_country').val().strip_tags());
-				$(container).find('.adr').val(adr.join(';'));
-				$(container).find('.adr_type').val($(dlg).find('#adr_type').val());
-				$(container).find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase())));
+				var adr = new Array(
+					$(dlg).find('#adr_pobox').val().strip_tags(),
+					$(dlg).find('#adr_extended').val().strip_tags(),
+					$(dlg).find('#adr_street').val().strip_tags(),
+					$(dlg).find('#adr_city').val().strip_tags(),
+					$(dlg).find('#adr_region').val().strip_tags(),
+					$(dlg).find('#adr_zipcode').val().strip_tags(),
+					$(dlg).find('#adr_country').val().strip_tags()
+				);
+				container.find('.adr').val(adr.join(';'));
+				container.find('.adr_type').val($(dlg).find('#adr_type').val());
+				container.find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase())));
 				Contacts.UI.Card.saveProperty($(container).find('input').first());
 				var adrtxt = '';
 				if(adr[0].length > 0) {
@@ -1104,7 +1134,7 @@ Contacts={
 				if(adr[6].length > 0) {
 					adrtxt = adrtxt + '<li>' + adr[6] + '</li>';
 				}
-				$(container).find('.addresslist').html(adrtxt);
+				container.find('.addresslist').html(adrtxt);
 			},
 			uploadPhoto:function(filelist) {
 				if(!filelist) {
@@ -1131,24 +1161,25 @@ Contacts={
 					form.submit();
 				}
 			},
-			loadPhotoHandlers:function(){
-				$('#phototools li a').tipsy('hide');
-				$('#phototools li a').tipsy();
+			loadPhotoHandlers:function() {
+				var phototools = $('#phototools');
+				phototools.find('li a').tipsy('hide');
+				phototools.find('li a').tipsy();
 				if(this.data.PHOTO) {
-					$('#phototools .delete').click(function() {
+					phototools.find('.delete').click(function() {
 						$(this).tipsy('hide');
 						Contacts.UI.Card.deleteProperty($('#contacts_details_photo'), 'single');
 						$(this).hide();
 					});
-					$('#phototools .edit').click(function() {
+					phototools.find('.edit').click(function() {
 						$(this).tipsy('hide');
 						Contacts.UI.Card.editCurrentPhoto();
 					});
-					$('#phototools .delete').show();
-					$('#phototools .edit').show();
+					phototools.find('.delete').show();
+					phototools.find('.edit').show();
 				} else {
-					$('#phototools .delete').hide();
-					$('#phototools .edit').hide();
+					phototools.find('.delete').hide();
+					phototools.find('.edit').hide();
 				}
 			},
 			cloudPhotoSelected:function(path){
@@ -1169,28 +1200,18 @@ Contacts={
 				$('#phototools li a').tipsy('hide');
 				var wrapper = $('#contacts_details_photo_wrapper');
 				wrapper.addClass('loading').addClass('wait');
-				
-				var img = new Image();
-				$(img).load(function () {
+				delete this.photo;
+				this.photo = new Image();
+				$(this.photo).load(function () {
 					$('img.contacts_details_photo').remove()
-					$(this).addClass('contacts_details_photo').hide();
+					$(this).addClass('contacts_details_photo');
 					wrapper.removeClass('loading').removeClass('wait');
 					$(this).insertAfter($('#phototools')).fadeIn();
 				}).error(function () {
 					// notify the user that the image could not be loaded
-					$(t('contacts','something went wrong.')).insertAfter($('#phototools'));
+					Contacts.UI.notify({message:t('contacts','Error loading profile picture.')});
 				}).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+self.id+refreshstr);
-		
-				$.getJSON(OC.filePath('contacts', 'ajax', 'loadphoto.php'),{'id':this.id, 'refresh': refresh},function(jsondata){
-					if(jsondata.status == 'success'){
-						$('#contacts_details_photo_wrapper').data('checksum', jsondata.data.checksum);
-						Contacts.UI.Card.loadPhotoHandlers();
-					}
-					else{
-						OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
-					}
-				});
-				$('#file_upload_form').show();
+				this.loadPhotoHandlers()
 			},
 			editCurrentPhoto:function(){
 				$.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),{'id':this.id},function(jsondata){
@@ -1244,10 +1265,11 @@ Contacts={
 			},
 			addMail:function() {
 				//alert('addMail');
-				$('#emaillist li.template:first-child').clone(true).appendTo($('#emaillist')).show().find('a .tip').tipsy();
-				$('#emaillist li.template:last-child').find('select').addClass('contacts_property');
-				$('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer');
-				$('#emaillist li:last-child').find('input[type="email"]').focus();
+				var emaillist = $('#emaillist');
+				emaillist.find('li.template:first-child').clone(true).appendTo(emaillist).show().find('a .tip').tipsy();
+				emaillist.find('li.template:last-child').find('select').addClass('contacts_property');
+				emaillist.find('li.template:last-child').removeClass('template').addClass('propertycontainer');
+				emaillist.find('li:last-child').find('input[type="email"]').focus();
 				return false;
 			},
 			loadMails:function() {
@@ -1283,35 +1305,37 @@ Contacts={
 				return false;
 			},
 			addPhone:function() {
-				$('#phonelist li.template:first-child').clone(true).appendTo($('#phonelist')); //.show();
-				$('#phonelist li.template:last-child').find('select').addClass('contacts_property');
-				$('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer');
-				$('#phonelist li:last-child').find('input[type="text"]').focus();
-				$('#phonelist li:last-child').find('select').multiselect({
+				var phonelist = $('#phonelist');
+				phonelist.find('li.template:first-child').clone(true).appendTo(phonelist); //.show();
+				phonelist.find('li.template:last-child').find('select').addClass('contacts_property');
+				phonelist.find('li.template:last-child').removeClass('template').addClass('propertycontainer');
+				phonelist.find('li:last-child').find('input[type="text"]').focus();
+				phonelist.find('li:last-child').find('select').multiselect({
 														noneSelectedText: t('contacts', 'Select type'),
 														header: false,
 														selectedList: 4,
 														classes: 'typelist'
 													});
-				$('#phonelist li:last-child').show();
+				phonelist.find('li:last-child').show();
 				return false;
 			},
 			loadPhones:function() {
 				$('#phones').hide();
 				$('#phonelist li.propertycontainer').remove();
+				var phonelist = $('#phonelist');
 				for(var phone in this.data.TEL) {
 					this.addPhone();
-					$('#phonelist li:last-child').find('select').multiselect('destroy');
-					$('#phonelist li:last-child').data('checksum', this.data.TEL[phone]['checksum'])
-					$('#phonelist li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']);
+					phonelist.find('li:last-child').find('select').multiselect('destroy');
+					phonelist.find('li:last-child').data('checksum', this.data.TEL[phone]['checksum'])
+					phonelist.find('li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']);
 					for(var param in this.data.TEL[phone]['parameters']) {
 						if(param.toUpperCase() == 'PREF') {
-							$('#phonelist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked');
+							phonelist.find('li:last-child').find('input[type="checkbox"]').attr('checked', 'checked');
 						}
 						else if(param.toUpperCase() == 'TYPE') {
 							for(ptype in this.data.TEL[phone]['parameters'][param]) {
 								var pt = this.data.TEL[phone]['parameters'][param][ptype];
-								$('#phonelist li:last-child').find('select option').each(function(){
+								phonelist.find('li:last-child').find('select option').each(function(){
 									//if ($(this).val().toUpperCase() == pt.toUpperCase()) {
 									if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) {
 										$(this).attr('selected', 'selected');
@@ -1320,14 +1344,14 @@ Contacts={
 							}
 						}
 					}
-					$('#phonelist li:last-child').find('select').multiselect({
-														noneSelectedText: t('contacts', 'Select type'),
-														header: false,
-														selectedList: 4,
-														classes: 'typelist'
-													});
+					phonelist.find('li:last-child').find('select').multiselect({
+											noneSelectedText: t('contacts', 'Select type'),
+											header: false,
+											selectedList: 4,
+											classes: 'typelist'
+										});
 				}
-				if($('#phonelist li').length > 1) {
+				if(phonelist.find('li').length > 1) {
 					$('#phones').show();
 					$('#contact_communication').show();
 				}
@@ -1335,8 +1359,6 @@ Contacts={
 			},
 		},
 		Addressbooks:{
-			droptarget:undefined,
-			droptext:t('contacts', 'Drop a VCF file<br />to import contacts.'),
 			overview:function(){
 				if($('#chooseaddressbook_dialog').dialog('isOpen') == true){
 					$('#chooseaddressbook_dialog').dialog('moveToTop');
@@ -1374,6 +1396,20 @@ Contacts={
 					}
 				  });
 			},
+			addAddressbook:function(name, description, cb) {
+				$.post(OC.filePath('contacts', 'ajax', 'addaddressbook.php'), { name: name, description: description, active: true },
+					function(jsondata){
+						if(jsondata.status == 'success'){
+							if(cb) {
+								cb(jsondata.data);
+							}
+						} else {
+							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+							return false;
+						}
+				});
+
+			},
 			newAddressbook:function(object){
 				var tr = $(document.createElement('tr'))
 					.load(OC.filePath('contacts', 'ajax', 'addbook.php'));
@@ -1393,6 +1429,7 @@ Contacts={
 					  function(jsondata) {
 						if (jsondata.status == 'success'){
 							$(obj).closest('tr').remove();
+							$('#contacts h3[data-id="'+bookid+'"],#contacts ul[data-id="'+bookid+'"]').remove();
 							Contacts.UI.Contacts.update();
 						} else {
 							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
@@ -1400,135 +1437,20 @@ Contacts={
 					  });
 				}
 			},
-			loadImportHandlers:function() {
-				$('#import_upload_start').change(function(){
-					Contacts.UI.Addressbooks.uploadImport(this.files);
-				});
-				$('#importaddressbook_dialog').find('.upload').click(function() {
-					Contacts.UI.Addressbooks.droptarget.html(t('contacts', 'Uploading...'));
-					Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, true);
-					//$('#import_upload_start').trigger('click');
-					//return false;
-				});
-				$('#importaddressbook_dialog').find('.upload').tipsy();
-				this.droptarget = $('#import_drop_target');
-				$(this.droptarget).bind('dragover',function(event){
-					$(event.target).addClass('droppable');
-					event.stopPropagation();
-					event.preventDefault();  
-				});
-				$(this.droptarget).bind('dragleave',function(event){
-					$(event.target).removeClass('droppable');
-				});
-				$(this.droptarget).bind('drop',function(event){
-					event.stopPropagation();
-					event.preventDefault();
-					$(event.target).removeClass('droppable');
-					$(event.target).html(t('contacts', 'Uploading...'));
-					Contacts.UI.loading(event.target, true);
-					$.importUpload(event.originalEvent.dataTransfer.files);
-				});
-
-				$.importUpload = function(files){
-					var file = files[0];
-					if(file.size > $('#max_upload').val()){
-						OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
-						$(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext);
-						Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
-						return;
-					}
-					if(file.type.indexOf('text') != 0) {
-						OC.dialogs.alert(t('contacts','You have dropped a file type that cannot be imported: ') + file.type, t('contacts','Wrong file type'));
-						$(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext);
-						Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
-						return;
-					}
-					var xhr = new XMLHttpRequest();
-
-					if (!xhr.upload) {
-						OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please upload the contacts file to ownCloud and import that way.'), t('contacts', 'Error'))
-					}
-					importUpload = xhr.upload,
-					xhr.onreadystatechange = function() {
-						if (xhr.readyState == 4){
-							response = $.parseJSON(xhr.responseText);
-							if(response.status == 'success') {
-								if(xhr.status == 200) {
-									Contacts.UI.Addressbooks.doImport(response.data.path, response.data.file);
-								} else {
-									$(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext);
-									Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
-									OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error'));
-								}
-							} else {
-								OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
-							}
-						}
-					};
-					xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadimport.php') + '?file='+encodeURIComponent(file.name)+'&requesttoken='+requesttoken, true);
-					xhr.setRequestHeader('Cache-Control', 'no-cache');
-					xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
-					xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name));
-					xhr.setRequestHeader('X-File-Size', file.size);
-					xhr.setRequestHeader('Content-Type', file.type);
-					xhr.send(file);
-				}
-			},
-			uploadImport:function(filelist) {
-				if(!filelist) {
-					OC.dialogs.alert(t('contacts','No files selected for upload.'), t('contacts', 'Error'));
-					return;
-				}
-				//var file = filelist.item(0);
-				var file = filelist[0];
-				var target = $('#import_upload_target');
-				var form = $('#import_upload_form');
-				var totalSize=0;
-				if(file.size > $('#max_upload').val()){
-					OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts', 'Error'));
-					return;
-				} else {
-					target.load(function(){
-						var response=jQuery.parseJSON(target.contents().text());
-						if(response != undefined && response.status == 'success'){
-							Contacts.UI.Addressbooks.doImport(response.data.path, response.data.file);
-						}else{
-							OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
-						}
-					});
-					form.submit();
-				}
-			},
-			importAddressbook:function(object){
-				var tr = $(document.createElement('tr'))
-					.load(OC.filePath('contacts', 'ajax', 'importaddressbook.php'));
-				$(object).closest('tr').after(tr).hide();
-			},
-			doImport:function(path, file){
-				$(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Importing...'));
-				Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, true);
-				var id = $('#importaddressbook_dialog').find('#book').val();
-				$.post(OC.filePath('contacts', '', 'import.php'), { id: id, path: path, file: file, fstype: 'OC_FilesystemView' },
+			doImport:function(file, aid){
+				$.post(OC.filePath('contacts', '', 'import.php'), { id: aid, file: file, fstype: 'OC_FilesystemView' },
 					function(jsondata){
-						if(jsondata.status == 'success'){
-							Contacts.UI.Addressbooks.droptarget.html(t('contacts', 'Import done. Success/Failure: ')+jsondata.data.imported+'/'+jsondata.data.failed);
-							$('#chooseaddressbook_dialog').find('#close_button').val(t('contacts', 'OK'));
-							Contacts.UI.Contacts.update();
-							setTimeout(
-									function() {
-										$(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext);
-									}, 5000);
-						} else {
-							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+						if(jsondata.status != 'success'){
+							Contacts.UI.notify({message:jsondata.data.message});
 						}
 				});
-				Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
+				return false;
 			},
 			submit:function(button, bookid){
 				var displayname = $("#displayname_"+bookid).val().trim();
 				var active = $("#edit_active_"+bookid+":checked").length;
 				var description = $("#description_"+bookid).val();
-				
+
 				if(displayname.length == 0) {
 					OC.dialogs.alert(t('contacts', 'Displayname cannot be empty.'), t('contacts', 'Error'));
 					return false;
@@ -1554,60 +1476,151 @@ Contacts={
 			}
 		},
 		Contacts:{
+			contacts:{},
 			batchnum:50,
+			getContact:function(id) {
+				if(!this.contacts[id]) {
+					this.contacts[id] = $('#contacts li[data-id="'+id+'"]');
+						if(!this.contacts[id]) {
+							self = this;
+							$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){
+								if(jsondata.status == 'success'){
+									self.contacts[id] = self.insertContact({data:jsondata.data});
+								}
+								else{
+									OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+								}
+							});
+						}
+				}
+				return this.contacts[id];
+			},
 			drop:function(event, ui) {
 				var dragitem = ui.draggable, droptarget = $(this);
-				//console.log('Drop ' + dragitem.data('id') +' on: ' + droptarget.data('id'));
+				if(dragitem.is('li')) {
+					Contacts.UI.Contacts.dropContact(event, dragitem, droptarget);
+				} else {
+					Contacts.UI.Contacts.dropAddressbook(event, dragitem, droptarget);
+				}
+			},
+			dropContact:function(event, dragitem, droptarget) {
 				if(dragitem.data('bookid') == droptarget.data('id')) {
 					return false;
 				}
 				var droplist = (droptarget.is('ul'))?droptarget:droptarget.next();
-				$.post(OC.filePath('contacts', 'ajax', 'movetoaddressbook.php'), { ids: dragitem.data('id'), aid: $(this).data('id') },
+				$.post(OC.filePath('contacts', 'ajax', 'movetoaddressbook.php'), { ids: dragitem.data('id'), aid: droptarget.data('id') },
 					function(jsondata){
 						if(jsondata.status == 'success'){
-							// Do some inserting/removing/sorting magic
-							var name = $(dragitem).find('a').html();
-							var added = false;
-							$(droplist).children().each(function(){
-								if ($(this).text().toLowerCase() > name.toLowerCase()) {
-									$(this).before(dragitem.detach()); //.fadeIn('slow');
-									added = true;
-									return false;
-								}
-							});
-							if(!added) {
-								$(droplist).append(dragitem.detach());
-							}
+							dragitem.attr('data-bookid', droptarget.data('id'))
 							dragitem.data('bookid', droptarget.data('id'));
+							Contacts.UI.Contacts.insertContact({
+								contactlist:droplist,
+								contact:dragitem.detach()
+							});
 							Contacts.UI.Contacts.scrollTo(dragitem.data('id'));
 						} else {
 							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
 						}
 				});
 			},
+			dropAddressbook:function(event, dragitem, droptarget) {
+				alert('Dropping address books not implemented yet');
+			},
+			/**
+			 * @params params An object with the properties 'contactlist':a jquery object of the ul to insert into,
+			 * 'contacts':a jquery object of all items in the list and either 'data': an object with the properties
+			 * id, addressbookid and displayname or 'contact': a listitem to be inserted directly.
+			 * If 'contactlist' or 'contacts' aren't defined they will be search for based in the properties in 'data'.
+			 */
+			insertContact:function(params) {
+				var id, bookid;
+				if(!params.contactlist) {
+					// FIXME: Check if contact really exists.
+					bookid = params.data ? params.data.addressbookid : params.contact.data('bookid');
+					id = params.data ? params.data.id : params.contact.data('id');
+					params.contactlist = $('#contacts ul[data-id="'+bookid+'"]');
+				}
+				if(!params.contacts) {
+					bookid = params.data ? params.data.addressbookid : params.contact.data('bookid');
+					id = params.data ? params.data.id : params.contact.data('id');
+					params.contacts = $('#contacts ul[data-id="'+bookid+'"] li');
+				}
+				var contact = params.data
+					? $('<li data-id="'+params.data.id+'" data-bookid="'+params.data.addressbookid+'" role="button"><a href="'+OC.linkTo('contacts', 'index.php')+'&id='+params.data.id+'"  style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+params.data.id+') no-repeat scroll 0% 0% transparent;">'+params.data.displayname+'</a></li>')
+					: params.contact;
+				var added = false;
+				var name = params.data ? params.data.displayname.toLowerCase() : contact.find('a').text().toLowerCase();
+				if(params.contacts) {
+					params.contacts.each(function() {
+						if ($(this).text().toLowerCase() > name) {
+							$(this).before(contact);
+							added = true;
+							return false;
+						}
+					});
+				}
+				if(!added || !params.contacts) {
+					params.contactlist.append(contact);
+				}
+				//this.contacts[id] = contact;
+				return contact;
+			},
+			next:function(reverse) {
+				// TODO: Check if we're last-child/first-child and jump to next/prev address book.
+				var curlistitem = $('#contacts li[data-id="'+Contacts.UI.Card.id+'"]');
+				var newlistitem = reverse ? curlistitem.prev('li') : curlistitem.next('li');
+				if(newlistitem) {
+					curlistitem.removeClass('active');
+					Contacts.UI.Card.update({
+						cid:newlistitem.data('id'),
+						aid:newlistitem.data('bookid')
+					});
+				}
+			},
+			previous:function() {
+				this.next(true);
+			},
 			// Reload the contacts list.
-			update:function(id, aid, start){
+			update:function(params){
+				if(!params) { params = {}; }
+				if(!params.start) {
+					if(params.aid) {
+						$('#contacts h3[data-id="'+params.aid+'"],#contacts ul[data-id="'+params.aid+'"]').remove();
+					} else {
+						$('#contacts').empty();
+					}
+				}
 				self = this;
-				console.log('update: ' + aid + ' ' + start);
+				console.log('update: ' + params.cid + ' ' + params.aid + ' ' + params.start);
 				var firstrun = false;
 				var opts = {};
-				opts['startat'] = (start?start:0);
-				if(aid) {
-					opts['aid'] = aid;
+				opts['startat'] = (params.start?params.start:0);
+				if(params.aid) {
+					opts['aid'] = params.aid;
 				}
 				$.getJSON(OC.filePath('contacts', 'ajax', 'contacts.php'),opts,function(jsondata){
 					if(jsondata.status == 'success'){
 						var books = jsondata.data.entries;
-						$.each(jsondata.data.entries, function(b, book) { 
+						$.each(books, function(b, book) {
 							if($('#contacts h3[data-id="'+b+'"]').length == 0) {
 								firstrun = true;
-								
 								if($('#contacts h3').length == 0) {
-									$('#contacts').html('<h3 class="addressbook" data-id="'+b+'">'+book.displayname+'</h3><ul class="contacts" data-id="'+b+'"></ul>');
+									$('#contacts').html('<h3 class="addressbook" contextmenu="addressbookmenu" data-id="'+b+'">'+book.displayname+'</h3><ul class="contacts hidden" data-id="'+b+'"></ul>');
 								} else {
 									if(!$('#contacts h3[data-id="'+b+'"]').length) {
-										$('<h3 class="addressbook" data-id="'+b+'">'+book.displayname+'</h3><ul class="contacts" data-id="'+b+'"></ul>')
-										.appendTo('#contacts');
+										var item = $('<h3 class="addressbook" contextmenu="addressbookmenu" data-id="'+b+'">'+book.displayname+'</h3><ul class="contacts hidden" data-id="'+b+'"></ul>')
+										var added = false;
+										$('#contacts h3').each(function(){
+											if ($(this).text().toLowerCase() > book.displayname.toLowerCase()) {
+												$(this).before(item).fadeIn('fast');
+												added = true;
+												return false;
+											}
+										});
+										if(!added) {
+											$('#contacts').append(item);
+										}
+
 									}
 								}
 								$('#contacts h3[data-id="'+b+'"]').on('click', function(event) {
@@ -1616,45 +1629,46 @@ Contacts={
 									$('#contacts ul[data-id="'+b+'"]').slideToggle(300);
 									return false;
 								});
-								var accept = 'li:not([data-bookid="'+b+'"])';
-								$('#contacts h3[data-id="'+b+'"]').droppable({
+								var accept = 'li:not([data-bookid="'+b+'"]),h3:not([data-id="'+b+'"])';
+								$('#contacts h3[data-id="'+b+'"],#contacts ul[data-id="'+b+'"]').droppable({
 									drop: Contacts.UI.Contacts.drop,
 									activeClass: 'ui-state-hover',
 									accept: accept
 								});
 							}
 							var contactlist = $('#contacts ul[data-id="'+b+'"]');
+							var contacts = $('#contacts ul[data-id="'+b+'"] li');
 							for(var c in book.contacts) {
 								if(book.contacts[c].id == undefined) { continue; }
-								if($('#contacts li[data-id="'+book.contacts[c]['id']+'"][data-id="'+book.contacts[c]['bookid']+'"]').length == 0) {
-									var contact = Contacts.UI.Card.createEntry(book.contacts[c]);
-									if(c == self.batchnum-5) {
+								if(!$('#contacts li[data-id="'+book.contacts[c]['id']+'"]').length) {
+									var contact = Contacts.UI.Contacts.insertContact({contactlist:contactlist, contacts:contacts, data:book.contacts[c]});
+									if(c == self.batchnum-10) {
 										contact.bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
 											$(this).unbind(event);
 											var bookid = $(this).data('bookid');
 											var numsiblings = $('.contacts li[data-bookid="'+bookid+'"]').length;
 											if (isInView && numsiblings >= self.batchnum) {
 												console.log('This would be a good time to load more contacts.');
-												Contacts.UI.Contacts.update(id, bookid, $('#contacts li[data-bookid="'+bookid+'"]').length);
+												Contacts.UI.Contacts.update({cid:params.cid, aid:bookid, start:$('#contacts li[data-bookid="'+bookid+'"]').length});
 											}
 										});
 									}
-									contactlist.append(contact);
 								}
 							}
 						});
 						if($('#contacts h3').length > 1) {
-							$('#contacts li').draggable({
+							$('#contacts li,#contacts h3').draggable({
+								distance: 10,
 								revert: 'invalid',
 								axis: 'y', containment: '#contacts',
-								scroll: true, scrollSensitivity: 100,
+								scroll: true, scrollSensitivity: 40,
 								opacity: 0.7, helper: 'clone'
 							});
 						} else {
 							$('#contacts h3').first().addClass('active');
 						}
 						if(opts['startat'] == 0) { // only update card on first load.
-							Contacts.UI.Card.update();
+							Contacts.UI.Card.update(params);
 						}
 					}
 					else{
@@ -1669,9 +1683,10 @@ Contacts={
 			},
 			scrollTo:function(id){
 				var item = $('#contacts li[data-id="'+id+'"]');
-				if(item) {
-					$('.contacts').animate({
-						scrollTop: $('#contacts li[data-id="'+id+'"]').offset().top-20}, 'slow','swing');
+				if(item && $.isNumeric(item.offset().top)) {
+					console.log('scrollTo ' + parseInt(item.offset().top));
+					$('#contacts').animate({
+						scrollTop: parseInt(item.offset()).top-40}, 'slow','swing');
 				}
 			}
 		}
@@ -1682,23 +1697,90 @@ $(document).ready(function(){
 	OCCategories.changed = Contacts.UI.Card.categoriesChanged;
 	OCCategories.app = 'contacts';
 
-	$('#notification').click(function(){
-		$('#notification').fadeOut();
+	$('#chooseaddressbook').on('click keydown', Contacts.UI.Addressbooks.overview);
+	$('#contacts_newcontact').on('click keydown', Contacts.UI.Card.editNew);
+
+	var ninjahelp = $('#ninjahelp');
+
+	ninjahelp.find('.close').on('click keydown',function() {
+		ninjahelp.hide();
+	});
+
+	$(document).on('keyup', function(event) {
+		console.log(event.which + ' ' + event.target.nodeName);
+		if(event.target.nodeName.toUpperCase() != 'BODY'
+			|| $('#contacts li').length == 0
+			|| !Contacts.UI.Card.id) {
+			return;
+		}
+		/**
+		 * To add:
+		 * (Shift)n/p: next/prev addressbook
+		 * u (85): hide/show leftcontent
+		 * f (70): add field
+		 */
+		switch(event.which) {
+			case 27: // Esc
+				ninjahelp.hide();
+				break;
+			case 46:
+				if(event.shiftKey) {
+					Contacts.UI.Card.delayedDelete();
+				}
+				break;
+			case 32: // space
+				if(event.shiftKey) {
+					Contacts.UI.Contacts.previous();
+					break;
+				}
+			case 40: // down
+			case 75: // k
+				Contacts.UI.Contacts.next();
+				break;
+			case 65: // a
+				if(event.shiftKey) {
+					// add addressbook
+					Contacts.UI.notImplemented();
+					break;
+				}
+				Contacts.UI.Card.editNew();
+				break;
+			case 38: // up
+			case 74: // j
+				Contacts.UI.Contacts.previous();
+				break;
+			case 78: // n
+				// next addressbook
+				Contacts.UI.notImplemented();
+				break;
+			case 13: // Enter
+			case 79: // o
+				var aid = $('#contacts h3.active').first().data('id');
+				if(aid) {
+					$('#contacts ul[data-id="'+aid+'"]').slideToggle(300);
+				}
+				break;
+			case 80: // p
+				// prev addressbook
+				Contacts.UI.notImplemented();
+				break;
+			case 82: // r
+				Contacts.UI.Contacts.update({cid:Contacts.UI.Card.id});
+				break;
+			case 191: // ?
+				ninjahelp.toggle('fast');
+				break;
+		}
+
 	});
-	
-	$('#chooseaddressbook').click(Contacts.UI.Addressbooks.overview);
-	$('#chooseaddressbook').keydown(Contacts.UI.Addressbooks.overview);
 
-	$('#contacts_newcontact').click(Contacts.UI.Card.editNew);
-	$('#contacts_newcontact').keydown(Contacts.UI.Card.editNew);
-	
-	// Load a contact.
+			// Load a contact.
 	$('.contacts').keydown(function(event) {
-		if(event.which == 13) {
+		if(event.which == 13 || event.which == 32) {
 			$('.contacts').click();
 		}
 	});
-	$(document).on('click', '.contacts', function(event){
+	$(document).on('click', '#contacts', function(event){
 		var $tgt = $(event.target);
 		if ($tgt.is('li') || $tgt.is('a')) {
 			var item = $tgt.is('li')?$($tgt):($tgt).parent();
@@ -1707,7 +1789,13 @@ $(document).ready(function(){
 			item.addClass('active');
 			var oldid = $('#rightcontent').data('id');
 			if(oldid != 0){
-				$('.contacts li[data-id="'+oldid+'"]').removeClass('active');
+				var olditem = $('.contacts li[data-id="'+oldid+'"]');
+				var oldbookid = olditem.data('bookid');
+				olditem.removeClass('active');
+				if(oldbookid != bookid) {
+					$('#contacts h3[data-id="'+oldbookid+'"]').removeClass('active');
+					$('#contacts h3[data-id="'+bookid+'"]').addClass('active');
+				}
 			}
 			$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){
 				if(jsondata.status == 'success'){
@@ -1721,81 +1809,267 @@ $(document).ready(function(){
 		return false;
 	});
 
-	/*$('.contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
-		if (isInView) { //NOTE: I've kept all conditions for future reference ;-)
-			// element is now visible in the viewport
-			if (visiblePartY == 'top') {
-				// top part of element is visible
-			} else if (visiblePartY == 'bottom') {
-				// bottom part of element is visible
-			} else {
-				// whole part of element is visible
-				if (!$(this).find('a').attr('style')) {
-					//alert($(this).data('id') + ' has background: ' + $(this).attr('style'));
-					$(this).find('a').css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+$(this).data('id')+') no-repeat');
-				}// else {
-				//	alert($(this).data('id') + ' has style ' + $(this).attr('style').match('url'));
-				//}
-			}
-		} else {
-			// element has gone out of viewport
-		}
-	});*/
-	
 	$('.contacts_property').live('change', function(){
 		Contacts.UI.Card.saveProperty(this);
 	});
 
-	/**
-	 * Upload function for dropped files. Should go in the Contacts class/object.
-	 */
-	$.fileUpload = function(files){
-		var file = files[0];
-		if(file.size > $('#max_upload').val()){
-			OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
-			return;
-		}
-		if (file.type.indexOf("image") != 0) {
-			OC.dialogs.alert(t('contacts','Only image files can be used as profile picture.'), t('contacts','Wrong file type'));
-			return;
-		}
-		var xhr = new XMLHttpRequest();
+	$(function() {
+		// Upload function for dropped contact photos files. Should go in the Contacts class/object.
+		$.fileUpload = function(files){
+			var file = files[0];
+			if(file.size > $('#max_upload').val()){
+				OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
+				return;
+			}
+			if (file.type.indexOf("image") != 0) {
+				OC.dialogs.alert(t('contacts','Only image files can be used as profile picture.'), t('contacts','Wrong file type'));
+				return;
+			}
+			var xhr = new XMLHttpRequest();
+
+			if (!xhr.upload) {
+				OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.'), t('contacts', 'Error'))
+			}
+			fileUpload = xhr.upload,
+			xhr.onreadystatechange = function() {
+				if (xhr.readyState == 4){
+					response = $.parseJSON(xhr.responseText);
+					if(response.status == 'success') {
+						if(xhr.status == 200) {
+							Contacts.UI.Card.editPhoto(response.data.id, response.data.tmp);
+						} else {
+							OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error'));
+						}
+					} else {
+						OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
+					}
+				}
+			};
 
-		if (!xhr.upload) {
-			OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.'), t('contacts', 'Error'))
+			fileUpload.onprogress = function(e){
+				if (e.lengthComputable){
+					var _progress = Math.round((e.loaded * 100) / e.total);
+					//if (_progress != 100){
+					//}
+				}
+			};
+			xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadphoto.php')+'?id='+Contacts.UI.Card.id+'&requesttoken='+requesttoken+'&imagefile='+encodeURIComponent(file.name), true);
+			xhr.setRequestHeader('Cache-Control', 'no-cache');
+			xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+			xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name));
+			xhr.setRequestHeader('X-File-Size', file.size);
+			xhr.setRequestHeader('Content-Type', file.type);
+			xhr.send(file);
 		}
-		fileUpload = xhr.upload,
-		xhr.onreadystatechange = function() {
-			if (xhr.readyState == 4){
-				response = $.parseJSON(xhr.responseText);
-				if(response.status == 'success') {
-					if(xhr.status == 200) {
-						Contacts.UI.Card.editPhoto(response.data.id, response.data.tmp);
+	});
+
+	$(document).bind('drop dragover', function (e) {
+			e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
+	});
+
+	//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
+	if(navigator.userAgent.search(/konqueror/i)==-1){
+		$('#import_upload_start').attr('multiple','multiple')
+	}
+	// Import using jquery.fileupload
+	$(function() {
+		var uploadingFiles = {}, numfiles = 0, uploadedfiles = 0, retries = 0;
+		var aid;
+
+		$('#import_upload_start').fileupload({
+			dropZone: $('#contacts'), // restrict dropZone to contacts list.
+			acceptFileTypes:  /^text\/(directory|vcard|x-vcard)$/i,
+			add: function(e, data) {
+				var files = data.files;
+				var totalSize=0;
+				if(files) {
+					numfiles += files.length; uploadedfiles = 0;
+					for(var i=0;i<files.length;i++) {
+						if(files[i].size ==0 && files[i].type== '') {
+							OC.dialogs.alert(t('files', 'Unable to upload your file as it is a directory or has 0 bytes'), t('files', 'Upload Error'));
+							return;
+						}
+						totalSize+=files[i].size;
+					}
+				}
+				if(totalSize>$('#max_upload').val()){
+					OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
+					numfiles = uploadedfiles = retries = aid = 0;
+					uploadingFiles = {};
+					return;
+				}else{
+					if($.support.xhrFileUpload) {
+						for(var i=0;i<files.length;i++){
+							var fileName = files[i].name;
+							var dropTarget;
+							if($(e.originalEvent.target).is('h3')) {
+								dropTarget = $(e.originalEvent.target).next('ul');
+							} else {
+								dropTarget = $(e.originalEvent.target).closest('ul');
+							}
+							if(dropTarget && dropTarget.hasClass('contacts')) { // TODO: More thorough check for where we are.
+								aid = dropTarget.attr('data-id');
+							} else {
+								aid = undefined;
+							}
+							var jqXHR =  $('#import_upload_start').fileupload('send', {files: files[i],
+								formData: function(form) {
+									var formArray = form.serializeArray();
+									formArray['aid'] = aid;
+									return formArray;
+								}})
+								.success(function(result, textStatus, jqXHR) {
+									if(result.status == 'success') {
+										// import the file
+										uploadedfiles += 1;
+									} else {
+										Contacts.UI.notify({message:jsondata.data.message});
+									}
+									return false;
+								})
+								.error(function(jqXHR, textStatus, errorThrown) {
+									console.log(textStatus);
+									Contacts.UI.notify({message:errorThrown + ': ' + textStatus,});
+								});
+							uploadingFiles[fileName] = jqXHR;
+						}
 					} else {
-						OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error'));
+						data.submit().success(function(data, status) {
+							response = jQuery.parseJSON(data[0].body.innerText);
+							if(response[0] != undefined && response[0].status == 'success') {
+								var file=response[0];
+								delete uploadingFiles[file.name];
+								$('tr').filterAttr('data-file',file.name).data('mime',file.mime);
+								var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text();
+								if(size==t('files','Pending')){
+									$('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size);
+								}
+								FileList.loadingDone(file.name);
+							} else {
+								Contacts.UI.notify({message:response.data.message});
+							}
+						});
 					}
+				}
+			},
+			fail: function(e, data) {
+				console.log('fail');
+				Contacts.UI.notify({message:data.errorThrown + ': ' + data.textStatus});
+				// TODO: Remove file from upload queue.
+			},
+			progressall: function(e, data) {
+				var progress = (data.loaded/data.total)*50;
+				$('#uploadprogressbar').progressbar('value',progress);
+			},
+			start: function(e, data) {
+				$('#uploadprogressbar').progressbar({value:0});
+				$('#uploadprogressbar').fadeIn();
+				if(data.dataType != 'iframe ') {
+					$('#upload input.stop').show();
+				}
+			},
+			stop: function(e, data) {
+				// stop only gets fired once so we collect uploaded items here.
+				var importFiles = function(aid, fileList) {
+					// Create a closure that can be called from different places.
+					if(numfiles != uploadedfiles) {
+						Contacts.UI.notify({message:t('contacts', 'Not all files uploaded. Retrying...')});
+						retries += 1;
+						if(retries > 3) {
+							numfiles = uploadedfiles = retries = aid = 0;
+							uploadingFiles = {};
+							$('#uploadprogressbar').fadeOut();
+							OC.dialogs.alert(t('contacts', 'Something went wrong with the upload, please retry.'), t('contacts', 'Error'));
+							return;
+						}
+						setTimeout(function() { // Just to let any uploads finish
+							importFiles(aid, uploadingFiles);
+						}, 1000);
+					}
+					$('#uploadprogressbar').progressbar('value',50);
+					var todo = uploadedfiles;
+					$.each(fileList, function(fileName, data) {
+						Contacts.UI.Addressbooks.doImport(fileName, aid);
+						delete fileList[fileName];
+						numfiles -= 1; uploadedfiles -= 1;
+						$('#uploadprogressbar').progressbar('value',50+(50/(todo-uploadedfiles)));
+					})
+					$('#uploadprogressbar').progressbar('value',100);
+					$('#uploadprogressbar').fadeOut();
+					setTimeout(function() {
+						Contacts.UI.Contacts.update({aid:aid});
+						numfiles = uploadedfiles = retries = aid = 0;
+					}, 1000);
+				}
+				if(!aid) {
+					// Either selected with filepicker or dropped outside of an address book.
+					$.getJSON(OC.filePath('contacts', 'ajax', 'selectaddressbook.php'),{},function(jsondata) {
+						if(jsondata.status == 'success') {
+							if($('#selectaddressbook_dialog').dialog('isOpen') == true) {
+								$('#selectaddressbook_dialog').dialog('moveToTop');
+							} else {
+								$('#dialog_holder').html(jsondata.data.page).ready(function($) {
+									var select_dlg = $('#selectaddressbook_dialog');
+									select_dlg.dialog({
+										modal: true, height: 'auto', width: 'auto',
+										buttons: {
+											'Ok':function() {
+												aid = select_dlg.find('input:checked').val();
+												if(aid == 'new') {
+													var displayname = select_dlg.find('input.name').val();
+													var description = select_dlg.find('input.desc').val();
+													if(!displayname.trim()) {
+														OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error'));
+														return false;
+													}
+													$(this).dialog('close');
+													Contacts.UI.Addressbooks.addAddressbook(displayname, description, function(addressbook){
+														aid = addressbook.id;
+														setTimeout(function() {
+															importFiles(aid, uploadingFiles);
+														}, 500);
+														console.log('aid ' + aid);
+													});
+												} else {
+													setTimeout(function() {
+														importFiles(aid, uploadingFiles);
+													}, 500);
+													console.log('aid ' + aid);
+													$(this).dialog('close');
+												}
+											},
+											'Cancel':function() {
+												$(this).dialog('close');
+												numfiles = uploadedfiles = retries = aid = 0;
+												uploadingFiles = {};
+												$('#uploadprogressbar').fadeOut();
+											}
+										},
+										close: function(event, ui) {
+											// TODO: If numfiles != 0 delete tmp files after a timeout.
+											$(this).dialog('destroy').remove();
+										}
+									});
+								});
+							}
+						} else {
+							$('#uploadprogressbar').fadeOut();
+							OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+						}
+					});
 				} else {
-					OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
+					// Dropped on an address book or it's list.
+					setTimeout(function() { // Just to let any uploads finish
+						importFiles(aid, uploadingFiles);
+					}, 1000);
+				}
+				if(data.dataType != 'iframe ') {
+					$('#upload input.stop').hide();
 				}
 			}
-		};
-	
-		fileUpload.onprogress = function(e){
-			if (e.lengthComputable){
-				var _progress = Math.round((e.loaded * 100) / e.total);
-				//if (_progress != 100){
-				//}
-			}
-		};
-		xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadphoto.php')+'?id='+Contacts.UI.Card.id+'&requesttoken='+requesttoken+'&imagefile='+encodeURIComponent(file.name), true);
-		xhr.setRequestHeader('Cache-Control', 'no-cache');
-		xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
-		xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name));
-		xhr.setRequestHeader('X-File-Size', file.size);
-		xhr.setRequestHeader('Content-Type', file.type);
-		xhr.send(file);
-	}
+		})
+	});
 
 	Contacts.UI.loadHandlers();
-	Contacts.UI.Contacts.update(id);
+	Contacts.UI.Contacts.update({cid:id});
 });
diff --git a/apps/contacts/js/jquery.inview.js b/apps/contacts/js/jquery.inview.js
index 01900b0b4b4be847ea1009415af870987e9c39cc..9687cd833681f195eaf61262d0a0f342f5c60381 100644
--- a/apps/contacts/js/jquery.inview.js
+++ b/apps/contacts/js/jquery.inview.js
@@ -62,8 +62,8 @@
 
   function getViewportOffset() {
     return {
-      top:  w.pageYOffset || documentElement.scrollTop   || d.body.scrollTop,
-      left: w.pageXOffset || documentElement.scrollLeft  || d.body.scrollLeft
+      top:  w.pageYOffset || documentElement.scrollTop   || (d.body?d.body.scrollTop:0),
+      left: w.pageXOffset || documentElement.scrollLeft  || (d.body?d.body.scrollLeft:0)
     };
   }
 
diff --git a/apps/contacts/js/loader.js b/apps/contacts/js/loader.js
index 577ad1030645d85bcb4c570884180c005b88d76f..5bca0ab72379a63a16cfe06ffaa15d0212402524 100644
--- a/apps/contacts/js/loader.js
+++ b/apps/contacts/js/loader.js
@@ -78,9 +78,9 @@ Contacts_Import={
 }
 $(document).ready(function(){
 	if(typeof FileActions !== 'undefined'){
-		FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog); 
+		FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
 		FileActions.setDefault('text/vcard','importaddressbook');
-		FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog); 
+		FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog);
 		FileActions.setDefault('text/x-vcard','importaddressbook');
 	};
 });
\ No newline at end of file
diff --git a/apps/contacts/lib/VCFExportPlugin.php b/apps/contacts/lib/VCFExportPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a64c964b06e7b090dd46b447ab84c1c217294e4
--- /dev/null
+++ b/apps/contacts/lib/VCFExportPlugin.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * VCF Exporter
+ *
+ * This plugin adds the ability to export entire address books as .vcf files.
+ * This is useful for clients that don't support CardDAV yet. They often do
+ * support vcf files.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_VCFExportPlugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Reference to Server class
+     *
+     * @var Sabre_DAV_Server
+     */
+    private $server;
+
+    /**
+     * Initializes the plugin and registers event handlers
+     *
+     * @param Sabre_DAV_Server $server
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod', array($this,'beforeMethod'), 90);
+
+    }
+
+    /**
+     * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+     * with ?export
+     *
+     * @param string $method
+     * @param string $uri
+     * @return bool
+     */
+    public function beforeMethod($method, $uri) {
+
+        if ($method!='GET') return;
+        if ($this->server->httpRequest->getQueryString()!='export') return;
+
+        // splitting uri
+        list($uri) = explode('?', $uri, 2);
+
+        $node = $this->server->tree->getNodeForPath($uri);
+
+        if (!($node instanceof Sabre_CardDAV_IAddressBook)) return;
+
+        // Checking ACL, if available.
+        if ($aclPlugin = $this->server->getPlugin('acl')) {
+            $aclPlugin->checkPrivileges($uri, '{DAV:}read');
+        }
+
+        $this->server->httpResponse->setHeader('Content-Type', 'text/directory');
+        $this->server->httpResponse->sendStatus(200);
+
+        $nodes = $this->server->getPropertiesForPath($uri, array(
+            '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data',
+        ), 1);
+
+        $this->server->httpResponse->sendBody($this->generateVCF($nodes));
+
+        // Returning false to break the event chain
+        return false;
+
+    }
+
+    /**
+     * Merges all vcard objects, and builds one big vcf export
+     *
+     * @param array $nodes
+     * @return string
+     */
+    public function generateVCF(array $nodes) {
+        $objects = array();
+
+        foreach($nodes as $node) {
+
+            if (!isset($node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data'])) {
+                continue;
+            }
+            $nodeData = $node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data'];
+            $objects[] = $nodeData;
+
+        }
+
+        return implode("\r\n", $objects);
+
+    }
+
+}
diff --git a/apps/contacts/lib/addressbook.php b/apps/contacts/lib/addressbook.php
index 7549464f46484ca0ceec4893e439cb3453543e60..eb61b6dbcedac5b3e127c404c7a1d4e4c2f8b383 100644
--- a/apps/contacts/lib/addressbook.php
+++ b/apps/contacts/lib/addressbook.php
@@ -37,17 +37,17 @@
 /**
  * This class manages our addressbooks.
  */
-class OC_Contacts_Addressbook{
+class OC_Contacts_Addressbook {
 	/**
 	 * @brief Returns the list of addressbooks for a specific user.
 	 * @param string $uid
 	 * @param boolean $active Only return addressbooks with this $active state, default(=false) is don't care
 	 * @return array or false.
 	 */
-	public static function all($uid, $active=false){
+	public static function all($uid, $active=false) {
 		$values = array($uid);
 		$active_where = '';
-		if ($active){
+		if ($active) {
 			$active_where = ' AND active = ?';
 			$values[] = 1;
 		}
@@ -55,13 +55,13 @@ class OC_Contacts_Addressbook{
 			$stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE userid = ? ' . $active_where . ' ORDER BY displayname' );
 			$result = $stmt->execute($values);
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.' exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.' uid: '.$uid,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' uid: '.$uid, OCP\Util::DEBUG);
 			return false;
 		}
 
 		$addressbooks = array();
-		while( $row = $result->fetchRow()){
+		while( $row = $result->fetchRow()) {
 			$addressbooks[] = $row;
 		}
 		if(!$active && !count($addressbooks)) {
@@ -76,14 +76,14 @@ class OC_Contacts_Addressbook{
 	 * @param integer $uid User id. If null current user will be used.
 	 * @return array
 	 */
-	public static function activeIds($uid = null){
-		if(is_null($uid)){
+	public static function activeIds($uid = null) {
+		if(is_null($uid)) {
 			$uid = OCP\USER::getUser();
 		}
 		$activeaddressbooks = self::all($uid, true);
 		$ids = array();
 		foreach($activeaddressbooks as $addressbook) {
-			$ids[] = $addressbook['userid'];
+			$ids[] = $addressbook['id'];
 		}
 		return $ids;
 	}
@@ -93,7 +93,7 @@ class OC_Contacts_Addressbook{
 	 * @param string $uid
 	 * @return array
 	 */
-	public static function active($uid){
+	public static function active($uid) {
 		return self::all($uid, true);
 	}
 
@@ -112,13 +112,13 @@ class OC_Contacts_Addressbook{
 	 * @param integer $id
 	 * @return associative array or false.
 	 */
-	public static function find($id){
+	public static function find($id) {
 		try {
 			$stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
 			$result = $stmt->execute(array($id));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id: '.$id,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -129,11 +129,11 @@ class OC_Contacts_Addressbook{
 	 * @brief Adds default address book
 	 * @return $id ID of the newly created addressbook or false on error.
 	 */
-	public static function addDefault($uid = null){
+	public static function addDefault($uid = null) {
 		if(is_null($uid)) {
 			$uid = OCP\USER::getUser();
 		}
-		$id = self::add($uid,'default','Default Address Book');
+		$id = self::add($uid, 'Contacts', 'Default Address Book');
 		if($id !== false) {
 			self::setActive($id, true);
 		}
@@ -147,13 +147,13 @@ class OC_Contacts_Addressbook{
 	 * @param string $description
 	 * @return insertid
 	 */
-	public static function add($uid,$name,$description=''){
+	public static function add($uid,$name,$description='') {
 		try {
 			$stmt = OCP\DB::prepare( 'SELECT uri FROM *PREFIX*contacts_addressbooks WHERE userid = ? ' );
 			$result = $stmt->execute(array($uid));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.' exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.' uid: '.$uid,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' uid: '.$uid, OCP\Util::DEBUG);
 			return false;
 		}
 		$uris = array();
@@ -166,8 +166,8 @@ class OC_Contacts_Addressbook{
 			$stmt = OCP\DB::prepare( 'INSERT INTO *PREFIX*contacts_addressbooks (userid,displayname,uri,description,ctag) VALUES(?,?,?,?,?)' );
 			$result = $stmt->execute(array($uid,$name,$uri,$description,1));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', uid: '.$uid,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', uid: '.$uid, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -182,16 +182,16 @@ class OC_Contacts_Addressbook{
 	 * @param string $description
 	 * @return insertid or false
 	 */
-	public static function addFromDAVData($principaluri,$uri,$name,$description){
+	public static function addFromDAVData($principaluri,$uri,$name,$description) {
 		$uid = self::extractUserID($principaluri);
 
 		try {
 			$stmt = OCP\DB::prepare('INSERT INTO *PREFIX*contacts_addressbooks (userid,displayname,uri,description,ctag) VALUES(?,?,?,?,?)');
 			$result = $stmt->execute(array($uid,$name,$uri,$description,1));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', uid: '.$uid,OCP\Util::DEBUG);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', uri: '.$uri,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', uid: '.$uid, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', uri: '.$uri, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -205,14 +205,14 @@ class OC_Contacts_Addressbook{
 	 * @param string $description
 	 * @return boolean
 	 */
-	public static function edit($id,$name,$description){
+	public static function edit($id,$name,$description) {
 		// Need these ones for checking uri
 		$addressbook = self::find($id);
 
-		if(is_null($name)){
+		if(is_null($name)) {
 			$name = $addressbook['name'];
 		}
-		if(is_null($description)){
+		if(is_null($description)) {
 			$description = $addressbook['description'];
 		}
 
@@ -220,8 +220,8 @@ class OC_Contacts_Addressbook{
 			$stmt = OCP\DB::prepare('UPDATE *PREFIX*contacts_addressbooks SET displayname=?,description=?, ctag=ctag+1 WHERE id=?');
 			$result = $stmt->execute(array($name,$description,$id));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id: '.$id,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -234,15 +234,15 @@ class OC_Contacts_Addressbook{
 	 * @param boolean $active
 	 * @return boolean
 	 */
-	public static function setActive($id,$active){
+	public static function setActive($id,$active) {
 		$sql = 'UPDATE *PREFIX*contacts_addressbooks SET active = ? WHERE id = ?';
-		OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id: '.$id.', active: '.intval($active),OCP\Util::ERROR);
+		OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id.', active: '.intval($active), OCP\Util::ERROR);
 		try {
 			$stmt = OCP\DB::prepare($sql);
 			$stmt->execute(array(intval($active), $id));
 			return true;
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception for '.$id.': '.$e->getMessage(),OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception for '.$id.': '.$e->getMessage(), OCP\Util::ERROR);
 			return false;
 		}
 	}
@@ -252,7 +252,7 @@ class OC_Contacts_Addressbook{
 	 * @param integer $id ID of the address book.
 	 * @return boolean
 	 */
-	public static function isActive($id){
+	public static function isActive($id) {
 		$sql = 'SELECT active FROM *PREFIX*contacts_addressbooks WHERE id = ?';
 		try {
 			$stmt = OCP\DB::prepare( $sql );
@@ -260,7 +260,7 @@ class OC_Contacts_Addressbook{
 			$row = $result->fetchRow();
 			return (bool)$row['active'];
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
 		}
 	}
 
@@ -269,13 +269,13 @@ class OC_Contacts_Addressbook{
 	 * @param integer $id
 	 * @return boolean
 	 */
-	public static function delete($id){
+	public static function delete($id) {
 		self::setActive($id, false);
 		try {
 			$stmt = OCP\DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
 			$stmt->execute(array($id));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception for '.$id.': '.$e->getMessage(),OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception for '.$id.': '.$e->getMessage(), OCP\Util::ERROR);
 			return false;
 		}
 		
@@ -292,7 +292,7 @@ class OC_Contacts_Addressbook{
 	 * @param integer $id
 	 * @return boolean
 	 */
-	public static function touch($id){
+	public static function touch($id) {
 		$stmt = OCP\DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET ctag = ctag + 1 WHERE id = ?' );
 		$stmt->execute(array($id));
 
@@ -305,11 +305,11 @@ class OC_Contacts_Addressbook{
 	 * @param array  $existing existing addressbook URIs
 	 * @return string new name
 	 */
-	public static function createURI($name,$existing){
-		$name = strtolower($name);
+	public static function createURI($name,$existing) {
+		$name = str_replace(' ', '_', strtolower($name));
 		$newname = $name;
 		$i = 1;
-		while(in_array($newname,$existing)){
+		while(in_array($newname, $existing)) {
 			$newname = $name.$i;
 			$i = $i + 1;
 		}
@@ -320,8 +320,8 @@ class OC_Contacts_Addressbook{
 	 * @brief gets the userid from a principal path
 	 * @return string
 	 */
-	public static function extractUserID($principaluri){
-		list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri);
+	public static function extractUserID($principaluri) {
+		list($prefix, $userid) = Sabre_DAV_URLUtil::splitPath($principaluri);
 		return $userid;
 	}
 }
diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php
index 25461877e627c00445605b3c8b7df7f0f2cc4383..689149367fa744e034f4d74ef78ff163f3846217 100644
--- a/apps/contacts/lib/app.php
+++ b/apps/contacts/lib/app.php
@@ -41,7 +41,7 @@ class OC_Contacts_App {
 		$card = OC_Contacts_VCard::find( $id );
 		if( $card === false ) {
 			OCP\Util::writeLog('contacts', 'Contact could not be found: '.$id, OCP\Util::ERROR);
-			OCP\JSON::error(array('data' => array( 'message' => self::$l10n->t('Contact could not be found.').' '.$id)));
+			OCP\JSON::error(array('data' => array( 'message' => self::$l10n->t('Contact could not be found.').' '.print_r($id, true))));
 			exit();
 		}
 
@@ -61,11 +61,11 @@ class OC_Contacts_App {
 		if(!is_null($vcard) && !$vcard->__isset('N')) {
 			$version = OCP\App::getAppVersion('contacts');
 			if($version >= 5) {
-				OCP\Util::writeLog('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', 'OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OCP\Util::DEBUG);
 			}
-			OCP\Util::writeLog('contacts','getContactVCard, Missing N field', OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', 'getContactVCard, Missing N field', OCP\Util::DEBUG);
 			if($vcard->__isset('FN')) {
-				OCP\Util::writeLog('contacts','getContactVCard, found FN field: '.$vcard->__get('FN'), OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', 'getContactVCard, found FN field: '.$vcard->__get('FN'), OCP\Util::DEBUG);
 				$n = implode(';', array_reverse(array_slice(explode(' ', $vcard->__get('FN')), 0, 2))).';;;';
 				$vcard->setString('N', $n);
 				OC_Contacts_VCard::edit( $id, $vcard);
@@ -196,16 +196,19 @@ class OC_Contacts_App {
 				foreach($vcaddressbooks as $vcaddressbook) {
 					$vcaddressbookids[] = $vcaddressbook['id'];
 				}
-				$vccontacts = OC_Contacts_VCard::all($vcaddressbookids);
-			}
-		}
-		if(is_array($vccontacts) && count($vccontacts) > 0) {
-			$cards = array();
-			foreach($vccontacts as $vccontact) {
-				$cards[] = $vccontact['carddata'];
+				$start = 0;
+				$batchsize = 10;
+				while($vccontacts = OC_Contacts_VCard::all($vcaddressbookids, $start, $batchsize)){
+					$cards = array();
+					foreach($vccontacts as $vccontact) {
+						$cards[] = $vccontact['carddata'];
+					}
+					OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', scanning: '.$batchsize.' starting from '.$start, OCP\Util::DEBUG);
+					// only reset on first batch.
+					self::getVCategories()->rescan($cards, true, ($start == 0 ? true : false));
+					$start += $batchsize;
+				}
 			}
-
-			self::$categories->rescan($cards);
 		}
 	}
 
diff --git a/apps/contacts/lib/connector_sabre.php b/apps/contacts/lib/connector_sabre.php
index c967e906601c2c10af30ab06932f5bd71d8146f8..9fcfff08fa809419fdaf95e5d117b9e594efe2d4 100644
--- a/apps/contacts/lib/connector_sabre.php
+++ b/apps/contacts/lib/connector_sabre.php
@@ -79,7 +79,7 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract {
 			}
 		}
 
-		OC_Contacts_Addressbook::edit($addressbookid,$name,$description);
+		OC_Contacts_Addressbook::edit($addressbookid, $name, $description);
 
 		return true;
 
@@ -113,7 +113,7 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract {
 
 		}
 
-		OC_Contacts_Addressbook::addFromDAVData($principaluri,$url,$name,$description);
+		OC_Contacts_Addressbook::addFromDAVData($principaluri, $url, $name, $description);
 	}
 
 	/**
@@ -138,7 +138,9 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract {
 		foreach($data as $i){
 			$cards[] = array(
 				'id' => $i['id'],
-				'carddata' => $i['carddata'],
+				//'carddata' => $i['carddata'],
+				'size' => strlen($i['carddata']),
+				'etag' => md5($i['carddata']),
 				'uri' => $i['uri'],
 				'lastmodified' => $i['lastmodified'] );
 		}
@@ -154,7 +156,7 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract {
 	 * @return array
 	 */
 	public function getCard($addressbookid, $carduri) {
-		return OC_Contacts_VCard::findWhereDAVDataIs($addressbookid,$carduri);
+		return OC_Contacts_VCard::findWhereDAVDataIs($addressbookid, $carduri);
 
 	}
 
diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php
index d91d3c565b52fcc20aa47dda028df6911b483b66..3344e3d6939a4dc936367bfb0df006551cb31c7c 100644
--- a/apps/contacts/lib/hooks.php
+++ b/apps/contacts/lib/hooks.php
@@ -34,12 +34,12 @@
  */
 class OC_Contacts_Hooks{
 	/**
-	 * @brief Add default Addressbooks of a certain user
-	 * @param paramters parameters from postDeleteUser-Hook
+	 * @brief Add default Addressbook for a certain user
+	 * @param paramters parameters from postCreateUser-Hook
 	 * @return array
 	 */
 	static public function createUser($parameters) {
-		OC_Contacts_Addressbook::addDefault($parameters['uid'],'default','Default Address Book');
+		OC_Contacts_Addressbook::addDefault($parameters['uid']);
 		return true;
 	}
 	
@@ -61,8 +61,8 @@ class OC_Contacts_Hooks{
 	static public function getCalenderSources($parameters) {
 		$base_url = OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id=';
 		foreach(OC_Contacts_Addressbook::all(OCP\USER::getUser()) as $addressbook) {
-			$parameters['sources'][] =
-				array(
+			$parameters['sources'][] 
+				= array(
 					'url' => $base_url.'birthday_'. $addressbook['id'],
 					'backgroundColor' => '#cccccc',
 					'borderColor' => '#888',
@@ -91,18 +91,24 @@ class OC_Contacts_Hooks{
 				$date = new DateTime($birthday);
 				$vevent = new OC_VObject('VEVENT');
 				//$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV));
-				$vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE);
+				$vevent->setDateTime('DTSTART', $date, 
+					Sabre_VObject_Element_DateTime::DATE);
 				$vevent->setString('DURATION', 'P1D');
-				$vevent->setString('UID', substr(md5(rand().time()),0,10));
+				$vevent->setString('UID', substr(md5(rand().time()), 0, 10));
 				// DESCRIPTION?
 				$vevent->setString('RRULE', 'FREQ=YEARLY');
-				$title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday'));
+				$title = str_replace('{name}',
+					$vcard->getAsString('FN'), 
+					OC_Contacts_App::$l10n->t('{name}\'s Birthday'));
 				$parameters['events'][] = array(
 					'id' => 0,//$card['id'],
 					'vevent' => $vevent,
 					'repeating' => true,
 					'summary' => $title,
-					'calendardata' => "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Contacts " . OCP\App::getAppVersion('contacts') . "\n" . $vevent->serialize() .  "END:VCALENDAR"
+					'calendardata' => "BEGIN:VCALENDAR\nVERSION:2.0\n"
+						. "PRODID:ownCloud Contacts " 
+						. OCP\App::getAppVersion('contacts') . "\n" 
+						. $vevent->serialize() .  "END:VCALENDAR"
 					);
 			}
 		}
diff --git a/apps/contacts/lib/search.php b/apps/contacts/lib/search.php
index 5d9ca97e761aafcebb84b03632b4b0567483d1ac..53aa2b48496a13609d6fcced0ac0125024a78fce 100644
--- a/apps/contacts/lib/search.php
+++ b/apps/contacts/lib/search.php
@@ -2,7 +2,7 @@
 class OC_Search_Provider_Contacts extends OC_Search_Provider{
 	function search($query){
 		$addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser(), 1);
-		if(count($addressbooks)==0 || !OCP\App::isEnabled('contacts')){
+		if(count($addressbooks)==0 || !OCP\App::isEnabled('contacts')) {
 			return array();
 		}
 		$results=array();
@@ -10,9 +10,9 @@ class OC_Search_Provider_Contacts extends OC_Search_Provider{
 		foreach($addressbooks as $addressbook){
 			$vcards = OC_Contacts_VCard::all($addressbook['id']);
 			foreach($vcards as $vcard){
-				if(substr_count(strtolower($vcard['fullname']), strtolower($query)) > 0){
+				if(substr_count(strtolower($vcard['fullname']), strtolower($query)) > 0) {
 					$link = OCP\Util::linkTo('contacts', 'index.php').'&id='.urlencode($vcard['id']);
-					$results[]=new OC_Search_Result($vcard['fullname'],'', $link,(string)$l->t('Contact'));//$name,$text,$link,$type
+					$results[]=new OC_Search_Result($vcard['fullname'], '', $link, (string)$l->t('Contact'));//$name,$text,$link,$type
 				}
 			}
 		}
diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php
index e3b656056243a527a26d104dc810df175a118b23..ca171e792fce61c582d01d14d2d4ef85376a1ba2 100644
--- a/apps/contacts/lib/vcard.php
+++ b/apps/contacts/lib/vcard.php
@@ -63,9 +63,9 @@ class OC_Contacts_VCard{
 				$stmt = OCP\DB::prepare( $prep );
 				$result = $stmt->execute($id);
 			} catch(Exception $e) {
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', ids: '.join(',', $id),OCP\Util::DEBUG);
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.'SQL:'.$prep,OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', ids: '.join(',', $id), OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.'SQL:'.$prep, OCP\Util::DEBUG);
 				return false;
 			}
 		} elseif(is_int($id) || is_string($id)) {
@@ -74,12 +74,12 @@ class OC_Contacts_VCard{
 				$stmt = OCP\DB::prepare( $sql );
 				$result = $stmt->execute(array($id));
 			} catch(Exception $e) {
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', ids: '. $id,OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', ids: '. $id, OCP\Util::DEBUG);
 				return false;
 			}
 		} else {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.'. Addressbook id(s) argument is empty: '. $id,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.'. Addressbook id(s) argument is empty: '. $id, OCP\Util::DEBUG);
 			return false;
 		}
 		$cards = array();
@@ -102,8 +102,8 @@ class OC_Contacts_VCard{
 			$stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE id = ?' );
 			$result = $stmt->execute(array($id));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id: '. $id,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '. $id, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -121,8 +121,8 @@ class OC_Contacts_VCard{
 			$stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri = ?' );
 			$result = $stmt->execute(array($aid,$uri));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', aid: '.$aid.' uri'.$uri, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', aid: '.$aid.' uri'.$uri, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -177,16 +177,16 @@ class OC_Contacts_VCard{
 		try {
 			$result = $stmt->execute(array($aid,$uri));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', aid: '.$aid.' uid'.$uid, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', aid: '.$aid.' uid'.$uid, OCP\Util::DEBUG);
 			return false;
 		}
-		if($result->numRows() > 0){
+		if($result->numRows() > 0) {
 			while(true) {
-				$tmpuid = substr(md5(rand().time()),0,10);
+				$tmpuid = substr(md5(rand().time()), 0, 10);
 				$uri = $tmpuid.'.vcf';
-				$result = $stmt->execute(array($aid,$uri));
-				if($result->numRows() > 0){
+				$result = $stmt->execute(array($aid, $uri));
+				if($result->numRows() > 0) {
 					continue;
 				} else {
 					$uid = $tmpuid;
@@ -212,7 +212,7 @@ class OC_Contacts_VCard{
 		// Add version if needed
 		if($version && $version < '3.0') {
 			$upgrade = true;
-			OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Updating from version: '.$version,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Updating from version: '.$version, OCP\Util::DEBUG);
 		}
 		foreach($vcard->children as &$property){
 			// Decode string properties and remove obsolete properties.
@@ -225,29 +225,29 @@ class OC_Contacts_VCard{
 			}
 			// Fix format of type parameters.
 			if($upgrade && in_array($property->name, $typeprops)) {
-				OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. before: '.$property->serialize(),OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. before: '.$property->serialize(), OCP\Util::DEBUG);
 				self::formatPropertyTypes($property);
-				OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. after: '.$property->serialize(),OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. after: '.$property->serialize(), OCP\Util::DEBUG);
 			}
-			if($property->name == 'FN'){
+			if($property->name == 'FN') {
 				$fn = $property->value;
 			}
-			if($property->name == 'N'){
+			if($property->name == 'N') {
 				$n = $property->value;
 			}
-			if($property->name == 'UID'){
+			if($property->name == 'UID') {
 				$uid = $property->value;
 			}
-			if($property->name == 'ORG'){
+			if($property->name == 'ORG') {
 				$org = $property->value;
 			}
-			if($property->name == 'EMAIL' && is_null($email)){ // only use the first email as substitute for missing N or FN.
+			if($property->name == 'EMAIL' && is_null($email)) { // only use the first email as substitute for missing N or FN.
 				$email = $property->value;
 			}
 		}
 		// Check for missing 'N', 'FN' and 'UID' properties
 		if(!$fn) {
-			if($n && $n != ';;;;'){
+			if($n && $n != ';;;;') {
 				$fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
 			} elseif($email) {
 				$fn = $email;
@@ -257,21 +257,21 @@ class OC_Contacts_VCard{
 				$fn = 'Unknown Name';
 			}
 			$vcard->setString('FN', $fn);
-			OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'FN\' field: '.$fn,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Added missing \'FN\' field: '.$fn, OCP\Util::DEBUG);
 		}
-		if(!$n || $n == ';;;;'){ // Fix missing 'N' field. Ugly hack ahead ;-)
+		if(!$n || $n == ';;;;') { // Fix missing 'N' field. Ugly hack ahead ;-)
 			$slice = array_reverse(array_slice(explode(' ', $fn), 0, 2)); // Take 2 first name parts of 'FN' and reverse.
 			if(count($slice) < 2) { // If not enought, add one more...
 				$slice[] = "";
 			}
 			$n = implode(';', $slice).';;;';
 			$vcard->setString('N', $n);
-			OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n, OCP\Util::DEBUG);
 		}
 		if(!$uid) {
 			$vcard->setUID();
 			$uid = $vcard->getAsString('UID');
-			OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid,OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid, OCP\Util::DEBUG);
 		}
 		if(self::trueUID($aid, $uid)) {
 			$vcard->setString('UID', $uid);
@@ -288,8 +288,8 @@ class OC_Contacts_VCard{
 	 * @return insertid on success or false.
 	 */
 	public static function add($aid, OC_VObject $card, $uri=null, $isnew=false){
-		if(is_null($card)){
-			OCP\Util::writeLog('contacts','OC_Contacts_VCard::add. No vCard supplied', OCP\Util::ERROR);
+		if(is_null($card)) {
+			OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::add. No vCard supplied', OCP\Util::ERROR);
 			return null;
 		};
 
@@ -298,7 +298,7 @@ class OC_Contacts_VCard{
 			self::updateValuesFromAdd($aid, $card);
 		}
 
-		$card->setString('VERSION','3.0');
+		$card->setString('VERSION', '3.0');
 		// Add product ID is missing.
 		$prodid = trim($card->getAsString('PRODID'));
 		if(!$prodid) {
@@ -323,8 +323,8 @@ class OC_Contacts_VCard{
 		try {
 			$result = $stmt->execute(array($aid,$fn,$data,$uri,time()));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', aid: '.$aid.' uri'.$uri, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', aid: '.$aid.' uri'.$uri, OCP\Util::DEBUG);
 			return false;
 		}
 		$newid = OCP\DB::insertid('*PREFIX*contacts_cards');
@@ -355,15 +355,15 @@ class OC_Contacts_VCard{
 		$now = new DateTime;
 		foreach($objects as $object) {
 			$vcard = OC_VObject::parse($object[1]);
-			if(!is_null($vcard)){
+			if(!is_null($vcard)) {
 				$vcard->setString('REV', $now->format(DateTime::W3C));
 				$data = $vcard->serialize();
 				try {
 					$result = $stmt->execute(array($data,time(),$object[0]));
 					//OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0].': '.$object[1],OCP\Util::DEBUG);
 				} catch(Exception $e) {
-					OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-					OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id: '.$object[0],OCP\Util::DEBUG);
+					OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+					OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$object[0], OCP\Util::DEBUG);
 				}
 			}
 		}
@@ -397,8 +397,8 @@ class OC_Contacts_VCard{
 		try {
 			$result = $stmt->execute(array($fn,$data,time(),$id));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id'.$id, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id'.$id, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -414,11 +414,11 @@ class OC_Contacts_VCard{
 	 * @param string $data  vCard file
 	 * @return boolean
 	 */
-	public static function editFromDAVData($aid,$uri,$data){
-		$oldcard = self::findWhereDAVDataIs($aid,$uri);
+	public static function editFromDAVData($aid, $uri, $data){
+		$oldcard = self::findWhereDAVDataIs($aid, $uri);
 		$card = OC_VObject::parse($data);
 		if(!$card) {
-			OCP\Util::writeLog('contacts','OC_Contacts_VCard::editFromDAVData. Unable to parse VCARD, uri: '.$uri,OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', Unable to parse VCARD, uri: '.$uri, OCP\Util::ERROR);
 			return false;
 		}
 		return self::edit($oldcard['id'], $card);
@@ -430,14 +430,13 @@ class OC_Contacts_VCard{
 	 * @return boolean
 	 */
 	public static function delete($id){
-		// FIXME: Add error checking.
 		OC_Hook::emit('OC_Contacts_VCard', 'pre_deleteVCard', array('aid' => null, 'id' => $id, 'uri' => null));
 		$stmt = OCP\DB::prepare( 'DELETE FROM *PREFIX*contacts_cards WHERE id = ?' );
 		try {
 			$stmt->execute(array($id));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', id: '.$id, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id, OCP\Util::DEBUG);
 			return false;
 		}
 
@@ -451,14 +450,13 @@ class OC_Contacts_VCard{
 	 * @return boolean
 	 */
 	public static function deleteFromDAVData($aid,$uri){
-		// FIXME: Add error checking. Deleting a card gives an Kontact/Akonadi error.
 		OC_Hook::emit('OC_Contacts_VCard', 'pre_deleteVCard', array('aid' => $aid, 'id' => null, 'uri' => $uri));
 		$stmt = OCP\DB::prepare( 'DELETE FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri=?' );
 		try {
 			$stmt->execute(array($aid,$uri));
 		} catch(Exception $e) {
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
-			OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', aid: '.$aid.' uri: '.$uri, OCP\Util::DEBUG);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', aid: '.$aid.' uri: '.$uri, OCP\Util::DEBUG);
 			return false;
 		}
 		OC_Contacts_Addressbook::touch($aid);
@@ -487,14 +485,14 @@ class OC_Contacts_VCard{
 	 * @return array
 	 */
 	public static function unescapeDelimiters($value, $delimiter=';') {
-		$array = explode($delimiter,$value);
+		$array = explode($delimiter, $value);
 		for($i=0;$i<count($array);$i++) {
-			if(substr($array[$i],-1,1)=="\\") {
+			if(substr($array[$i], -1, 1)=="\\") {
 				if(isset($array[$i+1])) {
-					$array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter.$array[$i+1];
+					$array[$i] = substr($array[$i], 0, count($array[$i])-2).$delimiter.$array[$i+1];
 					unset($array[$i+1]);
 				} else {
-					$array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter;
+					$array[$i] = substr($array[$i], 0, count($array[$i])-2).$delimiter;
 				}
 				$i = $i - 1;
 			}
@@ -510,12 +508,12 @@ class OC_Contacts_VCard{
 	 *
 	 * look at code ...
 	 */
-	public static function structureContact($object){
+	public static function structureContact($object) {
 		$details = array();
 		foreach($object->children as $property){
 			$temp = self::structureProperty($property);
 			if(!is_null($temp)) {
-				if(array_key_exists($property->name,$details)){
+				if(array_key_exists($property->name, $details)) {
 					$details[$property->name][] = $temp;
 				}
 				else{
@@ -539,10 +537,10 @@ class OC_Contacts_VCard{
 	 * NOTE: $value is not escaped anymore. It shouldn't make any difference
 	 * but we should look out for any problems.
 	 */
-	public static function structureProperty($property){
+	public static function structureProperty($property) {
 		$value = $property->value;
 		//$value = htmlspecialchars($value);
-		if($property->name == 'ADR' || $property->name == 'N'){
+		if($property->name == 'ADR' || $property->name == 'N') {
 			$value = self::unescapeDelimiters($value);
 		} elseif($property->name == 'BDAY') {
 			if(strpos($value, '-') === false) {
@@ -562,17 +560,17 @@ class OC_Contacts_VCard{
 			// Faulty entries by kaddressbook
 			// Actually TYPE=PREF is correct according to RFC 2426
 			// but this way is more handy in the UI. Tanghus.
-			if($parameter->name == 'TYPE' && $parameter->value == 'PREF'){
+			if($parameter->name == 'TYPE' && $parameter->value == 'PREF') {
 				$parameter->name = 'PREF';
 				$parameter->value = '1';
 			}
 			// NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters
 			// like TYPE=HOME,CELL,VOICE. Tanghus.
-			if (in_array($property->name, array('TEL', 'EMAIL')) && $parameter->name == 'TYPE'){
-				if (isset($temp['parameters'][$parameter->name])){
+			if (in_array($property->name, array('TEL', 'EMAIL')) && $parameter->name == 'TYPE') {
+				if (isset($temp['parameters'][$parameter->name])) {
 					$temp['parameters'][$parameter->name][] = $parameter->value;
 				}
-				else{
+				else {
 					$temp['parameters'][$parameter->name] = array($parameter->value);
 				}
 			}
@@ -590,7 +588,7 @@ class OC_Contacts_VCard{
 	 * @return boolean
 	 *
 	 */
-	public static function moveToAddressBook($aid, $id){
+	public static function moveToAddressBook($aid, $id) {
 		OC_Contacts_App::getAddressbook($aid); // check for user ownership.
 		if(is_array($id)) {
 			$id_sql = join(',', array_fill(0, count($id), '?'));
@@ -601,9 +599,9 @@ class OC_Contacts_VCard{
 				$vals = array_merge((array)$aid, $id);
 				$result = $stmt->execute($vals);
 			} catch(Exception $e) {
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::ERROR);
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', ids: '.join(',', $vals),OCP\Util::DEBUG);
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', SQL:'.$prep,OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', ids: '.join(',', $vals), OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', SQL:'.$prep, OCP\Util::DEBUG);
 				return false;
 			}
 		} else {
@@ -611,8 +609,8 @@ class OC_Contacts_VCard{
 				$stmt = OCP\DB::prepare( 'UPDATE *PREFIX*contacts_cards SET addressbookid = ? WHERE id = ?' );
 				$result = $stmt->execute(array($aid, $id));
 			} catch(Exception $e) {
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(),OCP\Util::DEBUG);
-				OCP\Util::writeLog('contacts',__CLASS__.'::'.__METHOD__.' id: '.$id,OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::DEBUG);
+				OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' id: '.$id, OCP\Util::DEBUG);
 				return false;
 			}
 		}
diff --git a/apps/contacts/photo.php b/apps/contacts/photo.php
index 4660d61f6184dde98c1979c6fde1b4255bbf9059..efdf157cd955280c153e8bf1bba382f2ab962b77 100644
--- a/apps/contacts/photo.php
+++ b/apps/contacts/photo.php
@@ -13,7 +13,7 @@
 OCP\User::checkLoggedIn();
 OCP\App::checkAppEnabled('contacts');
 
-function getStandardImage(){
+function getStandardImage() {
 	//OCP\Response::setExpiresHeader('P10D');
 	OCP\Response::enableCaching();
 	OCP\Response::redirect(OCP\Util::imagePath('contacts', 'person_large.png'));
@@ -27,37 +27,39 @@ if(is_null($id)) {
 }
 
 if(!extension_loaded('gd') || !function_exists('gd_info')) {
-	OCP\Util::writeLog('contacts','photo.php. GD module not installed',OCP\Util::DEBUG);
+	OCP\Util::writeLog('contacts', 
+		'photo.php. GD module not installed', OCP\Util::DEBUG);
 	getStandardImage();
 }
 
 $contact = OC_Contacts_App::getContactVCard($id);
 $image = new OC_Image();
-if(!$image) {
+if (!$image) {
 	getStandardImage();
 }
 // invalid vcard
-if( is_null($contact)) {
-	OCP\Util::writeLog('contacts','photo.php. The VCard for ID '.$id.' is not RFC compatible',OCP\Util::ERROR);
+if (is_null($contact)) {
+	OCP\Util::writeLog('contacts', 
+		'photo.php. The VCard for ID ' . $id . ' is not RFC compatible', 
+		OCP\Util::ERROR);
 } else {
 	OCP\Response::enableCaching($caching);
 	OC_Contacts_App::setLastModifiedHeader($contact);
 
 	// Photo :-)
-	if($image->loadFromBase64($contact->getAsString('PHOTO'))) {
+	if ($image->loadFromBase64($contact->getAsString('PHOTO'))) {
 		// OK
 		OCP\Response::setETagHeader(md5($contact->getAsString('PHOTO')));
 	}
 	else
 	// Logo :-/
-	if($image->loadFromBase64($contact->getAsString('LOGO'))) {
+	if ($image->loadFromBase64($contact->getAsString('LOGO'))) {
 		// OK
 		OCP\Response::setETagHeader(md5($contact->getAsString('LOGO')));
 	}
 	if ($image->valid()) {
 		$max_size = 200;
-		if($image->width() > $max_size ||
-		   $image->height() > $max_size) {
+		if ($image->width() > $max_size || $image->height() > $max_size) {
 			$image->resize($max_size);
 		}
 	}
@@ -65,8 +67,7 @@ if( is_null($contact)) {
 if (!$image->valid()) {
 	// Not found :-(
 	getStandardImage();
-	//$image->loadFromFile('img/person_large.png');
 }
 header('Content-Type: '.$image->mimeType());
 $image->show();
-//echo OC_Contacts_App::$l10n->t('This card does not contain a photo.');
+
diff --git a/apps/contacts/settings.php b/apps/contacts/settings.php
index c88fed0b4d6c1882bd81cc2c88fedf77c3315907..a079499381bd70ab5db25e0532bec92de4b0a0fa 100644
--- a/apps/contacts/settings.php
+++ b/apps/contacts/settings.php
@@ -1,6 +1,6 @@
 <?php
 
 $tmpl = new OCP\Template( 'contacts', 'settings');
+$tmpl->assign('addressbooks', OC_Contacts_Addressbook::all(OCP\USER::getUser()), false);
 
 return $tmpl->fetchPage();
-?>
diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php
index a8fb6c57c2cfc5d8d3454b98e0bcbedcda4aa4f0..b2dde12684c8bc1c04a07d8d0db1ff0cb62d3dd3 100644
--- a/apps/contacts/templates/index.php
+++ b/apps/contacts/templates/index.php
@@ -1,3 +1,4 @@
+<div id='notification'></div>
 <script type='text/javascript'>
 	var totalurl = '<?php echo OCP\Util::linkToRemote('carddav'); ?>addressbooks';
 	var categories = <?php echo json_encode($_['categories']); ?>;
@@ -5,25 +6,69 @@
 	var lang = '<?php echo OCP\Config::getUserValue(OCP\USER::getUser(), 'core', 'lang', 'en'); ?>';
 </script>
 <div id="leftcontent">
+	<div class="hidden" id="statusbar"></div>
 	<div id="contacts">
 	</div>
+	<div id="uploadprogressbar"></div>
 	<div id="bottomcontrols">
-		<form>
-			<button class="svg" id="contacts_newcontact" title="<?php echo $l->t('Add Contact'); ?>"><img class="svg" src="<?php echo OCP\Util::imagePath('contacts', 'contact-new.svg'); ?>" alt="<?php echo $l->t('Add Contact'); ?>"   /></button>
+		<form id="import_upload_form" action="<?php echo OCP\Util::linkTo('contacts', 'ajax/uploadimport.php'); ?>" method="post" enctype="multipart/form-data" target="import_upload_target">
+			<button class="svg" id="contacts_newcontact" title="<?php echo $l->t('Add Contact'); ?>"><img class="svg" src="<?php echo OCP\Util::imagePath('contacts', 'contact-new.svg'); ?>" alt="<?php echo $l->t('Add Contact'); ?>" /></button>
+			<span class="svg" id="contacts_import" title="<?php echo $l->t('Import'); ?>">
+				<input class="float" id="import_upload_start" type="file" accept="text/directory,text/vcard,text/x-vcard" name="importfile" />
+				<img class="svg" src="core/img/actions/upload.svg" alt="<?php echo $l->t('Import'); ?>" />
+			</span>
 			<button class="svg" id="chooseaddressbook" title="<?php echo $l->t('Addressbooks'); ?>"><img class="svg" src="core/img/actions/settings.svg" alt="<?php echo $l->t('Addressbooks'); ?>" /></button>
+			<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload">
 		</form>
+		<iframe name="import_upload_target" id='import_upload_target' src=""></iframe>
 	</div>
 </div>
 <div id="rightcontent" class="rightcontent" data-id="<?php echo $_['id']; ?>">
 	<?php
-		if ($_['id']){
+		if($_['has_contacts']) {
 			echo $this->inc('part.contact');
 		}
 		else{
 			echo $this->inc('part.no_contacts');
 		}
 	?>
+	<div class="hidden" id="ninjahelp">
+		<a class="close" tabindex="0" role="button">
+			<img class="svg" src="core/img/actions/delete.svg" alt="<?php echo $l->t('Close'); ?>" />
+		</a>
+		<h2><?php echo $l->t('Keyboard shortcuts'); ?></h2>
+		<div class="help-section">
+			<h3><?php echo $l->t('Navigation'); ?></h3>
+			<dl>
+				<dt>j/Down/Space</dt>
+				<dd><?php echo $l->t('Next contact in list'); ?></dd>
+				<dt>k/Up/Shift-Space</dt>
+				<dd><?php echo $l->t('Previous contact in list'); ?></dd>
+				<dt>o/Enter</dt>
+				<dd><?php echo $l->t('Expand/collapse current addressbook'); ?></dd>
+				<dt>n/p</dt>
+				<dd><?php echo $l->t('Next/previous addressbook'); ?></dd>
+			</dl>
+		</div>
+		<div class="help-section">
+			<h3><?php echo $l->t('Actions'); ?></h3>
+			<dl>
+				<dt>r</dt>
+				<dd><?php echo $l->t('Refresh contacts list'); ?></dd>
+				<dt>a</dt>
+				<dd><?php echo $l->t('Add new contact'); ?></dd>
+				<dt>Shift-a</dt>
+				<dd><?php echo $l->t('Add new addressbook'); ?></dd>
+				<dt>Shift-Delete</dt>
+				<dd><?php echo $l->t('Delete current contact'); ?></dd>
+			</dl>
+		</div>
+	</div>
 </div>
 <!-- Dialogs -->
 <div id="dialog_holder"></div>
 <!-- End of Dialogs -->
+<menu type="context" id="addressbookmenu">
+	<menuitem label="Delete" icon="core/img/actions/delete.svg" onclick="alert('Really? ' + $(this).attr('data-id'))"></menuitem>
+	<menuitem label="Rename" icon="core/img/actions/rename.svg" onclick="alert('Can\'t do that')"></menuitem>
+</menu>
diff --git a/apps/contacts/templates/part.chooseaddressbook.php b/apps/contacts/templates/part.chooseaddressbook.php
index a0ec053ab9169a3046214cd407c5d7ae71ec32f5..caed67736c58923470ea27b90190b8d64f41da2b 100644
--- a/apps/contacts/templates/part.chooseaddressbook.php
+++ b/apps/contacts/templates/part.chooseaddressbook.php
@@ -14,7 +14,6 @@ for($i = 0; $i < count($option_addressbooks); $i++){
 <tr>
 	<td colspan="5" style="padding: 0.5em;">
 		<a class="button" href="#" onclick="Contacts.UI.Addressbooks.newAddressbook(this);"><?php echo $l->t('New Address Book') ?></a>
-		<a class="button" href="#" onclick="Contacts.UI.Addressbooks.importAddressbook(this);"><?php echo $l->t('Import from VCF') ?></a>
 	</td>
 </tr>
 <tr>
diff --git a/apps/contacts/templates/part.cropphoto.php b/apps/contacts/templates/part.cropphoto.php
index 6d7b1e4477715fe3fe1b57192f42f9dba36c3c7f..3f5817622b2c9c891918906e8916c6a5b17e2b44 100644
--- a/apps/contacts/templates/part.cropphoto.php
+++ b/apps/contacts/templates/part.cropphoto.php
@@ -2,7 +2,6 @@
 $id = $_['id'];
 $tmpkey = $_['tmpkey'];
 $requesttoken = $_['requesttoken'];
-OCP\Util::writeLog('contacts','templates/part.cropphoto.php: tmpkey: '.$tmpkey, OCP\Util::DEBUG);
 ?>
 <script type="text/javascript">
 	jQuery(function($) {
diff --git a/apps/contacts/templates/part.edit_categories_dialog.php b/apps/contacts/templates/part.edit_categories_dialog.php
deleted file mode 100644
index 8997fa586bd9fc0a4ea6edd8a77652e1c75c5a10..0000000000000000000000000000000000000000
--- a/apps/contacts/templates/part.edit_categories_dialog.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-$categories = isset($_['categories'])?$_['categories']:array();
-?>
-<div id="edit_categories_dialog" title="<?php echo $l->t('Edit categories'); ?>">
-<!-- ?php print_r($types); ? -->
-	<form method="post" id="categoryform">
-	<div class="scrollarea">
-	<ul id="categorylist">
-	<?php foreach($categories as $category) { ?>
-	<li><input type="checkbox" name="categories[]" value="<?php echo $category; ?>" /><?php echo $category; ?></li>
-	<?php } ?>
-	</ul>
-	</div>
-	<div class="bottombuttons"><input type="text" id="category_addinput" name="category" /><button id="category_addbutton" disabled="disabled"><?php echo $l->t('Add'); ?></button></div>
-	</form>
-</div>
diff --git a/apps/contacts/templates/part.importaddressbook.php b/apps/contacts/templates/part.importaddressbook.php
deleted file mode 100644
index 01f8dd77d0aecaac01fcbc8e2624bfe0fb12217a..0000000000000000000000000000000000000000
--- a/apps/contacts/templates/part.importaddressbook.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-?>
-<td id="importaddressbook_dialog" colspan="6">
-<table>
-<tr>
-	<th><?php echo $l->t('Select address book to import to:') ?></th>
-	<td>
-		<form id="import_upload_form" action="<?php echo OCP\Util::linkTo('contacts', 'ajax/uploadimport.php'); ?>" method="post" enctype="multipart/form-data" target="import_upload_target">
-			<select id="book" name="book" class="float">
-			<?php
-			$contacts_options = OC_Contacts_Addressbook::all(OCP\USER::getUser());
-			echo OCP\html_select_options($contacts_options, $contacts_options[0]['id'], array('value'=>'id', 'label'=>'displayname'));
-			?>
-			</select>
-			<span id="import_drop_target" class="droptarget float"><?php echo $l->t("Drop a VCF file<br />to import contacts."); ?> (Max. <?php echo  $_['uploadMaxHumanFilesize']; ?>)</span>
-			<a class="svg upload float" title="<?php echo $l->t('Select from HD'); ?>">
-			<input class="float" id="import_upload_start" type="file" accept="text/*" name="importfile" /></a>
-			<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload">
-		</form>
-	</td>
-</tr>
-</table>
-
-<input id="close_button" style="float: left;" type="button" onclick="Contacts.UI.Addressbooks.cancel(this);" value="<?php echo $l->t("Cancel"); ?>">
-<iframe name="import_upload_target" id='import_upload_target' src=""></iframe>
-</td>
-<script type="text/javascript">
-$(document).ready(function(){
-	Contacts.UI.Addressbooks.loadImportHandlers();
-});
-</script>
\ No newline at end of file
diff --git a/apps/contacts/templates/part.selectaddressbook.php b/apps/contacts/templates/part.selectaddressbook.php
new file mode 100644
index 0000000000000000000000000000000000000000..c54ddaf2e67e33cffa2d94e3837aeee8331ce3a6
--- /dev/null
+++ b/apps/contacts/templates/part.selectaddressbook.php
@@ -0,0 +1,27 @@
+<div id="selectaddressbook_dialog" title="<?php echo $l->t("Select Address Books"); ?>">
+<form>
+<table style="width: 100%">
+	<?php foreach($_['addressbooks'] as $idx => $addressbook) { ?>
+	<tr>
+		<td>
+			<input id="book_<?php echo $addressbook['id']; ?>" name="book" type="radio" value="<?php echo $addressbook['id']; ?>" <?php echo ($idx==0?'checked="checked"':'')?>>
+		</td>
+		<td>
+			<label for="book_<?php echo $addressbook['id']; ?>"><?php echo $addressbook['displayname']; ?></label>
+		</td>
+		<td><?php echo $addressbook['description']; ?></td>
+	</tr>
+	<?php } ?>
+	<tr>
+		<td>
+			<input id="book_new" name="book" type="radio" value="new">
+		</td>
+		<th>
+			<input type="text" class="name" name="displayname" placeholder="<?php echo $l->t("Enter name"); ?>" />
+		</th>
+		<td><input type="text" class="desc" name="description" placeholder="<?php echo $l->t("Enter description"); ?>" /></td>
+	</tr>
+</table>
+</form>
+</div>
+
diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php
index 216003b6c698938437807adb6dbb54317f88a5fb..f520559d1432da97056134103709c0d01e565bfa 100644
--- a/apps/contacts/templates/settings.php
+++ b/apps/contacts/templates/settings.php
@@ -7,6 +7,12 @@
 		<dd><code><?php echo OCP\Util::linkToRemote('carddav'); ?></code></dd>
 		<dt><?php echo $l->t('iOS/OS X'); ?></dt>
 		<dd><code><?php echo OCP\Util::linkToRemote('carddav'); ?>principals/<?php echo OCP\USER::getUser(); ?></code>/</dd>
+		<dt><?php echo $l->t('Read only vCard directory link(s)'); ?></dt>
+		<dd>
+			<?php foreach($_['addressbooks'] as $addressbook) { ?>
+			<a href="<?php echo OCP\Util::linkToRemote('carddav').'addressbooks/'.OCP\USER::getUser().'/'.rawurlencode($addressbook['uri']) ?>?export"><?php echo $addressbook['displayname'] ?></a><br />
+			<?php } ?>
+		</dd>
 		</dl>
 		Powered by <a href="http://geonames.org/" target="_blank">geonames.org webservice</a>
 	</fieldset>
diff --git a/apps/contacts/thumbnail.php b/apps/contacts/thumbnail.php
index a69e9c74f72d1551a5bb4a24a84d7886e33f7285..6deb5ca379ee8fe033f7fdc1a8173df9c31f7edf 100644
--- a/apps/contacts/thumbnail.php
+++ b/apps/contacts/thumbnail.php
@@ -25,14 +25,15 @@ OCP\JSON::checkLoggedIn();
 OCP\App::checkAppEnabled('contacts');
 session_write_close();
 
-function getStandardImage(){
+function getStandardImage() {
 	//OCP\Response::setExpiresHeader('P10D');
 	OCP\Response::enableCaching();
 	OCP\Response::redirect(OCP\Util::imagePath('contacts', 'person.png'));
 }
 
 if(!extension_loaded('gd') || !function_exists('gd_info')) {
-	OCP\Util::writeLog('contacts','thumbnail.php. GD module not installed',OCP\Util::DEBUG);
+	OCP\Util::writeLog('contacts', 
+		'thumbnail.php. GD module not installed', OCP\Util::DEBUG);
 	getStandardImage();
 	exit();
 }
@@ -43,8 +44,10 @@ $caching = isset($_GET['refresh']) ? 0 : null;
 $contact = OC_Contacts_App::getContactVCard($id);
 
 // invalid vcard
-if(is_null($contact)){
-	OCP\Util::writeLog('contacts','thumbnail.php. The VCard for ID '.$id.' is not RFC compatible',OCP\Util::ERROR);
+if(is_null($contact)) {
+	OCP\Util::writeLog('contacts', 
+		'thumbnail.php. The VCard for ID ' . $id . ' is not RFC compatible', 
+		OCP\Util::ERROR);
 	getStandardImage();
 	exit();
 }
@@ -64,16 +67,24 @@ if($photo) {
 				if($image->show()) {
 					exit();
 				} else {
-					OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t display thumbnail for ID '.$id,OCP\Util::ERROR);
+					OCP\Util::writeLog('contacts', 
+						'thumbnail.php. Couldn\'t display thumbnail for ID ' . $id, 
+						OCP\Util::ERROR);
 				}
 			} else {
-				OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t resize thumbnail for ID '.$id,OCP\Util::ERROR);
+				OCP\Util::writeLog('contacts', 
+					'thumbnail.php. Couldn\'t resize thumbnail for ID ' . $id, 
+					OCP\Util::ERROR);
 			}
 		}else{
-			OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t crop thumbnail for ID '.$id,OCP\Util::ERROR);
+			OCP\Util::writeLog('contacts', 
+				'thumbnail.php. Couldn\'t crop thumbnail for ID ' . $id, 
+				OCP\Util::ERROR);
 		}
 	} else {
-		OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t load image string for ID '.$id,OCP\Util::ERROR);
+		OCP\Util::writeLog('contacts', 
+			'thumbnail.php. Couldn\'t load image string for ID ' . $id, 
+			OCP\Util::ERROR);
 	}
 }
 getStandardImage();
diff --git a/apps/contacts/tmpphoto.php b/apps/contacts/tmpphoto.php
index 5fde8de9977ffad8d7ccb7a90dd1dfaab5e591b9..156d5c80308925399362fca1cca4dbbd017cc8a8 100644
--- a/apps/contacts/tmpphoto.php
+++ b/apps/contacts/tmpphoto.php
@@ -24,7 +24,7 @@ $tmpkey = $_GET['tmpkey'];
 $maxsize = isset($_GET['maxsize']) ? $_GET['maxsize'] : -1;
 header("Cache-Control: no-cache, no-store, must-revalidate");
 
-OCP\Util::writeLog('contacts','tmpphoto.php: tmpkey: '.$tmpkey, OCP\Util::DEBUG);
+OCP\Util::writeLog('contacts', 'tmpphoto.php: tmpkey: '.$tmpkey, OCP\Util::DEBUG);
 
 $image = new OC_Image();
 $image->loadFromData(OC_Cache::get($tmpkey));
diff --git a/apps/external/ajax/setsites.php b/apps/external/ajax/setsites.php
index 772863974ae4bcf708a5f04961abbf88a74974bc..0f9e061d0e0539d1a81ce55083af985997f6233a 100644
--- a/apps/external/ajax/setsites.php
+++ b/apps/external/ajax/setsites.php
@@ -8,6 +8,7 @@
 
  
 OCP\User::checkAdminUser();
+OCP\JSON::callCheck();
 
 $sites = array();
 for ($i = 0; $i < sizeof($_POST['site_name']); $i++) {
@@ -22,4 +23,3 @@ else
 	OCP\Config::setAppValue('external', 'sites', json_encode($sites));
 
 echo 'true';
-?>
diff --git a/apps/external/index.php b/apps/external/index.php
index 81819e76e2f8f78196b8c2292fa9fdee3ace72bd..3b6f06e1bff023df73ade667f1577e4563a097c2 100644
--- a/apps/external/index.php
+++ b/apps/external/index.php
@@ -40,4 +40,3 @@ if (isset($_GET['id'])) {
 		$tmpl->printPage();
 	}
 }
-?>
diff --git a/apps/external/lib/external.php b/apps/external/lib/external.php
index 9fff5d5569a37b0a1bb92d5ad4865fe386cfbc99..d13a7cf6dd73b133c64720a06cc0ec526825d475 100644
--- a/apps/external/lib/external.php
+++ b/apps/external/lib/external.php
@@ -32,5 +32,3 @@ class OC_External {
 	}
 
 }
-
-?>
diff --git a/apps/external/settings.php b/apps/external/settings.php
index c109733e54824b76494d47eea7b8d3472a97a45e..488444c119a99ac77aab8d4fe63b56f91f7b59af 100644
--- a/apps/external/settings.php
+++ b/apps/external/settings.php
@@ -7,4 +7,3 @@ OCP\Util::addscript( "external", "admin" );
 $tmpl = new OCP\Template( 'external', 'settings');
 
 return $tmpl->fetchPage();
-?>
diff --git a/apps/files/ajax/autocomplete.php b/apps/files/ajax/autocomplete.php
index 7ff34da96b3a9421b60fbdf1ba204bf450afc84c..e504bb24bf8b8ef6f06413fc7a00c7e9914169e6 100644
--- a/apps/files/ajax/autocomplete.php
+++ b/apps/files/ajax/autocomplete.php
@@ -52,5 +52,3 @@ if(OC_Filesystem::file_exists($base) and OC_Filesystem::is_dir($base)){
 	}
 }
 OCP\JSON::encodedPrint($files);
-
-?>
diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php
index ed155de0dc76cf5e13e17701767ffd2615a393e0..695f803884eccee8d5bd5942702e92aa933c1541 100644
--- a/apps/files/ajax/delete.php
+++ b/apps/files/ajax/delete.php
@@ -4,6 +4,7 @@
 
 
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 // Get data
 $dir = stripslashes($_GET["dir"]);
@@ -25,5 +26,3 @@ if($success) {
 } else {
 	OCP\JSON::error(array("data" => array( "message" => "Could not delete:\n" . $filesWithError )));
 }
-
-?>
diff --git a/apps/files/ajax/download.php b/apps/files/ajax/download.php
index e9373f5f6ac253e54085dc59cb20775324cbd928..b9a4ddaf5e7df297d3ad4c5666e35eec4f344a2b 100644
--- a/apps/files/ajax/download.php
+++ b/apps/files/ajax/download.php
@@ -34,4 +34,3 @@ $files = $_GET["files"];
 $dir = $_GET["dir"];
 
 OC_Files::get($dir, $files, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
-?>
diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php
index f0600a179d00791f52acacc372446e7ee8451ac1..dae0c1a828dd8bacec93180133e46d19c866f6f7 100644
--- a/apps/files/ajax/list.php
+++ b/apps/files/ajax/list.php
@@ -42,5 +42,3 @@ $list->assign( "files", $files, false );
 $data = array('files' => $list->fetchPage());
 
 OCP\JSON::success(array('data' => $data));
-
-?>
diff --git a/apps/files/ajax/mimeicon.php b/apps/files/ajax/mimeicon.php
index 57898cd82d9c09f68289284c6a8e2a19c3573244..80d50f84528633c38ba13e6ec3fd3b6c74af715f 100644
--- a/apps/files/ajax/mimeicon.php
+++ b/apps/files/ajax/mimeicon.php
@@ -7,5 +7,3 @@ $RUNTIME_NOAPPS=false;
 
 
 print OC_Helper::mimetypeIcon($_GET['mime']);
-
-?>
diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php
index 945fe4e7b82a55ad4080bc801e195328a68408b3..3d4003a8edc456e211d67b1dcb0dbbe27784c4b2 100644
--- a/apps/files/ajax/move.php
+++ b/apps/files/ajax/move.php
@@ -4,6 +4,7 @@
 
 
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 // Get data
 $dir = stripslashes($_GET["dir"]);
@@ -16,5 +17,3 @@ if(OC_Files::move($dir,$file,$target,$file)){
 }else{
 	OCP\JSON::error(array("data" => array( "message" => "Could not move $file" )));
 }
-
-?>
diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php
index edb784148724e64ec0c877617f4479fc99477744..cc9208ad08fcd439a574c5922421917e4cedcd47 100644
--- a/apps/files/ajax/newfile.php
+++ b/apps/files/ajax/newfile.php
@@ -1,15 +1,25 @@
 <?php
 
 // Init owncloud
+global $eventSource;
 
+if(!OC_User::isLoggedIn()){
+	exit;
+}
 
-OCP\JSON::checkLoggedIn();
+session_write_close();
 
 // Get the params
-$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : '';
-$filename = isset( $_POST['filename'] ) ? stripslashes($_POST['filename']) : '';
-$content = isset( $_POST['content'] ) ? $_POST['content'] : '';
-$source = isset( $_POST['source'] ) ? stripslashes($_POST['source']) : '';
+$dir = isset( $_REQUEST['dir'] ) ? stripslashes($_REQUEST['dir']) : '';
+$filename = isset( $_REQUEST['filename'] ) ? stripslashes($_REQUEST['filename']) : '';
+$content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : '';
+$source = isset( $_REQUEST['source'] ) ? stripslashes($_REQUEST['source']) : '';
+
+if($source){
+	$eventSource=new OC_EventSource();
+}else{
+	OC_JSON::callCheck();
+}
 
 if($filename == '') {
 	OCP\JSON::error(array("data" => array( "message" => "Empty Filename" )));
@@ -20,22 +30,49 @@ if(strpos($filename,'/')!==false){
 	exit();
 }
 
+function progress($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max){
+	static $filesize = 0;
+	static $lastsize = 0;
+	global $eventSource;
+
+	switch($notification_code) {
+		case STREAM_NOTIFY_FILE_SIZE_IS:
+			$filesize = $bytes_max;
+			break;
+			
+		case STREAM_NOTIFY_PROGRESS:
+			if ($bytes_transferred > 0) {
+				if (!isset($filesize)) {
+				} else {
+					$progress = (int)(($bytes_transferred/$filesize)*100);
+					if($progress>$lastsize){//limit the number or messages send
+						$eventSource->send('progress',$progress);
+					}
+					$lastsize=$progress;
+				}
+			}
+			break;
+	}
+}
+
 if($source){
 	if(substr($source,0,8)!='https://' and substr($source,0,7)!='http://'){
 		OCP\JSON::error(array("data" => array( "message" => "Not a valid source" )));
 		exit();
 	}
-	$sourceStream=fopen($source,'rb');
+
+	$ctx = stream_context_create(null, array('notification' =>'progress'));
+	$sourceStream=fopen($source,'rb', false, $ctx);
 	$target=$dir.'/'.$filename;
 	$result=OC_Filesystem::file_put_contents($target,$sourceStream);
 	if($result){
 		$mime=OC_Filesystem::getMimetype($target);
-		OCP\JSON::success(array("data" => array('mime'=>$mime)));
-		exit();
+		$eventSource->send('success',$mime);
 	}else{
-		OCP\JSON::error(array("data" => array( "message" => "Error while downloading ".$source. ' to '.$target )));
-		exit();
+		$eventSource->send('error',"Error while downloading ".$source. ' to '.$target);
 	}
+	$eventSource->close();
+	exit();
 }else{
 	if($content){
 		if(OC_Filesystem::file_put_contents($dir.'/'.$filename,$content)){
diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php
index c5c37914c6a68c005e6c1df2b3795f397671eca6..ae92bcf09bb8f5f018a496593f361834ecc5a05b 100644
--- a/apps/files/ajax/newfolder.php
+++ b/apps/files/ajax/newfolder.php
@@ -4,6 +4,7 @@
 
 
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 // Get the params
 $dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : '';
diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php
index 7cb02f796731cf01d01110f6f02d854ceac0c450..d159f6e152f2bc3eddd12ca9ccb93c688adb2f21 100644
--- a/apps/files/ajax/rawlist.php
+++ b/apps/files/ajax/rawlist.php
@@ -22,5 +22,3 @@ foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ){
 }
 
 OCP\JSON::success(array('data' => $files));
-
-?>
diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php
index e2fa3d54a61aad43feab0b0054e9c5125f6e705b..45448279fa161d46137c2bddee952060eaa6a584 100644
--- a/apps/files/ajax/rename.php
+++ b/apps/files/ajax/rename.php
@@ -4,6 +4,7 @@
 
 
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 // Get data
 $dir = stripslashes($_GET["dir"]);
@@ -17,5 +18,3 @@ if( OC_Files::move( $dir, $file, $dir, $newname )) {
 else{
 	OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" )));
 }
-
-?>
diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php
index 6fcf97688c2ddf6950b76cf6453cc939c2e417b3..eef38858516b7e7bb2d27751ed487b717c6f76a7 100644
--- a/apps/files/ajax/scan.php
+++ b/apps/files/ajax/scan.php
@@ -16,6 +16,11 @@ session_write_close();
 if($force or !OC_FileCache::inCache('')){
 	if(!$checkOnly){
 		OCP\DB::beginTransaction();
+		
+		if(OC_Cache::isFast()){
+			OC_Cache::clear('fileid/'); //make sure the old fileid's don't mess things up
+		}
+		
 		OC_FileCache::scan($dir,$eventSource);
 		OC_FileCache::clean();
 		OCP\DB::commit();
diff --git a/apps/files/ajax/timezone.php b/apps/files/ajax/timezone.php
index cafb5074eceedc71c537c5b03b2060feb32268d3..0be441a36a237d16184a21f3b48ee50e2c5f3297 100644
--- a/apps/files/ajax/timezone.php
+++ b/apps/files/ajax/timezone.php
@@ -3,4 +3,4 @@
 	// see lib/base.php for an example
 	//session_start();
 	$_SESSION['timezone'] = $_GET['time'];
-?>
+        
\ No newline at end of file
diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php
index b779924cfb4f92fdc33f6600a61159e7adf2bee7..74e6eb560d8df0af438511cc279079262ab1842f 100644
--- a/apps/files/ajax/upload.php
+++ b/apps/files/ajax/upload.php
@@ -7,6 +7,7 @@
 OCP\JSON::setContentTypeHeader('text/plain');
 
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 if (!isset($_FILES['files'])) {
 	OCP\JSON::error(array("data" => array( "message" => "No file was uploaded. Unknown error" )));
@@ -59,5 +60,3 @@ if(strpos($dir,'..') === false){
 }
 
 OCP\JSON::error(array('data' => array('error' => $error, "file" => $fileName)));
-
-?>
diff --git a/apps/files/appinfo/update.php b/apps/files/appinfo/update.php
index f9953ba4de5e4efbc6d57532d25d37ea2ab6bdba..5514aed197f411fd92e6d9282a78e14818c22879 100644
--- a/apps/files/appinfo/update.php
+++ b/apps/files/appinfo/update.php
@@ -1,5 +1,16 @@
 <?php
 
+// fix webdav properties, remove namespace information between curly bracket (update from OC4 to OC5)
+$installedVersion=OCP\Config::getAppValue('files', 'installed_version');
+if (version_compare($installedVersion, '1.1.4', '<')) {
+	$query = OC_DB::prepare( "SELECT propertyname, propertypath, userid FROM `*PREFIX*properties`" );
+	$result = $query->execute();
+	while( $row = $result->fetchRow()){
+		$query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyname = ? WHERE userid = ? AND propertypath = ?' );
+		$query->execute( array( preg_replace("/^{.*}/", "", $row["propertyname"]),$row["userid"], $row["propertypath"] ));
+	}
+}
+
 //update from OC 3
 
 //try to remove remaining files.
diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version
index 8cfbc905b39f65131ba18e561d236557fbdc52cc..1b87bcd0b09c209d6eac67fd268556b35acd1933 100644
--- a/apps/files/appinfo/version
+++ b/apps/files/appinfo/version
@@ -1 +1 @@
-1.1.1
\ No newline at end of file
+1.1.4
\ No newline at end of file
diff --git a/apps/files/download.php b/apps/files/download.php
index 2b5d4e2d8768a718b971209a47f0b2a9779f20cb..4e2478d1ad7cb55fbe5dac57ea194048dbad7972 100644
--- a/apps/files/download.php
+++ b/apps/files/download.php
@@ -46,4 +46,3 @@ header('Content-Length: '.OC_Filesystem::filesize($filename));
 
 @ob_end_clean();
 OC_Filesystem::readfile( $filename );
-?>
diff --git a/apps/files/index.php b/apps/files/index.php
index ed36c5edab04276ddf24d603d1ef8b9d739372ca..79bed8e357ed8d711a89bd7a4041e9e7906cff47 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -40,6 +40,7 @@ $dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : '';
 // Redirect if directory does not exist
 if(!OC_Filesystem::is_dir($dir.'/')) {
 	header('Location: '.$_SERVER['PHP_SELF'].'');
+	exit();
 }
 
 $files = array();
@@ -98,5 +99,3 @@ $tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
 $tmpl->assign( 'uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
 $tmpl->assign( 'allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
 $tmpl->printPage();
-
-?>
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index e6a9a6883af8e7bec9d0f3bcb2b9b48ddb5fbcf0..3645258f98f0ae3a43211369a90477b4439e84f1 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -14,7 +14,7 @@ FileList={
 			var extension=false;
 		}
 		html+='<td class="filename" style="background-image:url('+img+')"><input type="checkbox" />';
-		html+='<a class="name" href="download.php?file='+$('#dir').val()+'/'+name+'"><span class="nametext">'+basename
+		html+='<a class="name" href="download.php?file='+$('#dir').val().replace(/</, '&lt;').replace(/>/, '&gt;')+'/'+name+'"><span class="nametext">'+basename
 		if(extension){
 			html+='<span class="extension">'+extension+'</span>';
 		}
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 86c5185bf728b6752b2f924375531259bdc61a7e..a4e2361feeb1c35013c4322862d444cca8defc23 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -497,23 +497,27 @@ $(document).ready(function() {
 						localName=(localName.match(/:\/\/(.[^/]+)/)[1]).replace('www.','');
 					}
 					localName = getUniqueName(localName);
-					$.post(
-						OC.filePath('files','ajax','newfile.php'),
-						{dir:$('#dir').val(),source:name,filename:localName},
-						function(result){
-							if(result.status == 'success'){
-								var date=new Date();
-								FileList.addFile(localName,0,date);
-								var tr=$('tr').filterAttr('data-file',localName);
-								tr.data('mime',result.data.mime);
-								getMimeIcon(result.data.mime,function(path){
-									tr.find('td.filename').attr('style','background-image:url('+path+')');
-								});
-							}else{
+					$('#uploadprogressbar').progressbar({value:0});
+					$('#uploadprogressbar').fadeIn();
 
-							}
-						}
-					);
+					var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName});
+					eventSource.listen('progress',function(progress){
+						$('#uploadprogressbar').progressbar('value',progress);
+					});
+					eventSource.listen('success',function(mime){
+						$('#uploadprogressbar').fadeOut();
+						var date=new Date();
+						FileList.addFile(localName,0,date);
+						var tr=$('tr').filterAttr('data-file',localName);
+						tr.data('mime',mime);
+						getMimeIcon(mime,function(path){
+							tr.find('td.filename').attr('style','background-image:url('+path+')');
+						});
+					});
+					eventSource.listen('error',function(error){
+						$('#uploadprogressbar').fadeOut();
+						alert(error);
+					});
 					break;
 			}
 			var li=$(this).parent();
diff --git a/apps/files/settings.php b/apps/files/settings.php
index e5a66239ebd39de6b628637811684b197f73f2ba..cd6dd8c1616c0e25e102269fe1743cb68eb53867 100644
--- a/apps/files/settings.php
+++ b/apps/files/settings.php
@@ -56,5 +56,3 @@ $tmpl = new OCP\Template( "files", "index", "user" );
 $tmpl->assign( 'files', $files );
 $tmpl->assign( "breadcrumb", $breadcrumb );
 $tmpl->printPage();
-
-?>
diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js
index 37d62265c9439b9991ab8d9ee5f1b20604b00260..8cc433246cb31a85aa6049eb23f83260ce716b29 100644
--- a/apps/files_encryption/js/settings.js
+++ b/apps/files_encryption/js/settings.js
@@ -17,8 +17,8 @@ $(document).ready(function(){
 		OC.AppConfig.setValue('files_encryption','type_blacklist',blackList);
 	}
 
-	$('#enbale_encryption').change(function(){
-		var checked=$('#enbale_encryption').is(':checked');
+	$('#enable_encryption').change(function(){
+		var checked=$('#enable_encryption').is(':checked');
 		OC.AppConfig.setValue('files_encryption','enable_encryption',(checked)?'true':'false');
 	})
 })
\ No newline at end of file
diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php
index e00205375639b664a623c8393fd1c83bd8f21e83..46471911d94482a04119ca38631eb4466bf3afbe 100644
--- a/apps/files_encryption/lib/cryptstream.php
+++ b/apps/files_encryption/lib/cryptstream.php
@@ -31,9 +31,7 @@ class OC_CryptStream{
 	public static $sourceStreams=array();
 	private $source;
 	private $path;
-	private $readBuffer;//for streams that dont support seeking
 	private $meta=array();//header/meta for source stream
-	private $count;
 	private $writeCache;
 	private $size;
 	private static $rootView;
@@ -100,7 +98,6 @@ class OC_CryptStream{
 	
 	public function stream_write($data){
 		$length=strlen($data);
-		$written=0;
 		$currentPos=ftell($this->source);
 		if($this->writeCache){
 			$data=$this->writeCache.$data;
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php
index 25b5a06f56ca8a02b76037df690e376f3e5d94a9..79780d694cfdeea3cfaa5e7446329d81dade18d0 100644
--- a/apps/files_encryption/templates/settings.php
+++ b/apps/files_encryption/templates/settings.php
@@ -7,6 +7,6 @@
 				<option selected="selected" value="<?php echo $type;?>"><?php echo $type;?></option>
 			<?php endforeach;?>
 		</select>
-		<input type='checkbox' id='enbale_encryption' <?php if($_['encryption_enabled']){echo 'checked="checked"';} ?>></input><label for='enbale_encryption'><?php echo $l->t('Enable Encryption')?></label>
+		<input type='checkbox' id='enable_encryption' <?php if($_['encryption_enabled']){echo 'checked="checked"';} ?>></input><label for='enable_encryption'><?php echo $l->t('Enable Encryption')?></label>
 	</fieldset>
 </form>
diff --git a/apps/files_external/ajax/addMountPoint.php b/apps/files_external/ajax/addMountPoint.php
index 549cb6a34277015261cac9832a9071e86687a987..e08f805942f0e935258f9016a29bd5f21c034e2e 100644
--- a/apps/files_external/ajax/addMountPoint.php
+++ b/apps/files_external/ajax/addMountPoint.php
@@ -1,6 +1,8 @@
 <?php
 
 OCP\JSON::checkAppEnabled('files_external');
+OCP\JSON::callCheck();
+
 if ($_POST['isPersonal'] == 'true') {
 	OCP\JSON::checkLoggedIn();
 	$isPersonal = true;
@@ -9,5 +11,3 @@ if ($_POST['isPersonal'] == 'true') {
 	$isPersonal = false;
 }
 OC_Mount_Config::addMountPoint($_POST['mountPoint'], $_POST['class'], $_POST['classOptions'], $_POST['mountType'], $_POST['applicable'], $isPersonal);
-
-?>
diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php
index c5f49a82d365c8994a7754899b4938744707aa9f..d28a7d24b2d951040bd2ad10582e12cc984ffb2a 100644
--- a/apps/files_external/ajax/addRootCertificate.php
+++ b/apps/files_external/ajax/addRootCertificate.php
@@ -5,6 +5,7 @@ OCP\JSON::checkAppEnabled('files_external');
 $view = \OCP\Files::getStorage("files_external");
 $from = $_FILES['rootcert_import']['tmp_name'];
 $path = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/';
+if(!file_exists($path)) mkdir($path,0700,true);
 $to = $path.$_FILES['rootcert_import']['name'];
 move_uploaded_file($from, $to);
 
diff --git a/apps/files_external/ajax/removeMountPoint.php b/apps/files_external/ajax/removeMountPoint.php
index b77b306bcb5773abed03432d24d490651b6837d3..aa44642620267a39b2538eba3773bee1c8759224 100644
--- a/apps/files_external/ajax/removeMountPoint.php
+++ b/apps/files_external/ajax/removeMountPoint.php
@@ -1,6 +1,8 @@
 <?php
 
 OCP\JSON::checkAppEnabled('files_external');
+OCP\JSON::callCheck();
+
 if ($_POST['isPersonal'] == 'true') {
 	OCP\JSON::checkLoggedIn();
 	$isPersonal = true;
@@ -9,5 +11,3 @@ if ($_POST['isPersonal'] == 'true') {
 	$isPersonal = false;
 }
 OC_Mount_Config::removeMountPoint($_POST['mountPoint'], $_POST['mountType'], $_POST['applicable'], $isPersonal);
-
-?>
diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php
index 35663d431f8e30637c7fc10f49156653451f5143..c849db384331073393930c7fda16e2b0c60ed89c 100755
--- a/apps/files_external/lib/dropbox.php
+++ b/apps/files_external/lib/dropbox.php
@@ -208,7 +208,7 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
 		if (isset(self::$tempFiles[$tmpFile])) {
 			$handle = fopen($tmpFile, 'r');
 			try {
-				$response = $this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle);
+				$this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle);
 				unlink($tmpFile);
 			} catch (Exception $exception) {
 				
diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php
index 41b560ae84e6f8a19eda60933095a4e6e151f73c..2ad85d09d5f8b7287584c0b6f3ff3159a273b80b 100644
--- a/apps/files_external/lib/google.php
+++ b/apps/files_external/lib/google.php
@@ -178,7 +178,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
 		if ($collection == '/' || $collection == '\.' || $collection == '.') {
 			$uri = 'https://docs.google.com/feeds/default/private/full';
 		// Get parent content link
-		} else if ($dom = $this->getResource(basename($dir))) {
+		} else if ($dom = $this->getResource(basename($collection))) {
 			$uri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src');
 		}
 		if (isset($uri)) {
@@ -341,7 +341,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
 						break;
 					}
 				}
-				$title = basename($path);
+				$title = basename($path2);
 				// Construct post data
 				$postData = '<?xml version="1.0" encoding="UTF-8"?>';
 				$postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007" xmlns:gd="http://schemas.google.com/g/2005" gd:etag='.$etag.'>';
@@ -352,13 +352,13 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
 			} else {
 				// Move to different collection
 				if ($collectionEntry = $this->getResource($collection)) {
-					$feedUri = $colelctionEntry->getElementsByTagName('content')->item(0)->getAttribute('src');
+					$feedUri = $collectionEntry->getElementsByTagName('content')->item(0)->getAttribute('src');
 					// Construct post data
 					$postData = '<?xml version="1.0" encoding="UTF-8"?>';
 					$postData .= '<entry xmlns="http://www.w3.org/2005/Atom">';
 					$postData .= '<id>'.$entry->getElementsByTagName('id')->item(0).'</id>';
 					$postData .= '</entry>';
-					$this->sendRequest($uri, 'POST', $postData);
+					$this->sendRequest($feedUri, 'POST', $postData);
 					return true;
 				}
 			}
@@ -424,7 +424,6 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
 			}
 		}
 		if (!isset($uploadUri) && $entry) {
-			$etag = $entry->getAttribute('gd:etag');
 			$links = $entry->getElementsByTagName('link');
 			foreach ($links as $link) {
 				if ($link->getAttribute('rel') == 'http://schemas.google.com/g/2005#resumable-create-media') {
diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php
index 5e34deb23373b8a342b3856210fd9bfb5cb87365..8a5e993b1d022221b5ae0228489a8b9f94fd408d 100644
--- a/apps/files_external/lib/smb.php
+++ b/apps/files_external/lib/smb.php
@@ -15,8 +15,6 @@ class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{
 	private $root;
 	private $share;
 
-	private static $tempFiles=array();
-
 	public function __construct($params){
 		$this->host=$params['host'];
 		$this->user=$params['user'];
diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php
index ea6ca65b976206109c26b581e1887f843cb37fc4..84d64b6519331c9d37600e673870512448a71572 100644
--- a/apps/files_external/lib/webdav.php
+++ b/apps/files_external/lib/webdav.php
@@ -45,7 +45,10 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
 		$this->client = new OC_Connector_Sabre_Client($settings);
 		
 		if($caview = \OCP\Files::getStorage('files_external')) {
-			$this->client->setCurlSettings(array(CURLOPT_CAINFO => \OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt'));
+			$certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt';
+			if (file_exists($certPath))  {
+				$this->client->addTrustedCertificates($certPath);
+			}
 		}
 		//create the root folder if necesary
 		$this->mkdir('');
@@ -74,16 +77,11 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
 		$path=$this->cleanPath($path);
 		try{
 			$response=$this->client->propfind($path, array(),1);
-			$stripLength=strlen($this->root)+strlen($path);
 			$id=md5('webdav'.$this->root.$path);
 			OC_FakeDirStream::$dirs[$id]=array();
-			$skip = true;
-			foreach($response as $file=>$data){
-				// Skip the first file, because it is the current directory
-				if ($skip) {
-					$skip = false;
-					continue;
-				}
+			$files=array_keys($response);
+			array_shift($files);//the first entry is the current directory
+			foreach($files as $file){
 				$file = urldecode(basename($file));
 				OC_FakeDirStream::$dirs[$id][]=$file;
 			}
@@ -117,7 +115,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
 	public function file_exists($path){
 		$path=$this->cleanPath($path);
 		try{
-			$response=$this->client->propfind($path, array('{DAV:}resourcetype'));
+			$this->client->propfind($path, array('{DAV:}resourcetype'));
 			return true;//no 404 exception
 		}catch(Exception $e){
 			return false;
@@ -198,7 +196,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
 			$mtime=time();
 		}
 		$path=$this->cleanPath($path);
-		$this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime,));
+		$this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime));
 	}
 
 	public function getFile($path,$target){
diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php
index 3d65e9b74733519824f5b815f6971ac90d8c647d..e8bc94790dcea18b9cb48d996185163422b40017 100644
--- a/apps/files_external/templates/settings.php
+++ b/apps/files_external/templates/settings.php
@@ -1,4 +1,4 @@
-<form id="files_external" method="post" enctype="multipart/form-data" action="/owncloud/?app=files_external&getfile=ajax%2FaddRootCertificate.php">
+<form id="files_external" method="post" enctype="multipart/form-data" action="<?php echo OCP\Util::linkTo('files_external', 'ajax/addRootCertificate.php'); ?>">
 	<fieldset class="personalblock">
 	<legend><strong><?php echo $l->t('External Storage'); ?></strong></legend>
 		<table id="externalStorage" data-admin='<?php echo json_encode($_['isAdminPage']); ?>'>
diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php
index d0084c94afd1c5f5f78f3a907845e23244951b8e..b9b4cf65bd674a71631954321792b9cfbc2cbe02 100644
--- a/apps/files_external/tests/amazons3.php
+++ b/apps/files_external/tests/amazons3.php
@@ -45,6 +45,3 @@ if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['r
 		}
 	}
 }
-
-?>
-
diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php
index 68481b4e66bcca947862b2348ce510ed5647af70..97796bca1289c6c78278fb177b04af74d5b635b6 100644
--- a/apps/files_external/tests/ftp.php
+++ b/apps/files_external/tests/ftp.php
@@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']){
 }else{
 	class Test_Filestorage_FTP extends Test_FileStorage {
 		private $config;
-		private $id;
 
 		public function setUp(){
 			$id=uniqid();
diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php
index 08116f0e748918e36e839597be1c43238ec0d957..806db5a6aaae3c6adbfe2b76952052f26746601d 100644
--- a/apps/files_external/tests/google.php
+++ b/apps/files_external/tests/google.php
@@ -28,7 +28,6 @@ if(!is_array($config) or !isset($config['google']) or !$config['google']['run'])
 	class Test_Filestorage_Google extends Test_FileStorage {
 		
 		private $config;
-		private $id;
 
 		public function setUp(){
 			$id=uniqid();
diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php
index e1495b7480dfaa1ace26de23b115e6488a6bdd64..001ef842276705b3fd3a759a6d89b57855448f88 100644
--- a/apps/files_external/tests/smb.php
+++ b/apps/files_external/tests/smb.php
@@ -14,7 +14,6 @@ if(!is_array($config) or !isset($config['smb']) or !$config['smb']['run']){
 }else{
 	class Test_Filestorage_SMB extends Test_FileStorage {
 		private $config;
-		private $id;
 
 		public function setUp(){
 			$id=uniqid();
diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php
index f0bde6ed605ae5f09c7b0a42f6759a2b2a0d6570..1520c9473d34e6f5db1c8c18d8ad3b3e344b25f8 100644
--- a/apps/files_external/tests/swift.php
+++ b/apps/files_external/tests/swift.php
@@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['swift']) or !$config['swift']['run']){
 }else{
 	class Test_Filestorage_SWIFT extends Test_FileStorage {
 		private $config;
-		private $id;
 
 		public function setUp(){
 			$id=uniqid();
diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php
index 144659819b6a19b23c831c1e6a3344e90f888677..14abbef2cbfac5cf666572ef8dcaafd9700687c8 100644
--- a/apps/files_external/tests/webdav.php
+++ b/apps/files_external/tests/webdav.php
@@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['webdav']) or !$config['webdav']['run'])
 }else{
 	class Test_Filestorage_DAV extends Test_FileStorage {
 		private $config;
-		private $id;
 
 		public function setUp(){
 			$id=uniqid();
diff --git a/apps/files_imageviewer/appinfo/app.php b/apps/files_imageviewer/appinfo/app.php
index 6c8d8c30cade13aff96ea55fb3bf2137a3ea4a76..6184585cff38082b7103a24af1bc9fdd751030a4 100644
--- a/apps/files_imageviewer/appinfo/app.php
+++ b/apps/files_imageviewer/appinfo/app.php
@@ -4,5 +4,3 @@ OCP\Util::addscript( 'files_imageviewer', 'lightbox' );
 OCP\Util::addscript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack');
 OCP\Util::addscript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
 OCP\Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' );
-
-?>
diff --git a/apps/files_pdfviewer/appinfo/app.php b/apps/files_pdfviewer/appinfo/app.php
index c8ca2dc8d940f2591741e5fe539a1b232acaf7db..e4771ee517fc26025c5ae4bbd280a0bc76b91c57 100644
--- a/apps/files_pdfviewer/appinfo/app.php
+++ b/apps/files_pdfviewer/appinfo/app.php
@@ -6,4 +6,3 @@ OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/compatibility');
 OCP\Util::addscript( 'files_pdfviewer', 'viewer');
 OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/build/pdf');
 OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/viewer');
-?>
diff --git a/apps/files_pdfviewer/js/pdfjs/viewer.js b/apps/files_pdfviewer/js/pdfjs/viewer.js
index f49257d79294b4e460f61190cf8035bba9c3bb09..90dd1eef0209e95f25d0b379c4f1131afdb95ba7 100644
--- a/apps/files_pdfviewer/js/pdfjs/viewer.js
+++ b/apps/files_pdfviewer/js/pdfjs/viewer.js
@@ -1432,7 +1432,7 @@ var DocumentOutlineView = function documentOutlineView(outline) {
         queue.push({parent: itemsDiv, items: item.items});
       }
 
-      levelData.parent.appendChild(div);
+//      levelData.parent.appendChild(div);
     }
   }
 };
@@ -1751,10 +1751,9 @@ function updateThumbViewArea() {
 }
 
 window.addEventListener('resize', function webViewerResize(evt) {
-  if (PDFView.initialized &&
+  if (PDFView.initialized && PDFView.active &&
       (document.getElementById('pageWidthOption').selected ||
-      document.getElementById('pageFitOption').selected ||
-      document.getElementById('pageAutoOption').selected))
+      document.getElementById('pageFitOption').selected ))
       PDFView.parseScale(document.getElementById('scaleSelect').value);
   updateViewarea();
 });
@@ -1788,8 +1787,8 @@ window.addEventListener('change', function webViewerChange(evt) {
   document.title = file.name;
 
   // URL does not reflect proper document location - hiding some icons.
-  document.getElementById('viewBookmark').setAttribute('hidden', 'true');
-  document.getElementById('download').setAttribute('hidden', 'true');
+//  document.getElementById('viewBookmark').setAttribute('hidden', 'true');
+//  document.getElementById('download').setAttribute('hidden', 'true');
 }, true);
 
 function selectScaleOption(value) {
diff --git a/apps/files_sharing/ajax/email.php b/apps/files_sharing/ajax/email.php
index 523c3d2078b66dacca21860bd0b346380c3b3215..3026eb74675cc21bda79d7b8f42ec3923d91ab55 100644
--- a/apps/files_sharing/ajax/email.php
+++ b/apps/files_sharing/ajax/email.php
@@ -1,6 +1,8 @@
 <?php
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('files_sharing');
+OCP\JSON::callCheck();
+
 $user = OCP\USER::getUser();
 // TODO translations
 $type = (strpos($_POST['file'], '.') === false) ? 'folder' : 'file';
@@ -8,6 +10,4 @@ $subject = $user.' shared a '.$type.' with you';
 $link = $_POST['link'];
 $text = $user.' shared the '.$type.' '.$_POST['file'].' with you. It is available for download here: '.$link;
 $fromaddress = OCP\Config::getUserValue($user, 'settings', 'email', 'sharing-noreply@'.OCP\Util::getServerHost());
-OC_Mail::send($_POST['toaddress'], $_POST['toaddress'], $subject, $text, $fromaddress, $user);
-
-?>
+OCP\Util::sendMail($_POST['toaddress'], $_POST['toaddress'], $subject, $text, $fromaddress, $user);
diff --git a/apps/files_sharing/ajax/getitem.php b/apps/files_sharing/ajax/getitem.php
index 94f0890d70680373fbcc721f4fea09cf75f604b9..ff6c29b6a0a08ad08e0c704dad298f42c53360ee 100644
--- a/apps/files_sharing/ajax/getitem.php
+++ b/apps/files_sharing/ajax/getitem.php
@@ -64,5 +64,3 @@ while ($path != $userDirectory) {
 }
 
 OCP\JSON::success(array('data' => $item));
-
-?>
diff --git a/apps/files_sharing/ajax/getstatuses.php b/apps/files_sharing/ajax/getstatuses.php
index 488cab13bc9ec65f2bddadf3c896373569c714ec..1be4d9a0d9c0916460ac91d6d70d41b891af6430 100644
--- a/apps/files_sharing/ajax/getstatuses.php
+++ b/apps/files_sharing/ajax/getstatuses.php
@@ -20,5 +20,3 @@ if ($rows = OC_Share::getMySharedItems()) {
 }
 
 OCP\JSON::success(array('data' => $items));
-
-?>
diff --git a/apps/files_sharing/ajax/setpermissions.php b/apps/files_sharing/ajax/setpermissions.php
index 4d19cd40f2d4fd5ee07d72f26eff2bf7816c8e6a..0a2cf78f7651a1b7f84be533b1e173152aaeedbf 100644
--- a/apps/files_sharing/ajax/setpermissions.php
+++ b/apps/files_sharing/ajax/setpermissions.php
@@ -2,6 +2,7 @@
 
 OCP\JSON::checkAppEnabled('files_sharing');
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 $source = '/'.OCP\USER::getUser().'/files'.$_POST['source'];
 $uid_shared_with = $_POST['uid_shared_with'];
@@ -9,5 +10,3 @@ $permissions = $_POST['permissions'];
 OC_Share::setPermissions($source, $uid_shared_with, $permissions);
 
 OCP\JSON::success();
-
-?>
diff --git a/apps/files_sharing/ajax/share.php b/apps/files_sharing/ajax/share.php
index 1ee8c3f791a6d214be1d358168f2cbb2ac003f97..3f224d1b67b943212484e21d3c277fd9f5ebe970 100644
--- a/apps/files_sharing/ajax/share.php
+++ b/apps/files_sharing/ajax/share.php
@@ -2,6 +2,7 @@
 
 OCP\JSON::checkAppEnabled('files_sharing');
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 $userDirectory = '/'.OCP\USER::getUser().'/files';
 $sources = explode(';', $_POST['sources']);
@@ -34,5 +35,3 @@ foreach ($sources as $source) {
 		}
 	}
 }
-
-?>
diff --git a/apps/files_sharing/ajax/toggleresharing.php b/apps/files_sharing/ajax/toggleresharing.php
index 673f00c5d18e57d5b483191f9cf0db0968f5adf7..7da4fdfeea8585db420d35ee5ab79e34a308186d 100644
--- a/apps/files_sharing/ajax/toggleresharing.php
+++ b/apps/files_sharing/ajax/toggleresharing.php
@@ -7,5 +7,3 @@ if ($_POST['resharing'] == true) {
 } else {
 	OCP\Config::setAppValue('files_sharing', 'resharing', 'no');
 }
-
-?>
diff --git a/apps/files_sharing/ajax/unshare.php b/apps/files_sharing/ajax/unshare.php
index d50e7963a0551eb143f7f9b2181cec94e71def06..02a59c4016b990ea3ea4ee3fef3715fa589af896 100644
--- a/apps/files_sharing/ajax/unshare.php
+++ b/apps/files_sharing/ajax/unshare.php
@@ -2,11 +2,10 @@
 
 OCP\JSON::checkAppEnabled('files_sharing');
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 $source = '/'.OCP\USER::getUser().'/files'.$_POST['source'];
 $uid_shared_with = $_POST['uid_shared_with'];
 OC_Share::unshare($source, $uid_shared_with);
 
 OCP\JSON::success();
-
-?>
diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php
index 0e1bf6d588ec75f653c0f278b24cdefafc55db7d..388a4844b952115ecd053e730bab0a0f2de5324b 100644
--- a/apps/files_sharing/ajax/userautocomplete.php
+++ b/apps/files_sharing/ajax/userautocomplete.php
@@ -28,5 +28,3 @@ $users[] = "</optgroup>";
 $groups[] = "</optgroup>";
 $users = array_merge($users, $groups);
 OCP\JSON::encodedPrint($users);
-
-?>
diff --git a/apps/files_sharing/appinfo/version b/apps/files_sharing/appinfo/version
index 7dff5b8921122a487162febe3c8e32effb7acb35..f4778493c50025c6ab147a1fec7486ef0c706792 100644
--- a/apps/files_sharing/appinfo/version
+++ b/apps/files_sharing/appinfo/version
@@ -1 +1 @@
-0.2.1
\ No newline at end of file
+0.2.2
\ No newline at end of file
diff --git a/apps/files_sharing/get.php b/apps/files_sharing/get.php
index 40a90a1530c559bacb5e0b24da05a9a22e2b78c8..70a5162d382f055ad51ecf949b86ccc553070248 100644
--- a/apps/files_sharing/get.php
+++ b/apps/files_sharing/get.php
@@ -86,4 +86,3 @@ if (isset($_GET['token']) && $source = OC_Share::getSource($_GET['token'])) {
 	$tmpl->printPage();
 	die();
 }
-?>
diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php
index 6e092269250052efdd0b736677f2e688ebc03155..0237acfc1ac3b06285f01159fadd2e0440c94aba 100644
--- a/apps/files_sharing/lib_share.php
+++ b/apps/files_sharing/lib_share.php
@@ -513,5 +513,3 @@ class OC_Share {
 	}
 
 }
-
-?>
diff --git a/apps/files_sharing/list.php b/apps/files_sharing/list.php
index 2fd24840d36359043e57041ec32f2a0800622a59..54704c942fda35913aa48b54ceafd186fc8d88e1 100644
--- a/apps/files_sharing/list.php
+++ b/apps/files_sharing/list.php
@@ -33,5 +33,3 @@ OCP\Util::addscript("files_sharing", "list");
 $tmpl = new OCP\Template("files_sharing", "list", "user");
 $tmpl->assign("shared_items", OC_Share::getMySharedItems());
 $tmpl->printPage();
-
-?>
diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php
index 4138fc2b399303fbf0037169ac53fe26f1c62f3e..32fd2124429bd854320e5bdcea7488330de2cc1a 100644
--- a/apps/files_sharing/sharedstorage.php
+++ b/apps/files_sharing/sharedstorage.php
@@ -67,7 +67,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 	public function rmdir($path) {
 		// The folder will be removed from the database, but won't be deleted from the owner's filesystem
 		OC_Share::unshareFromMySelf($this->datadir.$path);
-		$this->clearFolderSizeCache($path);
 	}
 	
 	public function opendir($path) {
@@ -190,7 +189,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 	
 	public function filesize($path) {
 		if ($path == "" || $path == "/" || $this->is_dir($path)) {
-			return $this->getFolderSize($path);
+			return 0;
 		} else {
 			$source = $this->getSource($path);
 			if ($source) {
@@ -200,55 +199,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 		}
 	}
 
-	public function getFolderSize($path) {
-		return 0; //depricated
-	}
-	
-	private function calculateFolderSize($path) {
-		if ($this->is_file($path)) {
-			$path = dirname($path);
-		}
-		$size = 0;
-		if ($dh = $this->opendir($path)) {
-			while (($filename = readdir($dh)) !== false) {
-				if ($filename != "." && $filename != "..") {
-					$subFile = $path."/".$filename;
-					if ($this->is_file($subFile)) {
-						$size += $this->filesize($subFile);
-					} else {
-						$size += $this->getFolderSize($subFile);
-					}
-				}
-			}
-			if ($size > 0) {
-				$dbpath = rtrim($this->datadir.$path, "/");
-// 				$query = OCP\DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)");
-// 				$result = $query->execute(array($dbpath, $size));
-			}
-		}
-		return $size;
-	}
-
-	private function clearFolderSizeCache($path) {
-		$path = rtrim($path, "/");
-		$path = preg_replace('{(/)\1+}', "/", $path);
-		if ($this->is_file($path)) {
-			$path = dirname($path);
-		}
-		$dbpath = rtrim($this->datadir.$path, "/");
-// 		$query = OCP\DB::prepare("DELETE FROM *PREFIX*/*foldersize*/ WHERE path = ?");
-// 		$result = $query->execute(array($dbpath));
-		if ($path != "/" && $path != "") {
-			$parts = explode("/", $path);
-			$part = array_pop($parts);
-			if (empty($part)) {
-				array_pop($parts);
-			}
-			$parent = implode("/", $parts);
-			$this->clearFolderSizeCache($parent);
-		}
-	}
-
 	public function is_readable($path) {
 		return true;
 	}
@@ -341,9 +291,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 				OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info);
 				$storage = OC_Filesystem::getStorage($source);
 				$result = $storage->file_put_contents($this->getInternalPath($source), $data);
-				if ($result) {
-					$this->clearFolderSizeCache($path);
-				}
 				return $result;
 			}
 		}
@@ -365,7 +312,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 		} else {
 			OC_Share::unshareFromMySelf($target);
 		}
-		$this->clearFolderSizeCache($this->getInternalPath($target));
 		return true;
 	}
 	
@@ -401,8 +347,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 		} else {
 			OC_Share::setTarget($oldTarget, $newTarget);
 		}
-		$this->clearFolderSizeCache($this->getInternalPath($oldTarget));
-		$this->clearFolderSizeCache($this->getInternalPath($newTarget));
 		return true;
 	}
 	
@@ -413,9 +357,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 			if ($this->is_writable($path2)) {
 				$tmpFile = $this->toTmpFile($path1);
 				$result = $this->fromTmpFile($tmpFile, $path2);
-				if ($result) {
-					$this->clearFolderSizeCache($path2);
-				}
 				return $result;
 			} else {
 				return false;
@@ -451,9 +392,6 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 			if ($source) {
 				$storage = OC_Filesystem::getStorage($source);
 				$result = $storage->fromTmpFile($tmpFile, $this->getInternalPath($source));
-				if ($result) {
-					$this->clearFolderSizeCache($path);
-				}
 				return $result;
 			}
 		} else {
@@ -520,7 +458,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
 		$source = $this->getSource($path);
 		if ($source) {
 			$storage = OC_Filesystem::getStorage($source);
-			return $storage->touch($this->getInternalPath($source),$time);
+			return $storage->touch($this->getInternalPath($source),$mtime);
 		}
 	}
 
diff --git a/apps/files_sharing_log/appinfo/database.xml b/apps/files_sharing_log/appinfo/database.xml
index 92e5f0125bd2d9f3c5c872274071bba1ed7aceaf..dae811f87fad84950e51fe02704c6eb9cb282d27 100644
--- a/apps/files_sharing_log/appinfo/database.xml
+++ b/apps/files_sharing_log/appinfo/database.xml
@@ -3,7 +3,7 @@
 	 <name>*dbname*</name>
 	 <create>true</create>
 	 <overwrite>false</overwrite>
-	 <charset>latin1</charset>
+	 <charset>utf8</charset>
 	 <table>
 		<name>*dbprefix*sharing_log</name>
 		<declaration>
diff --git a/apps/files_texteditor/ajax/savefile.php b/apps/files_texteditor/ajax/savefile.php
index 961db7105e3371dd7da4036a94cc12dae1e6940f..f3ac323e32fd2f8653f277038865de010e1c5b43 100644
--- a/apps/files_texteditor/ajax/savefile.php
+++ b/apps/files_texteditor/ajax/savefile.php
@@ -26,6 +26,7 @@
 
 // Check if we are a user
 OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 // Get paramteres
 $filecontents = isset($_POST['filecontents']) ? $_POST['filecontents'] : false;
diff --git a/apps/files_texteditor/appinfo/app.php b/apps/files_texteditor/appinfo/app.php
index c745170018e692de292941671b5ce771ec9956ba..1f9773bca3a6db3f2f369cb7e67802935604c84f 100644
--- a/apps/files_texteditor/appinfo/app.php
+++ b/apps/files_texteditor/appinfo/app.php
@@ -4,4 +4,3 @@ OCP\Util::addStyle( 'files_texteditor', 'DroidSansMono/stylesheet' );
 OCP\Util::addStyle( 'files_texteditor', 'style' );
 OCP\Util::addscript( 'files_texteditor', 'editor');
 OCP\Util::addscript( 'files_texteditor', 'aceeditor/ace');
-?>
diff --git a/apps/files_versions/ajax/expireAll.php b/apps/files_versions/ajax/expireAll.php
index 238e3bdad4ddb31532a7f35ff1d487dfe20cf227..2a678c7f0a58e486acf0622dd54ea8b4997dfe7b 100644
--- a/apps/files_versions/ajax/expireAll.php
+++ b/apps/files_versions/ajax/expireAll.php
@@ -27,8 +27,9 @@
 // Check user and app status
 OCP\JSON::checkLoggedIn();
 OCP\App::checkAppEnabled('files_versions');
+OCP\JSON::callCheck();
 
-$versions = new OCA_Versions\Storage( new OC_FilesystemView('') );
+$versions = new OCA_Versions\Storage();
 
 if( $versions->expireAll() ){
 	
diff --git a/apps/files_versions/ajax/rollbackVersion.php b/apps/files_versions/ajax/rollbackVersion.php
index 8d1092f8b8e217f03bf59f97ec3896d6cd90beb3..24d71a914a4ebc2a3b10798a9370a073a3538bb2 100644
--- a/apps/files_versions/ajax/rollbackVersion.php
+++ b/apps/files_versions/ajax/rollbackVersion.php
@@ -1,6 +1,7 @@
 <?php
 
 OCP\JSON::checkAppEnabled('files_versions');
+OCP\JSON::callCheck();
 
 $userDirectory = "/".OCP\USER::getUser()."/files";
 
diff --git a/apps/files_versions/ajax/togglesettings.php b/apps/files_versions/ajax/togglesettings.php
index d513d12dd6c044a89e8b6e4b9bcdbae42bdda8c9..546b37ae1aa85dc31054d759f681ed6aca633a09 100644
--- a/apps/files_versions/ajax/togglesettings.php
+++ b/apps/files_versions/ajax/togglesettings.php
@@ -2,10 +2,9 @@
 
 OCP\JSON::checkAppEnabled('files_versions');
 OCP\JSON::checkAdminUser();
+OCP\JSON::callCheck();
 if (OCP\Config::getSystemValue('versions', 'true')=='true') {
 	OCP\Config::setSystemValue('versions', 'false');
 } else {
 	OCP\Config::setSystemValue('versions', 'true');
 }
-
-?>
diff --git a/apps/files_versions/appinfo/update.php b/apps/files_versions/appinfo/update.php
new file mode 100644
index 0000000000000000000000000000000000000000..9569ca104856bef57dcffd9c054e07ff614e3ea6
--- /dev/null
+++ b/apps/files_versions/appinfo/update.php
@@ -0,0 +1,16 @@
+<?php
+
+$installedVersion=OCP\Config::getAppValue('files_versions', 'installed_version');
+// move versions to new directory
+if (version_compare($installedVersion, '1.0.2', '<')) {
+	$users = \OCP\User::getUsers();
+	$datadir =  \OCP\Config::getSystemValue('datadirectory').'/';
+    foreach ($users as $user) {
+    	$oldPath = $datadir.$user.'/versions';
+    	$newPath = $datadir.$user.'/files_versions';
+    	if(is_dir($oldPath)) {
+    		rename($oldPath, $newPath);
+    	}
+    }
+	
+}
diff --git a/apps/files_versions/appinfo/version b/apps/files_versions/appinfo/version
index 7f207341d5d9bda5ae8acfa7a6e02cdf7cd9d983..e6d5cb833c634c4e2972335825d1100492e7c96b 100644
--- a/apps/files_versions/appinfo/version
+++ b/apps/files_versions/appinfo/version
@@ -1 +1 @@
-1.0.1
\ No newline at end of file
+1.0.2
\ No newline at end of file
diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php
index e3332d7cdb6086319356604f41e37ba844927125..a34c92ee42207566bd270a2ceae5a6c35384f5c2 100644
--- a/apps/files_versions/history.php
+++ b/apps/files_versions/history.php
@@ -30,7 +30,7 @@ if ( isset( $_GET['path'] ) ) {
 	$path = $_GET['path'];
 	$path = strip_tags( $path );
 	$tmpl->assign( 'path', $path );
-	$versions = new OCA_Versions\Storage( new OC_FilesystemView('') );
+	$versions = new OCA_Versions\Storage();
 
 	// roll back to old version if button clicked
         if( isset( $_GET['revert'] ) ) {
@@ -71,5 +71,3 @@ if ( isset( $_GET['path'] ) ) {
 }
 
 $tmpl->printPage( );
-
-?>
diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php
index b43fdb9fd33cc43bcb43643de9da00a7dd686eed..bfc8fd3a378b6289c421edcfd372f027b871afc7 100644
--- a/apps/files_versions/lib/hooks.php
+++ b/apps/files_versions/lib/hooks.php
@@ -30,6 +30,7 @@ class Hooks {
 		}
 	}
 	
+
 	/**
 	 * @brief Erase versions of deleted file
 	 * @param array
@@ -37,9 +38,10 @@ class Hooks {
 	 * This function is connected to the delete signal of OC_Filesystem
 	 * cleanup the versions directory if the actual file gets deleted
 	 */
-	public static function remove_hook($params) {
+	public static function remove_hook($params) {
+		$versions_fileview = \OCP\Files::getStorage('files_versions');
 		$rel_path =  $params['path'];
-		$abs_path = \OCP\Config::getSystemValue('datadirectory').'/'.\OCP\User::getUser()."/versions".$rel_path.'.v';
+		$abs_path = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$rel_path.'.v';
 		if(Storage::isversioned($rel_path)) {
 			$versions = Storage::getVersions($rel_path);
 			foreach ($versions as $v){
@@ -55,11 +57,14 @@ class Hooks {
 	 * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
 	 * of the stored versions along the actual file
 	 */
-	public static function rename_hook($params) {
+	public static function rename_hook($params) {
+		$versions_fileview = \OCP\Files::getStorage('files_versions');
 		$rel_oldpath =  $params['oldpath'];
-		$abs_oldpath = \OCP\Config::getSystemValue('datadirectory').'/'.\OCP\User::getUser()."/versions".$rel_oldpath.'.v';
-		$abs_newpath = \OCP\Config::getSystemValue('datadirectory').'/'.\OCP\User::getUser()."/versions".$params['newpath'].'.v';
-		if(Storage::isversioned($rel_oldpath)) {
+		$abs_oldpath = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$rel_oldpath.'.v';
+		$abs_newpath = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$params['newpath'].'.v';
+		if(Storage::isversioned($rel_oldpath)) {
+			$info=pathinfo($abs_newpath);
+			if(!file_exists($info['dirname'])) mkdir($info['dirname'],0700,true);
 			$versions = Storage::getVersions($rel_oldpath);
 			foreach ($versions as $v){
 				rename($abs_oldpath.$v['version'], $abs_newpath.$v['version']);
@@ -68,5 +73,3 @@ class Hooks {
 	}
 	
 }
-
-?>
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index 4f0ee2c6d522969d238bb1ceb92bda6489e33a82..f146676757d1686fa42e98394cadca9d258c6a77 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -22,41 +22,26 @@ class Storage {
 	//   - files_versionsfolder
 	//   - files_versionsblacklist
 	//   - files_versionsmaxfilesize
-	//   - files_versionsinterval 
-	//   - files_versionmaxversions 
+	//   - files_versionsinterval
+	//   - files_versionmaxversions
 	//
 	// todo:
 	//   - finish porting to OC_FilesystemView to enable network transparency
 	//   - add transparent compression. first test if it´s worth it.
 
-	const DEFAULTENABLED=true; 
-	const DEFAULTFOLDER='versions'; 
-	const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp'; 
-	const DEFAULTMAXFILESIZE=1048576; // 10MB 
+	const DEFAULTENABLED=true;
+	const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp';
+	const DEFAULTMAXFILESIZE=1048576; // 10MB
 	const DEFAULTMININTERVAL=60; // 1 min
 	const DEFAULTMAXVERSIONS=50;
-	
+
 	private $view;
-	
-	function __construct( $view ) {
-	
-		$this->view = $view;
-		
-	}
 
-	/**
-	 * init the versioning and create the versions folder.
-	 */
-	public static function init() {
-		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
-			// create versions folder
-			$foldername=\OCP\Config::getSystemValue('datadirectory').'/'. \OCP\USER::getUser() .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
-			if(!is_dir($foldername)){
-				mkdir($foldername);
-			}
-		}
-	}
+	function __construct() {
+
+		$this->view = \OCP\Files::getStorage('files_versions');
 
+	}
 
 	/**
 	 * listen to write event.
@@ -75,11 +60,11 @@ class Storage {
 	 */
 	public function store($filename) {
 		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
-		
+
 			$files_view = \OCP\Files::getStorage("files");
 			$users_view = \OCP\Files::getStorage("files_versions");
 			$users_view->chroot(\OCP\User::getUser().'/');
-			
+				
 			if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
 				$pos = strpos($source, '/files', 1);
 				$uid = substr($source, 1, $pos - 1);
@@ -87,15 +72,14 @@ class Storage {
 			} else {
 				$uid = \OCP\User::getUser();
 			}
-			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
-			$filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files';
-			Storage::init();
-
+				
+			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory') .  $this->view->getAbsolutePath('');
+				
 			//check if source file already exist as version to avoid recursions.
 			if ($users_view->file_exists($filename)) {
 				return false;
 			}
-			
+				
 			// check if filename is a directory
 			if($files_view->is_dir($filename)){
 				return false;
@@ -110,7 +94,7 @@ class Storage {
 					return false;
 				}
 			}
-			
+				
 			// check filesize
 			if($files_view->filesize($filename)>\OCP\Config::getSystemValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){
 				return false;
@@ -129,12 +113,12 @@ class Storage {
 
 
 			// create all parent folders
-			$info=pathinfo($filename);	
-			if(!file_exists($versionsFolderName.'/'.$info['dirname'])) mkdir($versionsFolderName.'/'.$info['dirname'],0700,true);	
+			$info=pathinfo($filename);
+			if(!file_exists($versionsFolderName.'/'.$info['dirname'])) mkdir($versionsFolderName.'/'.$info['dirname'],0700,true);
 
 			// store a new version of a file
-			@$users_view->copy('files'.$filename, 'versions'.$filename.'.v'.time());
-        
+			@$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.time());
+
 			// expire old revisions if necessary
 			Storage::expire($filename);
 		}
@@ -145,11 +129,11 @@ class Storage {
 	 * rollback to an old version of a file.
 	 */
 	public static function rollback($filename,$revision) {
-	
+
 		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
 			$users_view = \OCP\Files::getStorage("files_versions");
 			$users_view->chroot(\OCP\User::getUser().'/');
-			
+				
 			if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
 				$pos = strpos($source, '/files', 1);
 				$uid = substr($source, 1, $pos - 1);
@@ -157,23 +141,20 @@ class Storage {
 			} else {
 				$uid = \OCP\User::getUser();
 			}
-			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'.$uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
-			
-			$filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files';
-			
+				
 			// rollback
-			if( @$users_view->copy('versions'.$filename.'.v'.$revision, 'files'.$filename) ) {
-			
+			if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) {
+					
 				return true;
-				
+
 			}else{
-			
+					
 				return false;
-				
+
 			}
-			
+				
 		}
-		
+
 	}
 
 	/**
@@ -181,18 +162,17 @@ class Storage {
 	 */
 	public static function isversioned($filename) {
 		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+			$versions_fileview = \OCP\Files::getStorage("files_versions");
 			if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
 				$pos = strpos($source, '/files', 1);
-				$uid = substr($source, 1, $pos - 1);
 				$filename = substr($source, $pos + 6);
-			} else {
-				$uid = \OCP\User::getUser();
 			}
-			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
 
+			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+			
 			// check for old versions
-			$matches=glob($versionsFolderName.'/'.$filename.'.v*');
-			if(count($matches)>1){
+			$matches=glob($versionsFolderName.$filename.'.v*');
+			if(count($matches)>0){
 				return true;
 			}else{
 				return false;
@@ -203,17 +183,17 @@ class Storage {
 	}
 
 
-        
-        /**
-         * @brief get a list of all available versions of a file in descending chronological order
-         * @param $filename file to find versions of, relative to the user files dir
-         * @param $count number of versions to return
-         * @returns array
-         */
-        public static function getVersions( $filename, $count = 0 ) {
-        
-                if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
-                
+
+	/**
+	 * @brief get a list of all available versions of a file in descending chronological order
+	 * @param $filename file to find versions of, relative to the user files dir
+	 * @param $count number of versions to return
+	 * @returns array
+	 */
+	public static function getVersions( $filename, $count = 0 ) {
+
+		if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
+
 			if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
 				$pos = strpos($source, '/files', 1);
 				$uid = substr($source, 1, $pos - 1);
@@ -221,71 +201,71 @@ class Storage {
 			} else {
 				$uid = \OCP\User::getUser();
 			}
-			$versionsFolderName = \OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
-			$versions = array();         
-			
+			$versions_fileview = \OCP\Files::getStorage('files_versions');
+			$versionsFolderName = \OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+			$versions = array();
+				
 			// fetch for old versions
 			$matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
-			
+				
 			sort( $matches );
-			
+				
 			$i = 0;
-			
-			foreach( $matches as $ma ) {
 				
+			$files_view = \OCP\Files::getStorage('files');
+			$local_file = $files_view->getLocalFile($filename);
+			foreach( $matches as $ma ) {
+
 				$i++;
 				$versions[$i]['cur'] = 0;
 				$parts = explode( '.v', $ma );
 				$versions[$i]['version'] = ( end( $parts ) );
-				
+
 				// if file with modified date exists, flag it in array as currently enabled version
-				$curFile['fileName'] = basename( $parts[0] );
-				$curFile['filePath'] = \OCP\Config::getSystemValue('datadirectory').\OC_Filesystem::getRoot().'/'.$curFile['fileName'];
-				
-				( \md5_file( $ma ) == \md5_file( $curFile['filePath'] ) ? $versions[$i]['fileMatch'] = 1 : $versions[$i]['fileMatch'] = 0 );
-				
+				( \md5_file( $ma ) == \md5_file( $local_file ) ? $versions[$i]['fileMatch'] = 1 : $versions[$i]['fileMatch'] = 0 );
+
 			}
-			
+				
 			$versions = array_reverse( $versions );
 			
 			foreach( $versions as $key => $value ) {
-				
+
 				// flag the first matched file in array (which will have latest modification date) as current version
-				if ( $versions[$key]['fileMatch'] ) {
-				
-					$versions[$key]['cur'] = 1;
+				if ( $value['fileMatch'] ) {
+
+					$value['cur'] = 1;
 					break;
-					
+						
 				}
-			
+					
 			}
-			
+				
 			$versions = array_reverse( $versions );
-			
+				
 			// only show the newest commits
 			if( $count != 0 and ( count( $versions )>$count ) ) {
-			
+					
 				$versions = array_slice( $versions, count( $versions ) - $count );
-				
+
 			}
-	
+
 			return( $versions );
 
 
-                } else {
-                
+		} else {
+
 			// if versioning isn't enabled then return an empty array
-                        return( array() );
-                        
-                }
-                
-        }
-
-        /**
-         * @brief Erase a file's versions which exceed the set quota
-         */
-        public static function expire($filename) {
-                if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+			return( array() );
+
+		}
+
+	}
+
+	/**
+	 * @brief Erase a file's versions which exceed the set quota
+	 */
+	public static function expire($filename) {
+		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
 
 			if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
 				$pos = strpos($source, '/files', 1);
@@ -294,36 +274,33 @@ class Storage {
 			} else {
 				$uid = \OCP\User::getUser();
 			}
-			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+			$versions_fileview = \OCP\Files::getStorage("files_versions");
+			$versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
 
 			// check for old versions
 			$matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
-			
+				
 			if( count( $matches ) > \OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) ) {
-			
+					
 				$numberToDelete = count( $matches-\OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) );
 
 				// delete old versions of a file
 				$deleteItems = array_slice( $matches, 0, $numberToDelete );
-				
+
 				foreach( $deleteItems as $de ) {
-				
+
 					unlink( $versionsFolderName.'/'.$filename.'.v'.$de );
-					
+						
 				}
 			}
-                }
-        }
-
-        /**
-         * @brief Erase all old versions of all user files
-         * @return true/false
-         */
-        public function expireAll() {
-		
-		$dir = \OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
-		
-		return $this->view->deleteAll( $dir, true );
-	
-        }
+		}
+	}
+
+	/**
+	 * @brief Erase all old versions of all user files
+	 * @return true/false
+	 */
+	public function expireAll() {
+		return $this->view->deleteAll('', true);
+	}
 }
diff --git a/apps/files_versions/settings.php b/apps/files_versions/settings.php
index 5f9e60fc589b56c5795d4c5597909f6a8669ab84..f2873b8f7c2cfebeaf0b23e20e763f3d23fe455b 100644
--- a/apps/files_versions/settings.php
+++ b/apps/files_versions/settings.php
@@ -7,4 +7,3 @@ OCP\Util::addscript( 'files_versions', 'versions' );
 $tmpl = new OCP\Template( 'files_versions', 'settings');
 
 return $tmpl->fetchPage();
-?>
diff --git a/apps/gallery/ajax/createAlbum.php b/apps/gallery/ajax/createAlbum.php
index 61e2e9ae2e9928b90b4899a833e0495959f78750..5bfa5aa9a93ad68bf069049f6b48ddd1a051a5d0 100644
--- a/apps/gallery/ajax/createAlbum.php
+++ b/apps/gallery/ajax/createAlbum.php
@@ -28,5 +28,3 @@ OCP\JSON::checkAppEnabled('gallery');
 OC_Gallery_Album::create(OCP\USER::getUser(), $_GET['album_name']);
 
 OCP\JSON::success(array('name' => $_GET['album_name']));
-
-?>
diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php
index 7cbe3e46e2175dc4d5ff1b2bafca9f442df183bb..ab8c64e28ad22b758cc1a40c643600c9702f5fc0 100644
--- a/apps/gallery/ajax/galleryOp.php
+++ b/apps/gallery/ajax/galleryOp.php
@@ -186,4 +186,3 @@ if ($_GET['operation']) {
     OCP\JSON::error(array('cause' => 'Unknown operation'));
   }
 }
-?>
diff --git a/apps/gallery/ajax/viewImage.php b/apps/gallery/ajax/viewImage.php
new file mode 100644
index 0000000000000000000000000000000000000000..daf0ab741f052546e253c53cc13a3c2b6b1d2155
--- /dev/null
+++ b/apps/gallery/ajax/viewImage.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * ownCloud - gallery application
+ *
+ * @author Ike Devolder
+ * @copyright 2012 Ike Devolder
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either 
+ * version 3 of the License, or any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('gallery');
+
+$img = $_GET['img'];
+
+$image = OC_Gallery_Photo::getViewImage($img);
+if ($image) {
+	OCP\Response::enableCaching(3600 * 24); // 24 hour
+	$image->show();
+}
diff --git a/apps/gallery/appinfo/version b/apps/gallery/appinfo/version
index 8f0916f768f0487bcf8d33827ce2c8dcecb645c1..4b9fcbec101a6ff8ec68e0f95131ccda4861407f 100644
--- a/apps/gallery/appinfo/version
+++ b/apps/gallery/appinfo/version
@@ -1 +1 @@
-0.5.0
+0.5.1
diff --git a/apps/gallery/css/styles.css b/apps/gallery/css/styles.css
index dcef1f08c6de4f408bdbe0ea99a21fe55468ed03..63f645662dd72ce65fe580e973310ae9e327a9b7 100644
--- a/apps/gallery/css/styles.css
+++ b/apps/gallery/css/styles.css
@@ -1,11 +1,11 @@
 #gallerycontent { margin-top: 2.8em; overflow: visible; }
-#g-settings {position: absolute; left 13.5em; top: 0;}
+#g-settings {position: absolute; left: 13.5em; top: 0;}
 div#controls input[type=button] { -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; opacity: 1; position:absolute; right:13.5em; top:0em; }
 input[type=button]:disabled { opacity: 0.5 }
 .ui-dialog tr {background-color: #eee;}
 .ui-dialog input {width: 90%;}
 
-div.gallery_div {position:relative; display: inline-block; height: 152px; width: 150px; margin: 5px;}
+div.gallery_div {position:relative; display: inline-block; height: 150px; width: 150px; margin: 5px;}
 div.miniature_border {position:absolute; height: 150px; -moz-transition-duration: 0.2s; -o-transition-duration:0.2s;  -webkit-transition-duration: .2s; background-position: 50%;}
 div.line {display:inline-block; border: 0; width: auto; height: 160px}
 div.gallery_div img{position:absolute; top: 1; left: 0; -moz-transition-duration: 0.3s; -o-transition-duration:0.3s; -webkit-transition-duration: 0.3s; height:150px; width: auto;}
diff --git a/apps/gallery/css/supersized.css b/apps/gallery/css/supersized.css
new file mode 100644
index 0000000000000000000000000000000000000000..57ee7e23a50c31f99d41d0b95e534dc5c5316ffa
--- /dev/null
+++ b/apps/gallery/css/supersized.css
@@ -0,0 +1,25 @@
+/*
+
+	Supersized - Fullscreen Slideshow jQuery Plugin
+	Version : 3.2.7
+	Site	: www.buildinternet.com/project/supersized
+	
+	Author	: Sam Dunn
+	Company : One Mighty Roar (www.onemightyroar.com)
+	License : MIT License / GPL License
+	
+*/
+#supersized-holder #supersized-loader { display:none; position:absolute; top:50%; left:50%; z-index:0; width:60px; height:60px; margin:-30px 0 0 -30px; text-indent:-999em; background:url('%appswebroot%/gallery/img/supersized/progress.gif') no-repeat center center;}
+
+#supersized-holder #supersized {  visibility:hidden; display:block; position:fixed; left:0; top:0; overflow:hidden; z-index:200; height:100%; width:100%; }
+#supersized-holder #supersized img { width:auto; height:auto; position:relative; display:none; outline:none; border:none; }
+#supersized-holder #supersized.speed img { -ms-interpolation-mode:nearest-neighbor; image-rendering: -moz-crisp-edges; }	/*Speed*/
+#supersized-holder #supersized.quality img { -ms-interpolation-mode:bicubic; image-rendering: optimizeQuality; }			/*Quality*/
+
+#supersized-holder #supersized li { display:block; list-style:none; z-index:150; position:fixed; overflow:hidden; top:0; left:0; width:100%; height:100%; background:#111; }
+#supersized-holder #supersized a { width:100%; height:100%; display:block; }
+#supersized-holder #supersized li.prevslide { z-index:160; }
+#supersized-holder #supersized li.activeslide { z-index:170; }
+#supersized-holder #supersized li.image-loading { background:#111 url('%appswebroot%/gallery/img/supersized/progress.gif') no-repeat center center; width:100%; height:100%; }
+#supersized-holder #supersized li.image-loading img{ visibility:hidden; }
+#supersized-holder #supersized li.prevslide img, #supersized-holder #supersized li.activeslide img{ display:inline; }
diff --git a/apps/gallery/css/supersized.shutter.css b/apps/gallery/css/supersized.shutter.css
new file mode 100644
index 0000000000000000000000000000000000000000..428c254c3b2fbbf2f91fdc3605af9fc55943a77b
--- /dev/null
+++ b/apps/gallery/css/supersized.shutter.css
@@ -0,0 +1,74 @@
+/*
+
+	Supersized - Fullscreen Slideshow jQuery Plugin
+	Version : 3.2.7
+	Site	: www.buildinternet.com/project/supersized
+	
+	Theme 	: Shutter 1.2
+	Author	: Sam Dunn
+	Company : One Mighty Roar (www.onemightyroar.com)
+	License : MIT License / GPL License
+	
+*/
+
+/* Controls Bar
+----------------------------*/
+#slideshow-content #slideshow-controls-wrapper { margin:0 auto; height:42px; width:100%; bottom:0px; left:0; z-index:204; background:url('%appswebroot%/gallery/img/supersized/nav-bg.png') repeat-x; position:fixed; }
+#slideshow-content #slideshow-controls { overflow:hidden; height:100%; position:relative; text-align:left; z-index:205; }
+#slideshow-content #slidecounter { float:left; color:#999; font:14px "Helvetica Neue", Helvetica, Arial, sans-serif; text-shadow:#000 0 -1px 0; margin:0px 10px 0 15px; line-height:42px; }
+#slideshow-content #slidecaption { overflow:hidden; float:left; color:#FFF; font:400 14px "Helvetica Neue", Helvetica, Arial, sans-serif; text-shadow:#000 1px 1px 2px; margin:0 20px 0 0; line-height:42px; }
+
+/*#navigation { float:right; margin:0px 20px 0 0; }*/
+#slideshow-content #play-button{ float:left; margin-top:1px;border-right:1px solid #333; background:url('%appswebroot%/gallery/img/supersized/bg-hover.png') repeat-x 0 44px; }
+#slideshow-content #play-button:hover{ background-position:0 1px; cursor:pointer; }
+
+#slideshow-content #prevslide, #nextslide{ position:fixed; height:43px; width:43px; top:50%; margin-top:-21px; opacity:0.6; z-index:204; }
+#slideshow-content #prevslide{ left:10px; background:url('%appswebroot%/gallery/img/supersized/back.png'); }
+#slideshow-content #nextslide{ right:10px; background:url('%appswebroot%/gallery/img/supersized/forward.png'); }
+#slideshow-content #prevslide:active, #nextslide:active{ margin-top:-19px; }
+#slideshow-content #prevslide:hover, #nextslide:hover{ cursor:pointer; }
+
+#slideshow-content ul#slide-list{ padding:15px 0; float:left; position:absolute; left:50%; }
+#slideshow-content ul#slide-list li{ list-style:none; width:12px; height:12px; float:left; margin:0 5px 0 0; }
+#slideshow-content ul#slide-list li.current-slide a, ul#slide-list li.current-slide a:hover{ background-position:0 0px; }
+#slideshow-content ul#slide-list li a{ display:block; width:12px; height:12px; background:url('%appswebroot%/gallery/img/supersized/nav-dot.png') no-repeat 0 -24px; }
+#slideshow-content ul#slide-list li a:hover{ background-position:0 -12px; cursor:pointer; }
+
+#slideshow-content #tray-button{ float:right; margin-top:1px; border-left:1px solid #333; background:url('%appswebroot%/gallery/img/supersized/bg-hover.png') repeat-x 0 44px; }
+#slideshow-content #tray-button:hover{ background-position:0 1px; cursor:pointer; }
+
+
+/* Progress Bar
+----------------------------*/					
+#slideshow-content #progress-back{ z-index:205; position:fixed; bottom:42px; left:0; height:8px; width:100%; background:url('%appswebroot%/gallery/img/supersized/progress-back.png') repeat-x; }
+#slideshow-content #progress-bar{ position:relative; height:8px; width:100%; background:url('%appswebroot%/gallery/img/supersized/progress-bar.png') repeat-x; }
+
+
+/* Thumbnail Navigation
+----------------------------*/	
+#slideshow-content #nextthumb, #slideshow-content #prevthumb { z-index:202; display:none; position:fixed; bottom:61px; height:75px; width:100px; overflow:hidden; background:#ddd; border:1px solid #fff; -webkit-box-shadow:0 0 5px #000; }
+#slideshow-content #nextthumb { right:12px; }
+#slideshow-content #prevthumb { left:12px; }
+#slideshow-content #nextthumb img, #slideshow-content #prevthumb img { width:150px; height:auto;  }
+#slideshow-content #nextthumb:active, #slideshow-content #prevthumb:active { bottom:59px; }
+#slideshow-content #nextthumb:hover, #slideshow-content #prevthumb:hover { cursor:pointer; }
+
+
+/* Thumbnail Tray
+----------------------------*/			
+#slideshow-content #thumb-tray{ position:fixed; z-index:203; bottom:0; left:0; background:url('%appswebroot%/gallery/img/supersized/bg-black.png'); height:150px; width:100%; overflow:hidden; text-align:center; -moz-box-shadow: 0px 0px 4px #000; -webkit-box-shadow: 0px 0px 4px #000; box-shadow: 0px 0px 4px #000; }
+
+#slideshow-content #thumb-back, #slideshow-content #thumb-forward{ position:absolute; z-index:5; bottom:42px; height:108px; width:40px; }
+#slideshow-content #thumb-back{ left:0; background: url('%appswebroot%/gallery/img/supersized/thumb-back.png') no-repeat center center;}
+#slideshow-content #thumb-forward{ right:0; background:url('%appswebroot%/gallery/img/supersized/thumb-forward.png') no-repeat center center;}
+#slideshow-content #thumb-back:hover, #slideshow-content #thumb-forward:hover{ cursor:pointer; background-color:rgba(256,256,256, 0.1); }
+#slideshow-content #thumb-back:hover{ border-right:1px solid rgba(256,256,256, 0.2); }
+#slideshow-content #thumb-forward:hover{ border-left:1px solid rgba(256,256,256, 0.2); }
+
+
+#slideshow-content ul#thumb-list{ display:inline-block; list-style:none; position:relative; left:0px; padding:0 0px; }
+#slideshow-content ul#thumb-list li{ background:#111; list-style:none; display:inline; width:150px; height:108px; overflow:hidden; float:left; margin:0; }
+#slideshow-content ul#thumb-list li img { width:200px; height:auto; opacity:0.5; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; filter:alpha(opacity=60); -webkit-transition: all 100ms ease-in-out; -moz-transition: all 100ms ease-in-out; -o-transition: all 100ms ease-in-out; -ms-transition: all 100ms ease-in-out; transition: all 100ms ease-in-out; }
+#slideshow-content ul#thumb-list li.current-thumb img, #slideshow-content ul#thumb-list li:hover img{ opacity:1; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); }
+#slideshow-content ul#thumb-list li:hover{ cursor:pointer; }
+				
diff --git a/apps/gallery/img/supersized/back.png b/apps/gallery/img/supersized/back.png
new file mode 100644
index 0000000000000000000000000000000000000000..44cd0ae703cb0810dc707f0da621708e14018fdd
Binary files /dev/null and b/apps/gallery/img/supersized/back.png differ
diff --git a/apps/gallery/img/supersized/bg-black.png b/apps/gallery/img/supersized/bg-black.png
new file mode 100644
index 0000000000000000000000000000000000000000..8c2f00140d611be9ab73598bd1ca5f313416b8ec
Binary files /dev/null and b/apps/gallery/img/supersized/bg-black.png differ
diff --git a/apps/gallery/img/supersized/bg-hover.png b/apps/gallery/img/supersized/bg-hover.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ca2022e106454b787657cecabc88aa30eb744fb
Binary files /dev/null and b/apps/gallery/img/supersized/bg-hover.png differ
diff --git a/apps/gallery/img/supersized/button-tray-down.png b/apps/gallery/img/supersized/button-tray-down.png
new file mode 100644
index 0000000000000000000000000000000000000000..99b92aef122f08800dd80bd79b92fd7d2293a692
Binary files /dev/null and b/apps/gallery/img/supersized/button-tray-down.png differ
diff --git a/apps/gallery/img/supersized/button-tray-up.png b/apps/gallery/img/supersized/button-tray-up.png
new file mode 100644
index 0000000000000000000000000000000000000000..7cc57785f0a7a309cc2959273f5eaaaa1a69ed40
Binary files /dev/null and b/apps/gallery/img/supersized/button-tray-up.png differ
diff --git a/apps/gallery/img/supersized/forward.png b/apps/gallery/img/supersized/forward.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2084ab3fafe805cc4fa302bc4d2723932fba46f
Binary files /dev/null and b/apps/gallery/img/supersized/forward.png differ
diff --git a/apps/gallery/img/supersized/nav-bg.png b/apps/gallery/img/supersized/nav-bg.png
new file mode 100644
index 0000000000000000000000000000000000000000..800f904ddc7d1f2bbd2b82d7eb20d23fdfdf406f
Binary files /dev/null and b/apps/gallery/img/supersized/nav-bg.png differ
diff --git a/apps/gallery/img/supersized/nav-dot.png b/apps/gallery/img/supersized/nav-dot.png
new file mode 100644
index 0000000000000000000000000000000000000000..a28a50789f37f6afc9abc8b1495c76cacf7a1889
Binary files /dev/null and b/apps/gallery/img/supersized/nav-dot.png differ
diff --git a/apps/gallery/img/supersized/pause.png b/apps/gallery/img/supersized/pause.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2c21a51ceeb781cb1a7ada35e9d3fe01ddb0083
Binary files /dev/null and b/apps/gallery/img/supersized/pause.png differ
diff --git a/apps/gallery/img/supersized/play.png b/apps/gallery/img/supersized/play.png
new file mode 100644
index 0000000000000000000000000000000000000000..16e53f8674546f9d1530ef1b29a8559eaa948386
Binary files /dev/null and b/apps/gallery/img/supersized/play.png differ
diff --git a/apps/gallery/img/supersized/progress-back.png b/apps/gallery/img/supersized/progress-back.png
new file mode 100644
index 0000000000000000000000000000000000000000..68cd45b6706bc9b1870d83842c89fc3423429a67
Binary files /dev/null and b/apps/gallery/img/supersized/progress-back.png differ
diff --git a/apps/gallery/img/supersized/progress-bar.png b/apps/gallery/img/supersized/progress-bar.png
new file mode 100644
index 0000000000000000000000000000000000000000..49ebb0513b78c054a2b5a48d8688f56f8b839241
Binary files /dev/null and b/apps/gallery/img/supersized/progress-bar.png differ
diff --git a/apps/gallery/img/supersized/progress.gif b/apps/gallery/img/supersized/progress.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f3e45e0569c02ae3fe7114691e5b5aba892e9ef4
Binary files /dev/null and b/apps/gallery/img/supersized/progress.gif differ
diff --git a/apps/gallery/img/supersized/supersized-logo.png b/apps/gallery/img/supersized/supersized-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1243a297840e79bc777d46e1a15b5b235d09983
Binary files /dev/null and b/apps/gallery/img/supersized/supersized-logo.png differ
diff --git a/apps/gallery/img/supersized/thumb-back.png b/apps/gallery/img/supersized/thumb-back.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c969ebd5276c73df8c1e8f4fb205731f00a0793
Binary files /dev/null and b/apps/gallery/img/supersized/thumb-back.png differ
diff --git a/apps/gallery/img/supersized/thumb-forward.png b/apps/gallery/img/supersized/thumb-forward.png
new file mode 100644
index 0000000000000000000000000000000000000000..afe451c75d0d6072a77a67f5ebbbf862dda8e3c4
Binary files /dev/null and b/apps/gallery/img/supersized/thumb-forward.png differ
diff --git a/apps/gallery/index.php b/apps/gallery/index.php
index b8aadacb47fb043f10729f551b94eebc00394fe3..ce79f8f8782a6834f248813f1180695d1c7c9c66 100644
--- a/apps/gallery/index.php
+++ b/apps/gallery/index.php
@@ -30,8 +30,14 @@ OCP\App::setActiveNavigationEntry( 'gallery_index' );
 OCP\Util::addStyle('files', 'files');
 OCP\Util::addStyle('gallery', 'styles');
 OCP\Util::addScript('gallery', 'pictures');
+OCP\Util::addStyle( 'gallery', 'supersized' );
+OCP\Util::addStyle( 'gallery', 'supersized.shutter' );
+OCP\Util::addScript('gallery', 'slideshow');
+OCP\Util::addScript('gallery', 'jquery.easing.min');
+OCP\Util::addScript('gallery', 'supersized.3.2.7.min');
+OCP\Util::addScript('gallery', 'supersized.shutter.min');
 
-include('gallery/lib/tiles.php');
+include 'gallery/lib/tiles.php';
 
 $root = !empty($_GET['root']) ? $_GET['root'] : '/';
 $images = \OC_FileCache::searchByMime('image', null, '/'.\OCP\USER::getUser().'/files'.$root);
@@ -97,4 +103,3 @@ $tmpl = new OCP\Template( 'gallery', 'index', 'user' );
 $tmpl->assign('root', $root, false);
 $tmpl->assign('tl', $tl, false);
 $tmpl->printPage();
-?>
diff --git a/apps/gallery/js/albums.js b/apps/gallery/js/albums.js
index 413c71471a34bebe1f7639598d625b7dac54adbb..62d3f783eceded951993c3499e02b1ee7631cdbb 100644
--- a/apps/gallery/js/albums.js
+++ b/apps/gallery/js/albums.js
@@ -79,7 +79,7 @@ Albums={
 			});
 			element.append(local);
 		}
-		var photoDisplayTemplate = '<div class="gallery_box"><div class="dummy"></div><div><a rel="images" href="'+OC.linkTo('files','download.php')+'?file=URLPATH"><img src="'+OC.filePath('gallery','ajax','thumbnail.php')+'?img=IMGPATH"></a></div></div>';
+        var photoDisplayTemplate = '<div class="gallery_box"><div class="dummy"></div><div><a rel="images" href="'+OC.linkTo('gallery/ajax','viewImage.php')+'?img=URLPATH"><img src="'+OC.filePath('gallery','ajax','thumbnail.php')+'?img=IMGPATH"></a></div></div>';
 		for (var i in Albums.photos) {
 			element.append(photoDisplayTemplate.replace("IMGPATH", escape(Albums.photos[i])).replace("URLPATH", escape(Albums.photos[i])));
 		}
diff --git a/apps/gallery/js/jquery.easing.min.js b/apps/gallery/js/jquery.easing.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..bbf8410391a14765b87d78ab9435bfacb799c079
--- /dev/null
+++ b/apps/gallery/js/jquery.easing.min.js
@@ -0,0 +1,71 @@
+/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - jQuery Easing
+ * 
+ * Open source under the BSD License. 
+ * 
+ * Copyright � 2008 George McGinley Smith
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this list of 
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list 
+ * of conditions and the following disclaimer in the documentation and/or other materials 
+ * provided with the distribution.
+ * 
+ * Neither the name of the author nor the names of contributors may be used to endorse 
+ * or promote products derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
+ * OF THE POSSIBILITY OF SUCH DAMAGE. 
+ *
+*/
+
+jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}});
+
+/*
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ * 
+ * Open source under the BSD License. 
+ * 
+ * Copyright � 2001 Robert Penner
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this list of 
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list 
+ * of conditions and the following disclaimer in the documentation and/or other materials 
+ * provided with the distribution.
+ * 
+ * Neither the name of the author nor the names of contributors may be used to endorse 
+ * or promote products derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
+ * OF THE POSSIBILITY OF SUCH DAMAGE. 
+ *
+ */
\ No newline at end of file
diff --git a/apps/gallery/js/slideshow.js b/apps/gallery/js/slideshow.js
new file mode 100644
index 0000000000000000000000000000000000000000..cc5dfc44a266d64083559ee781de53f134ef9841
--- /dev/null
+++ b/apps/gallery/js/slideshow.js
@@ -0,0 +1,58 @@
+$(document).ready(function(){
+        
+	$.endSlideshow = function () {
+		if($.supersized.vars.slideshow_interval){
+			clearInterval($.supersized.vars.slideshow_interval);
+		};
+
+		$('#supersized-holder').remove();
+		$('#slideshow-content').hide();
+		$('#thumb-list').remove();
+	}
+        
+	// add slideshow in holder div
+	$('#slideshow input.start').click(function(){
+
+		var images=[];
+		$('#gallerycontent div a').each(function(i,a){
+			images.push({image : a.href, title : a.title.replace(/</, '&lt;').replace(/>/, '&gt;'), thumb : a.children[0].src, url : 'javascript:$.endSlideshow()'});
+		});
+
+		if (images.length <= 0) {
+			return;
+		}
+
+		$('body').append("<div id='supersized-holder'></div>");
+		$('#supersized-loader').remove();
+		$('#supersized').remove();
+		$('#supersized-holder').append("<div id='supersized-loader'></div><ul id='supersized'></ul>");
+		$('#supersized').show();
+		$('#slideshow-content').show();
+
+
+		jQuery(function($){
+
+			$.supersized({
+
+				// Functionality
+				slide_interval      :   3000,		// Length between transitions
+				transition          :   1, 		// 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left
+				transition_speed    :   700,		// Speed of transition
+
+				// Components							
+				slide_links         :   'blank',	// Individual links for each slide (Options: false, 'num', 'name', 'blank')
+				slides              :   images		// Slideshow Images
+							    
+			});
+		});
+
+	});
+
+	//close slideshow on esc and remove holder
+	$(document).keyup(function(e) {
+		if (e.keyCode == 27) { // esc
+			$.endSlideshow();
+		}
+	});
+
+});
diff --git a/apps/gallery/js/supersized.3.2.7.js b/apps/gallery/js/supersized.3.2.7.js
new file mode 100644
index 0000000000000000000000000000000000000000..f5a1c0bbc2d8fcf3eb1cf5cb40a5e6bdc42645a0
--- /dev/null
+++ b/apps/gallery/js/supersized.3.2.7.js
@@ -0,0 +1,930 @@
+/*
+
+	Supersized - Fullscreen Slideshow jQuery Plugin
+	Version : 3.2.7
+	Site	: www.buildinternet.com/project/supersized
+	
+	Author	: Sam Dunn
+	Company : One Mighty Roar (www.onemightyroar.com)
+	License : MIT License / GPL License
+	
+*/
+
+(function($){
+
+	/* Place Supersized Elements
+	----------------------------*/
+	$(document).ready(function() {
+		$('body').append('<div id="supersized-loader"></div><ul id="supersized"></ul>');
+	});
+    
+    
+    $.supersized = function(options){
+    	
+    	/* Variables
+		----------------------------*/
+    	var el = '#supersized',
+        	base = this;
+        // Access to jQuery and DOM versions of element
+        base.$el = $(el);
+        base.el = el;
+        vars = $.supersized.vars;
+        // Add a reverse reference to the DOM object
+        base.$el.data("supersized", base);
+        api = base.$el.data('supersized');
+		
+		base.init = function(){
+        	// Combine options and vars
+        	$.supersized.vars = $.extend($.supersized.vars, $.supersized.themeVars);
+        	$.supersized.vars.options = $.extend({},$.supersized.defaultOptions, $.supersized.themeOptions, options);
+            base.options = $.supersized.vars.options;
+            
+            base._build();
+        };
+        
+        
+        /* Build Elements
+		----------------------------*/
+        base._build = function(){
+        	// Add in slide markers
+        	var thisSlide = 0,
+        		slideSet = '',
+				markers = '',
+				markerContent,
+				thumbMarkers = '',
+				thumbImage;
+				
+			while(thisSlide <= base.options.slides.length-1){
+				//Determine slide link content
+				switch(base.options.slide_links){
+					case 'num':
+						markerContent = thisSlide;
+						break;
+					case 'name':
+						markerContent = base.options.slides[thisSlide].title;
+						break;
+					case 'blank':
+						markerContent = '';
+						break;
+				}
+				
+				slideSet = slideSet+'<li class="slide-'+thisSlide+'"></li>';
+				
+				if(thisSlide == base.options.start_slide-1){
+					// Slide links
+					if (base.options.slide_links)markers = markers+'<li class="slide-link-'+thisSlide+' current-slide"><a>'+markerContent+'</a></li>';
+					// Slide Thumbnail Links
+					if (base.options.thumb_links){
+						base.options.slides[thisSlide].thumb ? thumbImage = base.options.slides[thisSlide].thumb : thumbImage = base.options.slides[thisSlide].image;
+						thumbMarkers = thumbMarkers+'<li class="thumb'+thisSlide+' current-thumb"><img src="'+thumbImage+'"/></li>';
+					};
+				}else{
+					// Slide links
+					if (base.options.slide_links) markers = markers+'<li class="slide-link-'+thisSlide+'" ><a>'+markerContent+'</a></li>';
+					// Slide Thumbnail Links
+					if (base.options.thumb_links){
+						base.options.slides[thisSlide].thumb ? thumbImage = base.options.slides[thisSlide].thumb : thumbImage = base.options.slides[thisSlide].image;
+						thumbMarkers = thumbMarkers+'<li class="thumb'+thisSlide+'"><img src="'+thumbImage+'"/></li>';
+					};
+				}
+				thisSlide++;
+			}
+			
+			if (base.options.slide_links) $(vars.slide_list).html(markers);
+			if (base.options.thumb_links && vars.thumb_tray.length){
+				$(vars.thumb_tray).append('<ul id="'+vars.thumb_list.replace('#','')+'">'+thumbMarkers+'</ul>');
+			}
+			
+			$(base.el).append(slideSet);
+			
+			// Add in thumbnails
+			if (base.options.thumbnail_navigation){
+				// Load previous thumbnail
+				vars.current_slide - 1 < 0  ? prevThumb = base.options.slides.length - 1 : prevThumb = vars.current_slide - 1;
+				$(vars.prev_thumb).show().html($("<img/>").attr("src", base.options.slides[prevThumb].image));
+				
+				// Load next thumbnail
+				vars.current_slide == base.options.slides.length - 1 ? nextThumb = 0 : nextThumb = vars.current_slide + 1;
+				$(vars.next_thumb).show().html($("<img/>").attr("src", base.options.slides[nextThumb].image));
+			}
+			
+            base._start(); // Get things started
+        };
+        
+        
+        /* Initialize
+		----------------------------*/
+    	base._start = function(){
+			
+			// Determine if starting slide random
+			if (base.options.start_slide){
+				vars.current_slide = base.options.start_slide - 1;
+			}else{
+				vars.current_slide = Math.floor(Math.random()*base.options.slides.length);	// Generate random slide number
+			}
+			
+			// If links should open in new window
+			var linkTarget = base.options.new_window ? ' target="_blank"' : '';
+			
+			// Set slideshow quality (Supported only in FF and IE, no Webkit)
+			if (base.options.performance == 3){
+				base.$el.addClass('speed'); 		// Faster transitions
+			} else if ((base.options.performance == 1) || (base.options.performance == 2)){
+				base.$el.addClass('quality');	// Higher image quality
+			}
+						
+			// Shuffle slide order if needed		
+			if (base.options.random){
+				arr = base.options.slides;
+				for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x);	// Fisher-Yates shuffle algorithm (jsfromhell.com/array/shuffle)
+			    base.options.slides = arr;
+			}
+			
+			/*-----Load initial set of images-----*/
+	
+			if (base.options.slides.length > 1){
+				if(base.options.slides.length > 2){
+					// Set previous image
+					vars.current_slide - 1 < 0  ? loadPrev = base.options.slides.length - 1 : loadPrev = vars.current_slide - 1;	// If slide is 1, load last slide as previous
+					var imageLink = (base.options.slides[loadPrev].url) ? "href='" + base.options.slides[loadPrev].url + "'" : "";
+				
+					var imgPrev = $('<img src="'+base.options.slides[loadPrev].image+'"/>');
+					var slidePrev = base.el+' li:eq('+loadPrev+')';
+					imgPrev.appendTo(slidePrev).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading prevslide');
+				
+					imgPrev.load(function(){
+						$(this).data('origWidth', $(this).width()).data('origHeight', $(this).height());
+						base.resizeNow();	// Resize background image
+					});	// End Load
+				}
+			} else {
+				// Slideshow turned off if there is only one slide
+				base.options.slideshow = 0;
+			}
+			
+			// Set current image
+			imageLink = (api.getField('url')) ? "href='" + api.getField('url') + "'" : "";
+			var img = $('<img src="'+api.getField('image')+'"/>');
+			
+			var slideCurrent= base.el+' li:eq('+vars.current_slide+')';
+			img.appendTo(slideCurrent).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading activeslide');
+			
+			img.load(function(){
+				base._origDim($(this));
+				base.resizeNow();	// Resize background image
+				base.launch();
+				if( typeof theme != 'undefined' && typeof theme._init == "function" ) theme._init();	// Load Theme
+			});
+			
+			if (base.options.slides.length > 1){
+				// Set next image
+				vars.current_slide == base.options.slides.length - 1 ? loadNext = 0 : loadNext = vars.current_slide + 1;	// If slide is last, load first slide as next
+				imageLink = (base.options.slides[loadNext].url) ? "href='" + base.options.slides[loadNext].url + "'" : "";
+				
+				var imgNext = $('<img src="'+base.options.slides[loadNext].image+'"/>');
+				var slideNext = base.el+' li:eq('+loadNext+')';
+				imgNext.appendTo(slideNext).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading');
+				
+				imgNext.load(function(){
+					$(this).data('origWidth', $(this).width()).data('origHeight', $(this).height());
+					base.resizeNow();	// Resize background image
+				});	// End Load
+			}
+			/*-----End load initial images-----*/
+			
+			//  Hide elements to be faded in
+			base.$el.css('visibility','hidden');
+			$('.load-item').hide();
+			
+    	};
+		
+		
+		/* Launch Supersized
+		----------------------------*/
+		base.launch = function(){
+		
+			base.$el.css('visibility','visible');
+			$('#supersized-loader').remove();		//Hide loading animation
+			
+			// Call theme function for before slide transition
+			if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('next');
+			$('.load-item').show();
+			
+			// Keyboard Navigation
+			if (base.options.keyboard_nav){
+				$(document.documentElement).keyup(function (event) {
+				
+					if(vars.in_animation) return false;		// Abort if currently animating
+					
+					// Left Arrow or Down Arrow
+					if ((event.keyCode == 37) || (event.keyCode == 40)) {
+						clearInterval(vars.slideshow_interval);	// Stop slideshow, prevent buildup
+						base.prevSlide();
+					
+					// Right Arrow or Up Arrow
+					} else if ((event.keyCode == 39) || (event.keyCode == 38)) {
+						clearInterval(vars.slideshow_interval);	// Stop slideshow, prevent buildup
+						base.nextSlide();
+					
+					// Spacebar	
+					} else if (event.keyCode == 32 && !vars.hover_pause) {
+						clearInterval(vars.slideshow_interval);	// Stop slideshow, prevent buildup
+						base.playToggle();
+					}
+				
+				});
+			}
+			
+			// Pause when hover on image
+			if (base.options.slideshow && base.options.pause_hover){
+				$(base.el).hover(function() {
+					if(vars.in_animation) return false;		// Abort if currently animating
+			   			vars.hover_pause = true;	// Mark slideshow paused from hover
+			   			if(!vars.is_paused){
+			   				vars.hover_pause = 'resume';	// It needs to resume afterwards
+			   				base.playToggle();
+			   			}
+			   	}, function() {
+					if(vars.hover_pause == 'resume'){
+						base.playToggle();
+						vars.hover_pause = false;
+					}
+			   	});
+			}
+			
+			if (base.options.slide_links){
+				// Slide marker clicked
+				$(vars.slide_list+'> li').click(function(){
+				
+					index = $(vars.slide_list+'> li').index(this);
+					targetSlide = index + 1;
+					
+					base.goTo(targetSlide);
+					return false;
+					
+				});
+			}
+			
+			// Thumb marker clicked
+			if (base.options.thumb_links){
+				$(vars.thumb_list+'> li').click(function(){
+				
+					index = $(vars.thumb_list+'> li').index(this);
+					targetSlide = index + 1;
+					
+					api.goTo(targetSlide);
+					return false;
+					
+				});
+			}
+			
+			// Start slideshow if enabled
+			if (base.options.slideshow && base.options.slides.length > 1){
+	    		
+	    		// Start slideshow if autoplay enabled
+	    		if (base.options.autoplay && base.options.slides.length > 1){
+	    			vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval);	// Initiate slide interval
+				}else{
+					vars.is_paused = true;	// Mark as paused
+				}
+				
+				//Prevent navigation items from being dragged					
+				$('.load-item img').bind("contextmenu mousedown",function(){
+					return false;
+				});
+								
+			}
+			
+			// Adjust image when browser is resized
+			$(window).resize(function(){
+	    		base.resizeNow();
+			});
+    		
+    	};
+        
+        
+        /* Resize Images
+		----------------------------*/
+		base.resizeNow = function(){
+			
+			return base.$el.each(function() {
+		  		//  Resize each image seperately
+		  		$('img', base.el).each(function(){
+		  			
+					thisSlide = $(this);
+					var ratio = (thisSlide.data('origHeight')/thisSlide.data('origWidth')).toFixed(2);	// Define image ratio
+					
+					// Gather browser size
+					var browserwidth = base.$el.width(),
+						browserheight = base.$el.height(),
+						offset;
+					
+					/*-----Resize Image-----*/
+					if (base.options.fit_always){	// Fit always is enabled
+						if ((browserheight/browserwidth) > ratio){
+							resizeWidth();
+						} else {
+							resizeHeight();
+						}
+					}else{	// Normal Resize
+						if ((browserheight <= base.options.min_height) && (browserwidth <= base.options.min_width)){	// If window smaller than minimum width and height
+						
+							if ((browserheight/browserwidth) > ratio){
+								base.options.fit_landscape && ratio < 1 ? resizeWidth(true) : resizeHeight(true);	// If landscapes are set to fit
+							} else {
+								base.options.fit_portrait && ratio >= 1 ? resizeHeight(true) : resizeWidth(true);		// If portraits are set to fit
+							}
+						
+						} else if (browserwidth <= base.options.min_width){		// If window only smaller than minimum width
+						
+							if ((browserheight/browserwidth) > ratio){
+								base.options.fit_landscape && ratio < 1 ? resizeWidth(true) : resizeHeight();	// If landscapes are set to fit
+							} else {
+								base.options.fit_portrait && ratio >= 1 ? resizeHeight() : resizeWidth(true);		// If portraits are set to fit
+							}
+							
+						} else if (browserheight <= base.options.min_height){	// If window only smaller than minimum height
+						
+							if ((browserheight/browserwidth) > ratio){
+								base.options.fit_landscape && ratio < 1 ? resizeWidth() : resizeHeight(true);	// If landscapes are set to fit
+							} else {
+								base.options.fit_portrait && ratio >= 1 ? resizeHeight(true) : resizeWidth();		// If portraits are set to fit
+							}
+						
+						} else {	// If larger than minimums
+							
+							if ((browserheight/browserwidth) > ratio){
+								base.options.fit_landscape && ratio < 1 ? resizeWidth() : resizeHeight();	// If landscapes are set to fit
+							} else {
+								base.options.fit_portrait && ratio >= 1 ? resizeHeight() : resizeWidth();		// If portraits are set to fit
+							}
+							
+						}
+					}
+					/*-----End Image Resize-----*/
+					
+					
+					/*-----Resize Functions-----*/
+					
+					function resizeWidth(minimum){
+						if (minimum){	// If minimum height needs to be considered
+							if(thisSlide.width() < browserwidth || thisSlide.width() < base.options.min_width ){
+								if (thisSlide.width() * ratio >= base.options.min_height){
+									thisSlide.width(base.options.min_width);
+						    		thisSlide.height(thisSlide.width() * ratio);
+						    	}else{
+						    		resizeHeight();
+						    	}
+						    }
+						}else{
+							if (base.options.min_height >= browserheight && !base.options.fit_landscape){	// If minimum height needs to be considered
+								if (browserwidth * ratio >= base.options.min_height || (browserwidth * ratio >= base.options.min_height && ratio <= 1)){	// If resizing would push below minimum height or image is a landscape
+									thisSlide.width(browserwidth);
+									thisSlide.height(browserwidth * ratio);
+								} else if (ratio > 1){		// Else the image is portrait
+									thisSlide.height(base.options.min_height);
+									thisSlide.width(thisSlide.height() / ratio);
+								} else if (thisSlide.width() < browserwidth) {
+									thisSlide.width(browserwidth);
+						    		thisSlide.height(thisSlide.width() * ratio);
+								}
+							}else{	// Otherwise, resize as normal
+								thisSlide.width(browserwidth);
+								thisSlide.height(browserwidth * ratio);
+							}
+						}
+					};
+					
+					function resizeHeight(minimum){
+						if (minimum){	// If minimum height needs to be considered
+							if(thisSlide.height() < browserheight){
+								if (thisSlide.height() / ratio >= base.options.min_width){
+									thisSlide.height(base.options.min_height);
+									thisSlide.width(thisSlide.height() / ratio);
+								}else{
+									resizeWidth(true);
+								}
+							}
+						}else{	// Otherwise, resized as normal
+							if (base.options.min_width >= browserwidth){	// If minimum width needs to be considered
+								if (browserheight / ratio >= base.options.min_width || ratio > 1){	// If resizing would push below minimum width or image is a portrait
+									thisSlide.height(browserheight);
+									thisSlide.width(browserheight / ratio);
+								} else if (ratio <= 1){		// Else the image is landscape
+									thisSlide.width(base.options.min_width);
+						    		thisSlide.height(thisSlide.width() * ratio);
+								}
+							}else{	// Otherwise, resize as normal
+								thisSlide.height(browserheight);
+								thisSlide.width(browserheight / ratio);
+							}
+						}
+					};
+					
+					/*-----End Resize Functions-----*/
+					
+					if (thisSlide.parents('li').hasClass('image-loading')){
+						$('.image-loading').removeClass('image-loading');
+					}
+					
+					// Horizontally Center
+					if (base.options.horizontal_center){
+						$(this).css('left', (browserwidth - $(this).width())/2);
+					}
+					
+					// Vertically Center
+					if (base.options.vertical_center){
+						$(this).css('top', (browserheight - $(this).height())/2);
+					}
+					
+				});
+				
+				// Basic image drag and right click protection
+				if (base.options.image_protect){
+					
+					$('img', base.el).bind("contextmenu mousedown",function(){
+						return false;
+					});
+				
+				}
+				
+				return false;
+				
+			});
+			
+		};
+        
+        
+        /* Next Slide
+		----------------------------*/
+		base.nextSlide = function(){
+			
+			if(vars.in_animation || !api.options.slideshow) return false;		// Abort if currently animating
+				else vars.in_animation = true;		// Otherwise set animation marker
+				
+		    clearInterval(vars.slideshow_interval);	// Stop slideshow
+		    
+		    var slides = base.options.slides,					// Pull in slides array
+				liveslide = base.$el.find('.activeslide');		// Find active slide
+				$('.prevslide').removeClass('prevslide');
+				liveslide.removeClass('activeslide').addClass('prevslide');	// Remove active class & update previous slide
+					
+			// Get the slide number of new slide
+			vars.current_slide + 1 == base.options.slides.length ? vars.current_slide = 0 : vars.current_slide++;
+			
+		    var nextslide = $(base.el+' li:eq('+vars.current_slide+')'),
+		    	prevslide = base.$el.find('.prevslide');
+			
+			// If hybrid mode is on drop quality for transition
+			if (base.options.performance == 1) base.$el.removeClass('quality').addClass('speed');	
+			
+			
+			/*-----Load Image-----*/
+			
+			loadSlide = false;
+
+			vars.current_slide == base.options.slides.length - 1 ? loadSlide = 0 : loadSlide = vars.current_slide + 1;	// Determine next slide
+
+			var targetList = base.el+' li:eq('+loadSlide+')';
+			if (!$(targetList).html()){
+				
+				// If links should open in new window
+				var linkTarget = base.options.new_window ? ' target="_blank"' : '';
+				
+				imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : "";	// If link exists, build it
+				var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); 
+				
+				img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden');
+				
+				img.load(function(){
+					base._origDim($(this));
+					base.resizeNow();
+				});	// End Load
+			};
+						
+			// Update thumbnails (if enabled)
+			if (base.options.thumbnail_navigation == 1){
+			
+				// Load previous thumbnail
+				vars.current_slide - 1 < 0  ? prevThumb = base.options.slides.length - 1 : prevThumb = vars.current_slide - 1;
+				$(vars.prev_thumb).html($("<img/>").attr("src", base.options.slides[prevThumb].image));
+			
+				// Load next thumbnail
+				nextThumb = loadSlide;
+				$(vars.next_thumb).html($("<img/>").attr("src", base.options.slides[nextThumb].image));
+				
+			}
+			
+			
+			
+			/*-----End Load Image-----*/
+			
+			
+			// Call theme function for before slide transition
+			if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('next');
+			
+			//Update slide markers
+			if (base.options.slide_links){
+				$('.current-slide').removeClass('current-slide');
+				$(vars.slide_list +'> li' ).eq(vars.current_slide).addClass('current-slide');
+			}
+		    
+		    nextslide.css('visibility','hidden').addClass('activeslide');	// Update active slide
+		    
+	    	switch(base.options.transition){
+	    		case 0: case 'none':	// No transition
+	    		    nextslide.css('visibility','visible'); vars.in_animation = false; base.afterAnimation();
+	    		    break;
+	    		case 1: case 'fade':	// Fade
+	    		    nextslide.animate({opacity : 0},0).css('visibility','visible').animate({opacity : 1, avoidTransforms : false}, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    		    break;
+	    		case 2: case 'slideTop':	// Slide Top
+	    		    nextslide.animate({top : -base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    		    break;
+	    		case 3: case 'slideRight':	// Slide Right
+	    			nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    			break;
+	    		case 4: case 'slideBottom': // Slide Bottom
+	    			nextslide.animate({top : base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    			break;
+	    		case 5: case 'slideLeft':  // Slide Left
+	    			nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    			break;
+	    		case 6: case 'carouselRight':	// Carousel Right
+	    			nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+					liveslide.animate({ left: -base.$el.width(), avoidTransforms : false }, base.options.transition_speed );
+	    			break;
+	    		case 7: case 'carouselLeft':   // Carousel Left
+	    			nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+					liveslide.animate({ left: base.$el.width(), avoidTransforms : false }, base.options.transition_speed );
+	    			break;
+	    	}
+		    return false;	
+		};
+		
+		
+		/* Previous Slide
+		----------------------------*/
+		base.prevSlide = function(){
+		
+			if(vars.in_animation || !api.options.slideshow) return false;		// Abort if currently animating
+				else vars.in_animation = true;		// Otherwise set animation marker
+			
+			clearInterval(vars.slideshow_interval);	// Stop slideshow
+			
+			var slides = base.options.slides,					// Pull in slides array
+				liveslide = base.$el.find('.activeslide');		// Find active slide
+				$('.prevslide').removeClass('prevslide');
+				liveslide.removeClass('activeslide').addClass('prevslide');		// Remove active class & update previous slide
+			
+			// Get current slide number
+			vars.current_slide == 0 ?  vars.current_slide = base.options.slides.length - 1 : vars.current_slide-- ;
+				
+		    var nextslide =  $(base.el+' li:eq('+vars.current_slide+')'),
+		    	prevslide =  base.$el.find('.prevslide');
+			
+			// If hybrid mode is on drop quality for transition
+			if (base.options.performance == 1) base.$el.removeClass('quality').addClass('speed');	
+			
+			
+			/*-----Load Image-----*/
+			
+			loadSlide = vars.current_slide;
+			
+			var targetList = base.el+' li:eq('+loadSlide+')';
+			if (!$(targetList).html()){
+				// If links should open in new window
+				var linkTarget = base.options.new_window ? ' target="_blank"' : '';
+				imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : "";	// If link exists, build it
+				var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); 
+				
+				img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden');
+				
+				img.load(function(){
+					base._origDim($(this));
+					base.resizeNow();
+				});	// End Load
+			};
+			
+			// Update thumbnails (if enabled)
+			if (base.options.thumbnail_navigation == 1){
+			
+				// Load previous thumbnail
+				//prevThumb = loadSlide;
+				loadSlide == 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = loadSlide - 1;
+				$(vars.prev_thumb).html($("<img/>").attr("src", base.options.slides[prevThumb].image));
+				
+				// Load next thumbnail
+				vars.current_slide == base.options.slides.length - 1 ? nextThumb = 0 : nextThumb = vars.current_slide + 1;
+				$(vars.next_thumb).html($("<img/>").attr("src", base.options.slides[nextThumb].image));
+			}
+			
+			/*-----End Load Image-----*/
+			
+			
+			// Call theme function for before slide transition
+			if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('prev');
+			
+			//Update slide markers
+			if (base.options.slide_links){
+				$('.current-slide').removeClass('current-slide');
+				$(vars.slide_list +'> li' ).eq(vars.current_slide).addClass('current-slide');
+			}
+			
+		    nextslide.css('visibility','hidden').addClass('activeslide');	// Update active slide
+		    
+		    switch(base.options.transition){
+	    		case 0: case 'none':	// No transition
+	    		    nextslide.css('visibility','visible'); vars.in_animation = false; base.afterAnimation();
+	    		    break;
+	    		case 1: case 'fade':	// Fade
+	    		  	nextslide.animate({opacity : 0},0).css('visibility','visible').animate({opacity : 1, avoidTransforms : false}, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    		    break;
+	    		case 2: case 'slideTop':	// Slide Top (reverse)
+	    		    nextslide.animate({top : base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    		    break;
+	    		case 3: case 'slideRight':	// Slide Right (reverse)
+	    			nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    			break;
+	    		case 4: case 'slideBottom': // Slide Bottom (reverse)
+	    			nextslide.animate({top : -base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    			break;
+	    		case 5: case 'slideLeft':  // Slide Left (reverse)
+	    			nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+	    			break;
+	    		case 6: case 'carouselRight':	// Carousel Right (reverse)
+	    			nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+					liveslide.animate({left : 0}, 0 ).animate({ left: base.$el.width(), avoidTransforms : false}, base.options.transition_speed );
+	    			break;
+	    		case 7: case 'carouselLeft':   // Carousel Left (reverse)
+	    			nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); });
+					liveslide.animate({left : 0}, 0 ).animate({ left: -base.$el.width(), avoidTransforms : false }, base.options.transition_speed );
+	    			break;
+	    	}
+		    return false;	
+		};
+		
+		
+		/* Play/Pause Toggle
+		----------------------------*/
+		base.playToggle = function(){
+		
+			if (vars.in_animation || !api.options.slideshow) return false;		// Abort if currently animating
+			
+			if (vars.is_paused){
+				
+				vars.is_paused = false;
+				
+				// Call theme function for play
+				if( typeof theme != 'undefined' && typeof theme.playToggle == "function" ) theme.playToggle('play');
+				
+				// Resume slideshow
+	        	vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval);
+	        	  
+        	}else{
+        		
+        		vars.is_paused = true;
+        		
+        		// Call theme function for pause
+        		if( typeof theme != 'undefined' && typeof theme.playToggle == "function" ) theme.playToggle('pause');
+        		
+        		// Stop slideshow
+        		clearInterval(vars.slideshow_interval);	
+       		
+       		}
+		    
+		    return false;
+    		
+    	};
+    	
+    	
+    	/* Go to specific slide
+		----------------------------*/
+    	base.goTo = function(targetSlide){
+			if (vars.in_animation || !api.options.slideshow) return false;		// Abort if currently animating
+			
+			var totalSlides = base.options.slides.length;
+			
+			// If target outside range
+			if(targetSlide < 0){
+				targetSlide = totalSlides;
+			}else if(targetSlide > totalSlides){
+				targetSlide = 1;
+			}
+			targetSlide = totalSlides - targetSlide + 1;
+			
+			clearInterval(vars.slideshow_interval);	// Stop slideshow, prevent buildup
+			
+			// Call theme function for goTo trigger
+			if (typeof theme != 'undefined' && typeof theme.goTo == "function" ) theme.goTo();
+			
+			if (vars.current_slide == totalSlides - targetSlide){
+				if(!(vars.is_paused)){
+					vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval);
+				} 
+				return false;
+			}
+			
+			// If ahead of current position
+			if(totalSlides - targetSlide > vars.current_slide ){
+				
+				// Adjust for new next slide
+				vars.current_slide = totalSlides-targetSlide-1;
+				vars.update_images = 'next';
+				base._placeSlide(vars.update_images);
+				
+			//Otherwise it's before current position
+			}else if(totalSlides - targetSlide < vars.current_slide){
+				
+				// Adjust for new prev slide
+				vars.current_slide = totalSlides-targetSlide+1;
+				vars.update_images = 'prev';
+			    base._placeSlide(vars.update_images);
+			    
+			}
+			
+			// set active markers
+			if (base.options.slide_links){
+				$(vars.slide_list +'> .current-slide').removeClass('current-slide');
+				$(vars.slide_list +'> li').eq((totalSlides-targetSlide)).addClass('current-slide');
+			}
+			
+			if (base.options.thumb_links){
+				$(vars.thumb_list +'> .current-thumb').removeClass('current-thumb');
+				$(vars.thumb_list +'> li').eq((totalSlides-targetSlide)).addClass('current-thumb');
+			}
+			
+		};
+        
+        
+        /* Place Slide
+		----------------------------*/
+        base._placeSlide = function(place){
+    			
+			// If links should open in new window
+			var linkTarget = base.options.new_window ? ' target="_blank"' : '';
+			
+			loadSlide = false;
+			
+			if (place == 'next'){
+				
+				vars.current_slide == base.options.slides.length - 1 ? loadSlide = 0 : loadSlide = vars.current_slide + 1;	// Determine next slide
+				
+				var targetList = base.el+' li:eq('+loadSlide+')';
+				
+				if (!$(targetList).html()){
+					// If links should open in new window
+					var linkTarget = base.options.new_window ? ' target="_blank"' : '';
+					
+					imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : "";	// If link exists, build it
+					var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); 
+					
+					img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden');
+					
+					img.load(function(){
+						base._origDim($(this));
+						base.resizeNow();
+					});	// End Load
+				};
+				
+				base.nextSlide();
+				
+			}else if (place == 'prev'){
+			
+				vars.current_slide - 1 < 0  ? loadSlide = base.options.slides.length - 1 : loadSlide = vars.current_slide - 1;	// Determine next slide
+				
+				var targetList = base.el+' li:eq('+loadSlide+')';
+				
+				if (!$(targetList).html()){
+					// If links should open in new window
+					var linkTarget = base.options.new_window ? ' target="_blank"' : '';
+					
+					imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : "";	// If link exists, build it
+					var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); 
+					
+					img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden');
+					
+					img.load(function(){
+						base._origDim($(this));
+						base.resizeNow();
+					});	// End Load
+				};
+				base.prevSlide();
+			}
+			
+		};
+		
+		
+		/* Get Original Dimensions
+		----------------------------*/
+		base._origDim = function(targetSlide){
+			targetSlide.data('origWidth', targetSlide.width()).data('origHeight', targetSlide.height());
+		};
+		
+		
+		/* After Slide Animation
+		----------------------------*/
+		base.afterAnimation = function(){
+			
+			// If hybrid mode is on swap back to higher image quality
+			if (base.options.performance == 1){
+		    	base.$el.removeClass('speed').addClass('quality');
+			}
+			
+			// Update previous slide
+			if (vars.update_images){
+				vars.current_slide - 1 < 0  ? setPrev = base.options.slides.length - 1 : setPrev = vars.current_slide-1;
+				vars.update_images = false;
+				$('.prevslide').removeClass('prevslide');
+				$(base.el+' li:eq('+setPrev+')').addClass('prevslide');
+			}
+			
+			vars.in_animation = false;
+			
+			// Resume slideshow
+			if (!vars.is_paused && base.options.slideshow){
+				vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval);
+				if (base.options.stop_loop && vars.current_slide == base.options.slides.length - 1 ) base.playToggle();
+			}
+			
+			// Call theme function for after slide transition
+			if (typeof theme != 'undefined' && typeof theme.afterAnimation == "function" ) theme.afterAnimation();
+			
+			return false;
+		
+		};
+		
+		base.getField = function(field){
+			return base.options.slides[vars.current_slide][field];
+		};
+		
+        // Make it go!
+        base.init();
+	};
+	
+	
+	/* Global Variables
+	----------------------------*/
+	$.supersized.vars = {
+	
+		// Elements							
+		thumb_tray			:	'#thumb-tray',	// Thumbnail tray
+		thumb_list			:	'#thumb-list',	// Thumbnail list
+		slide_list          :   '#slide-list',	// Slide link list
+		
+		// Internal variables
+		current_slide			:	0,			// Current slide number
+		in_animation 			:	false,		// Prevents animations from stacking
+		is_paused 				: 	false,		// Tracks paused on/off
+		hover_pause				:	false,		// If slideshow is paused from hover
+		slideshow_interval		:	false,		// Stores slideshow timer					
+		update_images 			: 	false,		// Trigger to update images after slide jump
+		options					:	{}			// Stores assembled options list
+		
+	};
+	
+	
+	/* Default Options
+	----------------------------*/
+	$.supersized.defaultOptions = {
+    
+    	// Functionality
+		slideshow               :   1,			// Slideshow on/off
+		autoplay				:	1,			// Slideshow starts playing automatically
+		start_slide             :   1,			// Start slide (0 is random)
+		stop_loop				:	0,			// Stops slideshow on last slide
+		random					: 	0,			// Randomize slide order (Ignores start slide)
+		slide_interval          :   5000,		// Length between transitions
+		transition              :   1, 			// 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left
+		transition_speed		:	750,		// Speed of transition
+		new_window				:	1,			// Image links open in new window/tab
+		pause_hover             :   0,			// Pause slideshow on hover
+		keyboard_nav            :   1,			// Keyboard navigation on/off
+		performance				:	1,			// 0-Normal, 1-Hybrid speed/quality, 2-Optimizes image quality, 3-Optimizes transition speed //  (Only works for Firefox/IE, not Webkit)
+		image_protect			:	1,			// Disables image dragging and right click with Javascript
+												   
+		// Size & Position
+		fit_always				:	0,			// Image will never exceed browser width or height (Ignores min. dimensions)
+		fit_landscape			:   0,			// Landscape images will not exceed browser width
+		fit_portrait         	:   1,			// Portrait images will not exceed browser height  			   
+		min_width		        :   0,			// Min width allowed (in pixels)
+		min_height		        :   0,			// Min height allowed (in pixels)
+		horizontal_center       :   1,			// Horizontally center background
+		vertical_center         :   1,			// Vertically center background
+		
+												   
+		// Components							
+		slide_links				:	1,			// Individual links for each slide (Options: false, 'num', 'name', 'blank')
+		thumb_links				:	1,			// Individual thumb links for each slide
+		thumbnail_navigation    :   0			// Thumbnail navigation
+    	
+    };
+    
+    $.fn.supersized = function(options){
+        return this.each(function(){
+            (new $.supersized(options));
+        });
+    };
+		
+})(jQuery);
+
diff --git a/apps/gallery/js/supersized.3.2.7.min.js b/apps/gallery/js/supersized.3.2.7.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..b9cea9cee1c6d0c92e535c972c0c77cb3d99279b
--- /dev/null
+++ b/apps/gallery/js/supersized.3.2.7.min.js
@@ -0,0 +1,13 @@
+/*
+
+	Supersized - Fullscreen Slideshow jQuery Plugin
+	Version : 3.2.7
+	Site	: www.buildinternet.com/project/supersized
+	
+	Author	: Sam Dunn
+	Company : One Mighty Roar (www.onemightyroar.com)
+	License : MIT License / GPL License
+	
+*/
+
+(function(a){a(document).ready(function(){a("body").append('<div id="supersized-loader"></div><ul id="supersized"></ul>')});a.supersized=function(b){var c="#supersized",d=this;d.$el=a(c);d.el=c;vars=a.supersized.vars;d.$el.data("supersized",d);api=d.$el.data("supersized");d.init=function(){a.supersized.vars=a.extend(a.supersized.vars,a.supersized.themeVars);a.supersized.vars.options=a.extend({},a.supersized.defaultOptions,a.supersized.themeOptions,b);d.options=a.supersized.vars.options;d._build()};d._build=function(){var g=0,e="",j="",h,f="",i;while(g<=d.options.slides.length-1){switch(d.options.slide_links){case"num":h=g;break;case"name":h=d.options.slides[g].title;break;case"blank":h="";break}e=e+'<li class="slide-'+g+'"></li>';if(g==d.options.start_slide-1){if(d.options.slide_links){j=j+'<li class="slide-link-'+g+' current-slide"><a>'+h+"</a></li>"}if(d.options.thumb_links){d.options.slides[g].thumb?i=d.options.slides[g].thumb:i=d.options.slides[g].image;f=f+'<li class="thumb'+g+' current-thumb"><img src="'+i+'"/></li>'}}else{if(d.options.slide_links){j=j+'<li class="slide-link-'+g+'" ><a>'+h+"</a></li>"}if(d.options.thumb_links){d.options.slides[g].thumb?i=d.options.slides[g].thumb:i=d.options.slides[g].image;f=f+'<li class="thumb'+g+'"><img src="'+i+'"/></li>'}}g++}if(d.options.slide_links){a(vars.slide_list).html(j)}if(d.options.thumb_links&&vars.thumb_tray.length){a(vars.thumb_tray).append('<ul id="'+vars.thumb_list.replace("#","")+'">'+f+"</ul>")}a(d.el).append(e);if(d.options.thumbnail_navigation){vars.current_slide-1<0?prevThumb=d.options.slides.length-1:prevThumb=vars.current_slide-1;a(vars.prev_thumb).show().html(a("<img/>").attr("src",d.options.slides[prevThumb].image));vars.current_slide==d.options.slides.length-1?nextThumb=0:nextThumb=vars.current_slide+1;a(vars.next_thumb).show().html(a("<img/>").attr("src",d.options.slides[nextThumb].image))}d._start()};d._start=function(){if(d.options.start_slide){vars.current_slide=d.options.start_slide-1}else{vars.current_slide=Math.floor(Math.random()*d.options.slides.length)}var o=d.options.new_window?' target="_blank"':"";if(d.options.performance==3){d.$el.addClass("speed")}else{if((d.options.performance==1)||(d.options.performance==2)){d.$el.addClass("quality")}}if(d.options.random){arr=d.options.slides;for(var h,m,k=arr.length;k;h=parseInt(Math.random()*k),m=arr[--k],arr[k]=arr[h],arr[h]=m){}d.options.slides=arr}if(d.options.slides.length>1){if(d.options.slides.length>2){vars.current_slide-1<0?loadPrev=d.options.slides.length-1:loadPrev=vars.current_slide-1;var g=(d.options.slides[loadPrev].url)?"href='"+d.options.slides[loadPrev].url+"'":"";var q=a('<img src="'+d.options.slides[loadPrev].image+'"/>');var n=d.el+" li:eq("+loadPrev+")";q.appendTo(n).wrap("<a "+g+o+"></a>").parent().parent().addClass("image-loading prevslide");q.load(function(){a(this).data("origWidth",a(this).width()).data("origHeight",a(this).height());d.resizeNow()})}}else{d.options.slideshow=0}g=(api.getField("url"))?"href='"+api.getField("url")+"'":"";var l=a('<img src="'+api.getField("image")+'"/>');var f=d.el+" li:eq("+vars.current_slide+")";l.appendTo(f).wrap("<a "+g+o+"></a>").parent().parent().addClass("image-loading activeslide");l.load(function(){d._origDim(a(this));d.resizeNow();d.launch();if(typeof theme!="undefined"&&typeof theme._init=="function"){theme._init()}});if(d.options.slides.length>1){vars.current_slide==d.options.slides.length-1?loadNext=0:loadNext=vars.current_slide+1;g=(d.options.slides[loadNext].url)?"href='"+d.options.slides[loadNext].url+"'":"";var e=a('<img src="'+d.options.slides[loadNext].image+'"/>');var p=d.el+" li:eq("+loadNext+")";e.appendTo(p).wrap("<a "+g+o+"></a>").parent().parent().addClass("image-loading");e.load(function(){a(this).data("origWidth",a(this).width()).data("origHeight",a(this).height());d.resizeNow()})}d.$el.css("visibility","hidden");a(".load-item").hide()};d.launch=function(){d.$el.css("visibility","visible");a("#supersized-loader").remove();if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("next")}a(".load-item").show();if(d.options.keyboard_nav){a(document.documentElement).keyup(function(e){if(vars.in_animation){return false}if((e.keyCode==37)||(e.keyCode==40)){clearInterval(vars.slideshow_interval);d.prevSlide()}else{if((e.keyCode==39)||(e.keyCode==38)){clearInterval(vars.slideshow_interval);d.nextSlide()}else{if(e.keyCode==32&&!vars.hover_pause){clearInterval(vars.slideshow_interval);d.playToggle()}}}})}if(d.options.slideshow&&d.options.pause_hover){a(d.el).hover(function(){if(vars.in_animation){return false}vars.hover_pause=true;if(!vars.is_paused){vars.hover_pause="resume";d.playToggle()}},function(){if(vars.hover_pause=="resume"){d.playToggle();vars.hover_pause=false}})}if(d.options.slide_links){a(vars.slide_list+"> li").click(function(){index=a(vars.slide_list+"> li").index(this);targetSlide=index+1;d.goTo(targetSlide);return false})}if(d.options.thumb_links){a(vars.thumb_list+"> li").click(function(){index=a(vars.thumb_list+"> li").index(this);targetSlide=index+1;api.goTo(targetSlide);return false})}if(d.options.slideshow&&d.options.slides.length>1){if(d.options.autoplay&&d.options.slides.length>1){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}else{vars.is_paused=true}a(".load-item img").bind("contextmenu mousedown",function(){return false})}a(window).resize(function(){d.resizeNow()})};d.resizeNow=function(){return d.$el.each(function(){a("img",d.el).each(function(){thisSlide=a(this);var f=(thisSlide.data("origHeight")/thisSlide.data("origWidth")).toFixed(2);var e=d.$el.width(),h=d.$el.height(),i;if(d.options.fit_always){if((h/e)>f){g()}else{j()}}else{if((h<=d.options.min_height)&&(e<=d.options.min_width)){if((h/e)>f){d.options.fit_landscape&&f<1?g(true):j(true)}else{d.options.fit_portrait&&f>=1?j(true):g(true)}}else{if(e<=d.options.min_width){if((h/e)>f){d.options.fit_landscape&&f<1?g(true):j()}else{d.options.fit_portrait&&f>=1?j():g(true)}}else{if(h<=d.options.min_height){if((h/e)>f){d.options.fit_landscape&&f<1?g():j(true)}else{d.options.fit_portrait&&f>=1?j(true):g()}}else{if((h/e)>f){d.options.fit_landscape&&f<1?g():j()}else{d.options.fit_portrait&&f>=1?j():g()}}}}}function g(k){if(k){if(thisSlide.width()<e||thisSlide.width()<d.options.min_width){if(thisSlide.width()*f>=d.options.min_height){thisSlide.width(d.options.min_width);thisSlide.height(thisSlide.width()*f)}else{j()}}}else{if(d.options.min_height>=h&&!d.options.fit_landscape){if(e*f>=d.options.min_height||(e*f>=d.options.min_height&&f<=1)){thisSlide.width(e);thisSlide.height(e*f)}else{if(f>1){thisSlide.height(d.options.min_height);thisSlide.width(thisSlide.height()/f)}else{if(thisSlide.width()<e){thisSlide.width(e);thisSlide.height(thisSlide.width()*f)}}}}else{thisSlide.width(e);thisSlide.height(e*f)}}}function j(k){if(k){if(thisSlide.height()<h){if(thisSlide.height()/f>=d.options.min_width){thisSlide.height(d.options.min_height);thisSlide.width(thisSlide.height()/f)}else{g(true)}}}else{if(d.options.min_width>=e){if(h/f>=d.options.min_width||f>1){thisSlide.height(h);thisSlide.width(h/f)}else{if(f<=1){thisSlide.width(d.options.min_width);thisSlide.height(thisSlide.width()*f)}}}else{thisSlide.height(h);thisSlide.width(h/f)}}}if(thisSlide.parents("li").hasClass("image-loading")){a(".image-loading").removeClass("image-loading")}if(d.options.horizontal_center){a(this).css("left",(e-a(this).width())/2)}if(d.options.vertical_center){a(this).css("top",(h-a(this).height())/2)}});if(d.options.image_protect){a("img",d.el).bind("contextmenu mousedown",function(){return false})}return false})};d.nextSlide=function(){if(vars.in_animation||!api.options.slideshow){return false}else{vars.in_animation=true}clearInterval(vars.slideshow_interval);var h=d.options.slides,e=d.$el.find(".activeslide");a(".prevslide").removeClass("prevslide");e.removeClass("activeslide").addClass("prevslide");vars.current_slide+1==d.options.slides.length?vars.current_slide=0:vars.current_slide++;var g=a(d.el+" li:eq("+vars.current_slide+")"),i=d.$el.find(".prevslide");if(d.options.performance==1){d.$el.removeClass("quality").addClass("speed")}loadSlide=false;vars.current_slide==d.options.slides.length-1?loadSlide=0:loadSlide=vars.current_slide+1;var k=d.el+" li:eq("+loadSlide+")";if(!a(k).html()){var j=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(k).wrap("<a "+imageLink+j+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}if(d.options.thumbnail_navigation==1){vars.current_slide-1<0?prevThumb=d.options.slides.length-1:prevThumb=vars.current_slide-1;a(vars.prev_thumb).html(a("<img/>").attr("src",d.options.slides[prevThumb].image));nextThumb=loadSlide;a(vars.next_thumb).html(a("<img/>").attr("src",d.options.slides[nextThumb].image))}if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("next")}if(d.options.slide_links){a(".current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq(vars.current_slide).addClass("current-slide")}g.css("visibility","hidden").addClass("activeslide");switch(d.options.transition){case 0:case"none":g.css("visibility","visible");vars.in_animation=false;d.afterAnimation();break;case 1:case"fade":g.animate({opacity:0},0).css("visibility","visible").animate({opacity:1,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 2:case"slideTop":g.animate({top:-d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 3:case"slideRight":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 4:case"slideBottom":g.animate({top:d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 5:case"slideLeft":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 6:case"carouselRight":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:-d.$el.width(),avoidTransforms:false},d.options.transition_speed);break;case 7:case"carouselLeft":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:d.$el.width(),avoidTransforms:false},d.options.transition_speed);break}return false};d.prevSlide=function(){if(vars.in_animation||!api.options.slideshow){return false}else{vars.in_animation=true}clearInterval(vars.slideshow_interval);var h=d.options.slides,e=d.$el.find(".activeslide");a(".prevslide").removeClass("prevslide");e.removeClass("activeslide").addClass("prevslide");vars.current_slide==0?vars.current_slide=d.options.slides.length-1:vars.current_slide--;var g=a(d.el+" li:eq("+vars.current_slide+")"),i=d.$el.find(".prevslide");if(d.options.performance==1){d.$el.removeClass("quality").addClass("speed")}loadSlide=vars.current_slide;var k=d.el+" li:eq("+loadSlide+")";if(!a(k).html()){var j=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(k).wrap("<a "+imageLink+j+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}if(d.options.thumbnail_navigation==1){loadSlide==0?prevThumb=d.options.slides.length-1:prevThumb=loadSlide-1;a(vars.prev_thumb).html(a("<img/>").attr("src",d.options.slides[prevThumb].image));vars.current_slide==d.options.slides.length-1?nextThumb=0:nextThumb=vars.current_slide+1;a(vars.next_thumb).html(a("<img/>").attr("src",d.options.slides[nextThumb].image))}if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("prev")}if(d.options.slide_links){a(".current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq(vars.current_slide).addClass("current-slide")}g.css("visibility","hidden").addClass("activeslide");switch(d.options.transition){case 0:case"none":g.css("visibility","visible");vars.in_animation=false;d.afterAnimation();break;case 1:case"fade":g.animate({opacity:0},0).css("visibility","visible").animate({opacity:1,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 2:case"slideTop":g.animate({top:d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 3:case"slideRight":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 4:case"slideBottom":g.animate({top:-d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 5:case"slideLeft":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 6:case"carouselRight":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:0},0).animate({left:d.$el.width(),avoidTransforms:false},d.options.transition_speed);break;case 7:case"carouselLeft":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:0},0).animate({left:-d.$el.width(),avoidTransforms:false},d.options.transition_speed);break}return false};d.playToggle=function(){if(vars.in_animation||!api.options.slideshow){return false}if(vars.is_paused){vars.is_paused=false;if(typeof theme!="undefined"&&typeof theme.playToggle=="function"){theme.playToggle("play")}vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}else{vars.is_paused=true;if(typeof theme!="undefined"&&typeof theme.playToggle=="function"){theme.playToggle("pause")}clearInterval(vars.slideshow_interval)}return false};d.goTo=function(f){if(vars.in_animation||!api.options.slideshow){return false}var e=d.options.slides.length;if(f<0){f=e}else{if(f>e){f=1}}f=e-f+1;clearInterval(vars.slideshow_interval);if(typeof theme!="undefined"&&typeof theme.goTo=="function"){theme.goTo()}if(vars.current_slide==e-f){if(!(vars.is_paused)){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}return false}if(e-f>vars.current_slide){vars.current_slide=e-f-1;vars.update_images="next";d._placeSlide(vars.update_images)}else{if(e-f<vars.current_slide){vars.current_slide=e-f+1;vars.update_images="prev";d._placeSlide(vars.update_images)}}if(d.options.slide_links){a(vars.slide_list+"> .current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq((e-f)).addClass("current-slide")}if(d.options.thumb_links){a(vars.thumb_list+"> .current-thumb").removeClass("current-thumb");a(vars.thumb_list+"> li").eq((e-f)).addClass("current-thumb")}};d._placeSlide=function(e){var h=d.options.new_window?' target="_blank"':"";loadSlide=false;if(e=="next"){vars.current_slide==d.options.slides.length-1?loadSlide=0:loadSlide=vars.current_slide+1;var g=d.el+" li:eq("+loadSlide+")";if(!a(g).html()){var h=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(g).wrap("<a "+imageLink+h+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}d.nextSlide()}else{if(e=="prev"){vars.current_slide-1<0?loadSlide=d.options.slides.length-1:loadSlide=vars.current_slide-1;var g=d.el+" li:eq("+loadSlide+")";if(!a(g).html()){var h=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(g).wrap("<a "+imageLink+h+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}d.prevSlide()}}};d._origDim=function(e){e.data("origWidth",e.width()).data("origHeight",e.height())};d.afterAnimation=function(){if(d.options.performance==1){d.$el.removeClass("speed").addClass("quality")}if(vars.update_images){vars.current_slide-1<0?setPrev=d.options.slides.length-1:setPrev=vars.current_slide-1;vars.update_images=false;a(".prevslide").removeClass("prevslide");a(d.el+" li:eq("+setPrev+")").addClass("prevslide")}vars.in_animation=false;if(!vars.is_paused&&d.options.slideshow){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval);if(d.options.stop_loop&&vars.current_slide==d.options.slides.length-1){d.playToggle()}}if(typeof theme!="undefined"&&typeof theme.afterAnimation=="function"){theme.afterAnimation()}return false};d.getField=function(e){return d.options.slides[vars.current_slide][e]};d.init()};a.supersized.vars={thumb_tray:"#thumb-tray",thumb_list:"#thumb-list",slide_list:"#slide-list",current_slide:0,in_animation:false,is_paused:false,hover_pause:false,slideshow_interval:false,update_images:false,options:{}};a.supersized.defaultOptions={slideshow:1,autoplay:1,start_slide:1,stop_loop:0,random:0,slide_interval:5000,transition:1,transition_speed:750,new_window:1,pause_hover:0,keyboard_nav:1,performance:1,image_protect:1,fit_always:0,fit_landscape:0,fit_portrait:1,min_width:0,min_height:0,horizontal_center:1,vertical_center:1,slide_links:1,thumb_links:1,thumbnail_navigation:0};a.fn.supersized=function(b){return this.each(function(){(new a.supersized(b))})}})(jQuery);
\ No newline at end of file
diff --git a/apps/gallery/js/supersized.shutter.js b/apps/gallery/js/supersized.shutter.js
new file mode 100644
index 0000000000000000000000000000000000000000..cc3025a94a381b4d6f79939e06883fada7f7636a
--- /dev/null
+++ b/apps/gallery/js/supersized.shutter.js
@@ -0,0 +1,337 @@
+/*
+
+	Supersized - Fullscreen Slideshow jQuery Plugin
+	Version : 3.2.7
+	Theme 	: Shutter 1.1
+	
+	Site	: www.buildinternet.com/project/supersized
+	Author	: Sam Dunn
+	Company : One Mighty Roar (www.onemightyroar.com)
+	License : MIT License / GPL License
+
+*/
+
+(function($){
+	
+	theme = {
+	 	
+	 	
+	 	/* Initial Placement
+		----------------------------*/
+	 	_init : function(){
+	 		
+	 		// Center Slide Links
+	 		if (api.options.slide_links) $(vars.slide_list).css('margin-left', -$(vars.slide_list).width()/2);
+	 		
+			// Start progressbar if autoplay enabled
+                        if (api.options.autoplay){
+                                if (api.options.progress_bar) theme.progressBar();
+			}else{
+				if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "play.png");	// If pause play button is image, swap src
+				if (api.options.progress_bar) $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 );	//  Place progress bar
+			}
+			
+			
+			/* Thumbnail Tray
+			----------------------------*/
+			// Hide tray off screen
+			$(vars.thumb_tray).animate({bottom : -$(vars.thumb_tray).height()}, 0 );
+			
+			// Thumbnail Tray Toggle
+			$(vars.tray_button).toggle(function(){
+				$(vars.thumb_tray).stop().animate({bottom : 0, avoidTransforms : true}, 300 );
+				if ($(vars.tray_arrow).attr('src')) $(vars.tray_arrow).attr("src", vars.image_path + "button-tray-down.png");
+				return false;
+			}, function() {
+				$(vars.thumb_tray).stop().animate({bottom : -$(vars.thumb_tray).height(), avoidTransforms : true}, 300 );
+				if ($(vars.tray_arrow).attr('src')) $(vars.tray_arrow).attr("src", vars.image_path + "button-tray-up.png");
+				return false;
+			});
+			
+			// Make thumb tray proper size
+			$(vars.thumb_list).width($('> li', vars.thumb_list).length * $('> li', vars.thumb_list).outerWidth(true));	//Adjust to true width of thumb markers
+			
+			// Display total slides
+			if ($(vars.slide_total).length){
+				$(vars.slide_total).html(api.options.slides.length);
+			}
+			
+			
+			/* Thumbnail Tray Navigation
+			----------------------------*/	
+			if (api.options.thumb_links){
+				//Hide thumb arrows if not needed
+				if ($(vars.thumb_list).width() <= $(vars.thumb_tray).width()){
+					$(vars.thumb_back +','+vars.thumb_forward).fadeOut(0);
+				}
+				
+				// Thumb Intervals
+        		vars.thumb_interval = Math.floor($(vars.thumb_tray).width() / $('> li', vars.thumb_list).outerWidth(true)) * $('> li', vars.thumb_list).outerWidth(true);
+        		vars.thumb_page = 0;
+        		
+        		// Cycle thumbs forward
+        		$(vars.thumb_forward).click(function(){
+        			if (vars.thumb_page - vars.thumb_interval <= -$(vars.thumb_list).width()){
+        				vars.thumb_page = 0;
+        				$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+        			}else{
+        				vars.thumb_page = vars.thumb_page - vars.thumb_interval;
+        				$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+        			}
+        		});
+        		
+        		// Cycle thumbs backwards
+        		$(vars.thumb_back).click(function(){
+        			if (vars.thumb_page + vars.thumb_interval > 0){
+        				vars.thumb_page = Math.floor($(vars.thumb_list).width() / vars.thumb_interval) * -vars.thumb_interval;
+        				if ($(vars.thumb_list).width() <= -vars.thumb_page) vars.thumb_page = vars.thumb_page + vars.thumb_interval;
+        				$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+					}else{
+        				vars.thumb_page = vars.thumb_page + vars.thumb_interval;
+        				$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+        			}
+        		});
+				
+			}
+			
+			
+			/* Navigation Items
+			----------------------------*/
+		    $(vars.next_slide).click(function() {
+		    	api.nextSlide();
+		    });
+		    
+		    $(vars.prev_slide).click(function() {
+		    	api.prevSlide();
+		    });
+		    
+		    	// Full Opacity on Hover
+		    	if(jQuery.support.opacity){
+			    	$(vars.prev_slide +','+vars.next_slide).mouseover(function() {
+					   $(this).stop().animate({opacity:1},100);
+					}).mouseout(function(){
+					   $(this).stop().animate({opacity:0.6},100);
+					});
+				}
+			
+			if (api.options.thumbnail_navigation){
+				// Next thumbnail clicked
+				$(vars.next_thumb).click(function() {
+			    	api.nextSlide();
+			    });
+			    // Previous thumbnail clicked
+			    $(vars.prev_thumb).click(function() {
+			    	api.prevSlide();
+			    });
+			}
+			
+		    $(vars.play_button).click(function() {
+				api.playToggle();						    
+		    });
+			
+			
+			/* Thumbnail Mouse Scrub
+			----------------------------*/
+    		if (api.options.mouse_scrub){
+				$(vars.thumb_tray).mousemove(function(e) {
+					var containerWidth = $(vars.thumb_tray).width(),
+						listWidth = $(vars.thumb_list).width();
+					if (listWidth > containerWidth){
+						var mousePos = 1,
+							diff = e.pageX - mousePos;
+						if (diff > 10 || diff < -10) { 
+						    mousePos = e.pageX; 
+						    newX = (containerWidth - listWidth) * (e.pageX/containerWidth);
+						    diff = parseInt(Math.abs(parseInt($(vars.thumb_list).css('left'))-newX )).toFixed(0);
+						    $(vars.thumb_list).stop().animate({'left':newX}, {duration:diff*3, easing:'easeOutExpo'});
+						}
+					}
+				});
+			}
+			
+			
+			/* Window Resize
+			----------------------------*/
+			$(window).resize(function(){
+				
+				// Delay progress bar on resize
+				if (api.options.progress_bar && !vars.in_animation){
+					if (vars.slideshow_interval) clearInterval(vars.slideshow_interval);
+					if (api.options.slides.length - 1 > 0) clearInterval(vars.slideshow_interval);
+					
+					$(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 );
+					
+					if (!vars.progressDelay && api.options.slideshow){
+						// Delay slideshow from resuming so Chrome can refocus images
+						vars.progressDelay = setTimeout(function() {
+								if (!vars.is_paused){
+									theme.progressBar();
+									vars.slideshow_interval = setInterval(api.nextSlide, api.options.slide_interval);
+								}
+								vars.progressDelay = false;
+						}, 1000);
+					}
+				}
+				
+				// Thumb Links
+				if (api.options.thumb_links && vars.thumb_tray.length){
+					// Update Thumb Interval & Page
+					vars.thumb_page = 0;	
+					vars.thumb_interval = Math.floor($(vars.thumb_tray).width() / $('> li', vars.thumb_list).outerWidth(true)) * $('> li', vars.thumb_list).outerWidth(true);
+					
+					// Adjust thumbnail markers
+					if ($(vars.thumb_list).width() > $(vars.thumb_tray).width()){
+						$(vars.thumb_back +','+vars.thumb_forward).fadeIn('fast');
+						$(vars.thumb_list).stop().animate({'left':0}, 200);
+					}else{
+						$(vars.thumb_back +','+vars.thumb_forward).fadeOut('fast');
+					}
+					
+				}
+			});	
+			
+								
+	 	},
+	 	
+	 	
+	 	/* Go To Slide
+		----------------------------*/
+	 	goTo : function(){
+	 		if (api.options.progress_bar && !vars.is_paused){
+				$(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 );
+				theme.progressBar();
+			}
+		},
+	 	
+	 	/* Play & Pause Toggle
+		----------------------------*/
+	 	playToggle : function(state){
+	 		
+	 		if (state =='play'){
+	 			// If image, swap to pause
+	 			if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "pause.png");
+				if (api.options.progress_bar && !vars.is_paused) theme.progressBar();
+	 		}else if (state == 'pause'){
+	 			// If image, swap to play
+	 			if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "play.png");
+        		if (api.options.progress_bar && vars.is_paused)$(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 );
+	 		}
+	 		
+	 	},
+	 	
+	 	
+	 	/* Before Slide Transition
+		----------------------------*/
+	 	beforeAnimation : function(direction){
+		    if (api.options.progress_bar && !vars.is_paused) $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 );
+		  	
+		  	/* Update Fields
+		  	----------------------------*/
+		  	// Update slide caption
+		   	if ($(vars.slide_caption).length){
+		   		(api.getField('title')) ? $(vars.slide_caption).html(api.getField('title')) : $(vars.slide_caption).html('');
+		   	}
+		    // Update slide number
+			if (vars.slide_current.length){
+			    $(vars.slide_current).html(vars.current_slide + 1);
+			}
+		    
+		    
+		    // Highlight current thumbnail and adjust row position
+		    if (api.options.thumb_links){
+		    
+				$('.current-thumb').removeClass('current-thumb');
+				$('li', vars.thumb_list).eq(vars.current_slide).addClass('current-thumb');
+				
+				// If thumb out of view
+				if ($(vars.thumb_list).width() > $(vars.thumb_tray).width()){
+					// If next slide direction
+					if (direction == 'next'){
+						if (vars.current_slide == 0){
+							vars.thumb_page = 0;
+							$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+						} else if ($('.current-thumb').offset().left - $(vars.thumb_tray).offset().left >= vars.thumb_interval){
+	        				vars.thumb_page = vars.thumb_page - vars.thumb_interval;
+	        				$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+						}
+					// If previous slide direction
+					}else if(direction == 'prev'){
+						if (vars.current_slide == api.options.slides.length - 1){
+							vars.thumb_page = Math.floor($(vars.thumb_list).width() / vars.thumb_interval) * -vars.thumb_interval;
+							if ($(vars.thumb_list).width() <= -vars.thumb_page) vars.thumb_page = vars.thumb_page + vars.thumb_interval;
+							$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+						} else if ($('.current-thumb').offset().left - $(vars.thumb_tray).offset().left < 0){
+							if (vars.thumb_page + vars.thumb_interval > 0) return false;
+	        				vars.thumb_page = vars.thumb_page + vars.thumb_interval;
+	        				$(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'});
+						}
+					}
+				}
+				
+				
+			}
+		    
+	 	},
+	 	
+	 	
+	 	/* After Slide Transition
+		----------------------------*/
+	 	afterAnimation : function(){
+	 		if (api.options.progress_bar && !vars.is_paused) theme.progressBar();	//  Start progress bar
+	 	},
+	 	
+	 	
+	 	/* Progress Bar
+		----------------------------*/
+		progressBar : function(){
+    		$(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ).animate({left:0}, api.options.slide_interval);
+    	}
+	 	
+	 
+	 };
+	 
+	 
+	 /* Theme Specific Variables
+	 ----------------------------*/
+	 $.supersized.themeVars = {
+	 	
+	 	// Internal Variables
+		progress_delay		:	false,				// Delay after resize before resuming slideshow
+		thumb_page 			: 	false,				// Thumbnail page
+		thumb_interval 		: 	false,				// Thumbnail interval
+		image_path			:	OC.webroot+"/apps/gallery/img/supersized/",				// Default image path
+													
+		// General Elements							
+		play_button			:	'#pauseplay',		// Play/Pause button
+		next_slide			:	'#nextslide',		// Next slide button
+		prev_slide			:	'#prevslide',		// Prev slide button
+		next_thumb			:	'#nextthumb',		// Next slide thumb button
+		prev_thumb			:	'#prevthumb',		// Prev slide thumb button
+		
+		slide_caption		:	'#slidecaption',	// Slide caption
+		slide_current		:	'.slidenumber',		// Current slide number
+		slide_total			:	'.totalslides',		// Total Slides
+		slide_list			:	'#slide-list',		// Slide jump list							
+		
+		thumb_tray			:	'#thumb-tray',		// Thumbnail tray
+		thumb_list			:	'#thumb-list',		// Thumbnail list
+		thumb_forward		:	'#thumb-forward',	// Cycles forward through thumbnail list
+		thumb_back			:	'#thumb-back',		// Cycles backwards through thumbnail list
+		tray_arrow			:	'#tray-arrow',		// Thumbnail tray button arrow
+		tray_button			:	'#tray-button',		// Thumbnail tray button
+		
+		progress_bar		:	'#progress-bar'		// Progress bar
+	 												
+	 };												
+	
+	 /* Theme Specific Options
+	 ----------------------------*/												
+	 $.supersized.themeOptions = {					
+	 						   
+		progress_bar		:	1,		// Timer for each slide											
+		mouse_scrub			:	0		// Thumbnails move with mouse
+		
+	 };
+	
+	
+})(jQuery);
diff --git a/apps/gallery/js/supersized.shutter.min.js b/apps/gallery/js/supersized.shutter.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..52ea4a3384a10b6d1511487eed480047878f35e6
--- /dev/null
+++ b/apps/gallery/js/supersized.shutter.min.js
@@ -0,0 +1,14 @@
+/*
+
+	Supersized - Fullscreen Slideshow jQuery Plugin
+	Version : 3.2.7
+	Theme 	: Shutter 1.1
+	
+	Site	: www.buildinternet.com/project/supersized
+	Author	: Sam Dunn
+	Company : One Mighty Roar (www.onemightyroar.com)
+	License : MIT License / GPL License
+
+*/
+
+(function(a){theme={_init:function(){if(api.options.slide_links){a(vars.slide_list).css("margin-left",-a(vars.slide_list).width()/2)}if(api.options.autoplay){if(api.options.progress_bar){theme.progressBar()}}else{if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"play.png")}if(api.options.progress_bar){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}}a(vars.thumb_tray).animate({bottom:-a(vars.thumb_tray).height()},0);a(vars.tray_button).toggle(function(){a(vars.thumb_tray).stop().animate({bottom:0,avoidTransforms:true},300);if(a(vars.tray_arrow).attr("src")){a(vars.tray_arrow).attr("src",vars.image_path+"button-tray-down.png")}return false},function(){a(vars.thumb_tray).stop().animate({bottom:-a(vars.thumb_tray).height(),avoidTransforms:true},300);if(a(vars.tray_arrow).attr("src")){a(vars.tray_arrow).attr("src",vars.image_path+"button-tray-up.png")}return false});a(vars.thumb_list).width(a("> li",vars.thumb_list).length*a("> li",vars.thumb_list).outerWidth(true));if(a(vars.slide_total).length){a(vars.slide_total).html(api.options.slides.length)}if(api.options.thumb_links){if(a(vars.thumb_list).width()<=a(vars.thumb_tray).width()){a(vars.thumb_back+","+vars.thumb_forward).fadeOut(0)}vars.thumb_interval=Math.floor(a(vars.thumb_tray).width()/a("> li",vars.thumb_list).outerWidth(true))*a("> li",vars.thumb_list).outerWidth(true);vars.thumb_page=0;a(vars.thumb_forward).click(function(){if(vars.thumb_page-vars.thumb_interval<=-a(vars.thumb_list).width()){vars.thumb_page=0;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{vars.thumb_page=vars.thumb_page-vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}});a(vars.thumb_back).click(function(){if(vars.thumb_page+vars.thumb_interval>0){vars.thumb_page=Math.floor(a(vars.thumb_list).width()/vars.thumb_interval)*-vars.thumb_interval;if(a(vars.thumb_list).width()<=-vars.thumb_page){vars.thumb_page=vars.thumb_page+vars.thumb_interval}a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{vars.thumb_page=vars.thumb_page+vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}})}a(vars.next_slide).click(function(){api.nextSlide()});a(vars.prev_slide).click(function(){api.prevSlide()});if(jQuery.support.opacity){a(vars.prev_slide+","+vars.next_slide).mouseover(function(){a(this).stop().animate({opacity:1},100)}).mouseout(function(){a(this).stop().animate({opacity:0.6},100)})}if(api.options.thumbnail_navigation){a(vars.next_thumb).click(function(){api.nextSlide()});a(vars.prev_thumb).click(function(){api.prevSlide()})}a(vars.play_button).click(function(){api.playToggle()});if(api.options.mouse_scrub){a(vars.thumb_tray).mousemove(function(f){var c=a(vars.thumb_tray).width(),g=a(vars.thumb_list).width();if(g>c){var b=1,d=f.pageX-b;if(d>10||d<-10){b=f.pageX;newX=(c-g)*(f.pageX/c);d=parseInt(Math.abs(parseInt(a(vars.thumb_list).css("left"))-newX)).toFixed(0);a(vars.thumb_list).stop().animate({left:newX},{duration:d*3,easing:"easeOutExpo"})}}})}a(window).resize(function(){if(api.options.progress_bar&&!vars.in_animation){if(vars.slideshow_interval){clearInterval(vars.slideshow_interval)}if(api.options.slides.length-1>0){clearInterval(vars.slideshow_interval)}a(vars.progress_bar).stop().animate({left:-a(window).width()},0);if(!vars.progressDelay&&api.options.slideshow){vars.progressDelay=setTimeout(function(){if(!vars.is_paused){theme.progressBar();vars.slideshow_interval=setInterval(api.nextSlide,api.options.slide_interval)}vars.progressDelay=false},1000)}}if(api.options.thumb_links&&vars.thumb_tray.length){vars.thumb_page=0;vars.thumb_interval=Math.floor(a(vars.thumb_tray).width()/a("> li",vars.thumb_list).outerWidth(true))*a("> li",vars.thumb_list).outerWidth(true);if(a(vars.thumb_list).width()>a(vars.thumb_tray).width()){a(vars.thumb_back+","+vars.thumb_forward).fadeIn("fast");a(vars.thumb_list).stop().animate({left:0},200)}else{a(vars.thumb_back+","+vars.thumb_forward).fadeOut("fast")}}})},goTo:function(b){if(api.options.progress_bar&&!vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0);theme.progressBar()}},playToggle:function(b){if(b=="play"){if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"pause.png")}if(api.options.progress_bar&&!vars.is_paused){theme.progressBar()}}else{if(b=="pause"){if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"play.png")}if(api.options.progress_bar&&vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}}}},beforeAnimation:function(b){if(api.options.progress_bar&&!vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}if(a(vars.slide_caption).length){(api.getField("title"))?a(vars.slide_caption).html(api.getField("title")):a(vars.slide_caption).html("")}if(vars.slide_current.length){a(vars.slide_current).html(vars.current_slide+1)}if(api.options.thumb_links){a(".current-thumb").removeClass("current-thumb");a("li",vars.thumb_list).eq(vars.current_slide).addClass("current-thumb");if(a(vars.thumb_list).width()>a(vars.thumb_tray).width()){if(b=="next"){if(vars.current_slide==0){vars.thumb_page=0;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{if(a(".current-thumb").offset().left-a(vars.thumb_tray).offset().left>=vars.thumb_interval){vars.thumb_page=vars.thumb_page-vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}}}else{if(b=="prev"){if(vars.current_slide==api.options.slides.length-1){vars.thumb_page=Math.floor(a(vars.thumb_list).width()/vars.thumb_interval)*-vars.thumb_interval;if(a(vars.thumb_list).width()<=-vars.thumb_page){vars.thumb_page=vars.thumb_page+vars.thumb_interval}a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{if(a(".current-thumb").offset().left-a(vars.thumb_tray).offset().left<0){if(vars.thumb_page+vars.thumb_interval>0){return false}vars.thumb_page=vars.thumb_page+vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}}}}}}},afterAnimation:function(){if(api.options.progress_bar&&!vars.is_paused){theme.progressBar()}},progressBar:function(){a(vars.progress_bar).stop().animate({left:-a(window).width()},0).animate({left:0},api.options.slide_interval)}};a.supersized.themeVars={progress_delay:false,thumb_page:false,thumb_interval:false,image_path:OC.webroot+"/apps/gallery/img/supersized/",play_button:"#pauseplay",next_slide:"#nextslide",prev_slide:"#prevslide",next_thumb:"#nextthumb",prev_thumb:"#prevthumb",slide_caption:"#slidecaption",slide_current:".slidenumber",slide_total:".totalslides",slide_list:"#slide-list",thumb_tray:"#thumb-tray",thumb_list:"#thumb-list",thumb_forward:"#thumb-forward",thumb_back:"#thumb-back",tray_arrow:"#tray-arrow",tray_button:"#tray-button",progress_bar:"#progress-bar"};a.supersized.themeOptions={progress_bar:1,mouse_scrub:0}})(jQuery);
diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php
index 39d6d3aded1b0c35f8b26ba658acc6e4d6d465a0..701949d4d8077ba7f7f8098ec4ee9eb5ee52e565 100644
--- a/apps/gallery/lib/album.php
+++ b/apps/gallery/lib/album.php
@@ -58,7 +58,7 @@ class OC_Gallery_Album {
 		return $stmt->execute($args);
 	}
 
-	public static function removeByName($owner, $name) { self::remove($ownmer, $name); }
+	public static function removeByName($owner, $name) { self::remove($owner, $name); }
 	public static function removeByPath($owner, $path) { self::remove($owner, null, $path); }
 	public static function removeByParentPath($owner, $parent) { self::remove($owner, null, null, $parent); }
 	
diff --git a/apps/gallery/lib/hooks_handlers.php b/apps/gallery/lib/hooks_handlers.php
index 093979834da58895aa38d1c58af19bf82a043680..3bafdb5cf4d18ed7de6f7cd724c7590904f83b98 100644
--- a/apps/gallery/lib/hooks_handlers.php
+++ b/apps/gallery/lib/hooks_handlers.php
@@ -38,5 +38,3 @@ class OC_Gallery_Hooks_Handlers {
 		//TODO: implement this
 	}
 }
-
-?>
diff --git a/apps/gallery/lib/images_utils.php b/apps/gallery/lib/images_utils.php
index ac3a383c97748985a0a44540510556882f86607e..f5e37cf1dee3ab044ba5dcd98ebf99958b3ba21b 100644
--- a/apps/gallery/lib/images_utils.php
+++ b/apps/gallery/lib/images_utils.php
@@ -60,5 +60,3 @@ function CroppedThumbnail($imgSrc,$thumbnail_width,$thumbnail_height, $tgtImg, $
     imagedestroy($process);
     imagedestroy($myImage);
 }
-
-?>
diff --git a/apps/gallery/lib/managers.php b/apps/gallery/lib/managers.php
index 17eb741a660e0accfe2dcd2cd199ee17b589d197..575d962dbe3a196e2811850cb5b2e6d8d146e158 100644
--- a/apps/gallery/lib/managers.php
+++ b/apps/gallery/lib/managers.php
@@ -2,7 +2,7 @@
 
 namespace OC\Pictures;
 
-class DatabaseManager {
+class DatabaseManager {        
 	private static $instance = null;
 	protected $cache = array();
 	const TAG = 'DatabaseManager';
@@ -25,6 +25,15 @@ class DatabaseManager {
 		}
 	}
 
+	public function setFileData($path, $width, $height) {
+		$stmt = \OCP\DB::prepare('INSERT INTO *PREFIX*pictures_images_cache (uid_owner, path, width, height) VALUES (?, ?, ?, ?)');
+		$stmt->execute(array(\OCP\USER::getUser(), $path, $width, $height));
+		$ret = array('path' => $path, 'width' => $width, 'height' => $height);
+		$dir = dirname($path);
+		$this->cache[$dir][$path] = $ret;
+		return $ret;
+	}
+
 	public function getFileData($path) {
 		$gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery';
 		$path = $gallery_path.$path;
@@ -39,9 +48,7 @@ class DatabaseManager {
 		if (!$image->loadFromFile($path)) {
 			return false;
 		}
-		$stmt = \OCP\DB::prepare('INSERT INTO *PREFIX*pictures_images_cache (uid_owner, path, width, height) VALUES (?, ?, ?, ?)');
-		$stmt->execute(array(\OCP\USER::getUser(), $path, $image->width(), $image->height()));
-		$ret = array('path' => $path, 'width' => $image->width(), 'height' => $image->height());
+		$ret = $this->setFileData($path, $image->width(), $image->height());
 		unset($image);
 		$this->cache[$dir][$path] = $ret;
 		return $ret;
@@ -54,6 +61,7 @@ class ThumbnailsManager {
 	
 	private static $instance = null;
 	const TAG = 'ThumbnailManager';
+        const THUMBNAIL_HEIGHT = 150;
 	
 	public static function getInstance() {
 		if (self::$instance === null)
@@ -62,9 +70,9 @@ class ThumbnailsManager {
 	}
 
 	public function getThumbnail($path) {
-		$gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery';
-		if (file_exists($gallery_path.$path)) {
-			return new \OC_Image($gallery_path.$path);
+		$gallery_storage = \OCP\Files::getStorage('gallery');
+		if ($gallery_storage->file_exists($path)) {
+			return new \OC_Image($gallery_storage->getLocalFile($path));
 		}
 		if (!\OC_Filesystem::file_exists($path)) {
 			\OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN);
@@ -73,27 +81,39 @@ class ThumbnailsManager {
 		$image = new \OC_Image();
 		$image->loadFromFile(\OC_Filesystem::getLocalFile($path));
 		if (!$image->valid()) return false;
-
+                            
 		$image->fixOrientation();
-
-		$ret = $image->preciseResize(floor((150*$image->width())/$image->height()), 150);
+                
+		$ret = $image->preciseResize( floor((self::THUMBNAIL_HEIGHT*$image->width())/$image->height()), self::THUMBNAIL_HEIGHT );
 		
 		if (!$ret) {
 			\OC_Log::write(self::TAG, 'Couldn\'t resize image', \OC_Log::ERROR);
 			unset($image);
 			return false;
 		}
-
-		$image->save($gallery_path.'/'.$path);
+		$l = $gallery_storage->getLocalFile($path);
+                
+		$image->save($l);
 		return $image;
 	}
-	
+
+	public function getThumbnailWidth($image) {
+		return floor((self::THUMBNAIL_HEIGHT*$image->widthTopLeft())/$image->heightTopLeft());
+	}
+
 	public function getThumbnailInfo($path) {
 		$arr = DatabaseManager::getInstance()->getFileData($path);
 		if (!$arr) {
-			$thubnail = $this->getThumbnail($path);
-			unset($thubnail);
-			$arr = DatabaseManager::getInstance()->getFileData($path);
+			if (!\OC_Filesystem::file_exists($path)) {
+				\OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN);
+				return false;
+			}
+			$image = new \OC_Image();
+			$image->loadFromFile(\OC_Filesystem::getLocalFile($path));
+			if (!$image->valid()) {
+				return false;
+			}
+			$arr = DatabaseManager::getInstance()->setFileData($path, $this->getThumbnailWidth($image), self::THUMBNAIL_HEIGHT);
 		}
 		$ret = array('filepath' => $arr['path'],
 					 'width' => $arr['width'],
@@ -102,13 +122,12 @@ class ThumbnailsManager {
 	}
 	
 	public function delete($path) {
-		$thumbnail = \OCP\Config::getSystemValue('datadirectory').'/'.\OC_User::getUser()."/gallery".$path;
-		if (file_exists($thumbnail)) {
-			unlink($thumbnail);
+		$thumbnail_storage = \OCP\Files::getStorage('gallery');
+		if ($thumbnail_storage->file_exists($path)) {
+			$thumbnail_storage->unlink($path);
 		}
 	}
 	
 	private function __construct() {}
 
 }
-?>
diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php
index f9527cb5fdbe8301ad70265b25c40e2e01a5ca58..3b4e74641b3af70df610ddd8d62ccfb0c35c2e84 100644
--- a/apps/gallery/lib/photo.php
+++ b/apps/gallery/lib/photo.php
@@ -1,53 +1,55 @@
 <?php
 
 /**
-* ownCloud - gallery application
-*
-* @author Bartek Przybylski
-* @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com
-* 
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either 
-* version 3 of the License, or any later version.
-* 
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Lesser General Public 
-* License along with this library. If not, see <http://www.gnu.org/licenses/>.
-* 
-*/
-
+ * ownCloud - gallery application
+ *
+ * @author Bartek Przybylski
+ * @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either 
+ * version 3 of the License, or any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
 class OC_Gallery_Photo {
-	public static function create($albumId, $img){
+
+	public static function create($albumId, $img) {
 		$stmt = OCP\DB::prepare('INSERT INTO *PREFIX*gallery_photos (album_id, file_path) VALUES (?, ?)');
 		$stmt->execute(array($albumId, $img));
 	}
-	public static function find($albumId, $img=null){
+
+	public static function find($albumId, $img = null) {
 		$sql = 'SELECT * FROM *PREFIX*gallery_photos WHERE album_id = ?';
 		$args = array($albumId);
-		if (!is_null($img)){
+		if (!is_null($img)) {
 			$sql .= ' AND file_path = ?';
 			$args[] = $img;
 		}
 		$stmt = OCP\DB::prepare($sql);
 		return $stmt->execute($args);
 	}
-	public static function findForAlbum($owner, $album_name){
+
+	public static function findForAlbum($owner, $album_name) {
 		$stmt = OCP\DB::prepare('SELECT *'
-			.' FROM *PREFIX*gallery_photos photos,'
-				.' *PREFIX*gallery_albums albums'
-			.' WHERE albums.uid_owner = ?'
-				.' AND albums.album_name = ?'
-				.' AND photos.album_id = albums.album_id');
+					.' FROM *PREFIX*gallery_photos photos,'
+						.' *PREFIX*gallery_albums albums'
+					.' WHERE albums.uid_owner = ?'
+						.' AND albums.album_name = ?'
+						.' AND photos.album_id = albums.album_id');
 		return $stmt->execute(array($owner, $album_name));
 	}
 
-  public static function removeByPath($path, $album_id) {
-    $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE file_path LIKE ? and album_id = ?');
+	public static function removeByPath($path, $album_id) {
+		$stmt = OCP\DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE file_path LIKE ? and album_id = ?');
 		$stmt->execute(array($path, $album_id));
 	}
 
@@ -67,19 +69,20 @@ class OC_Gallery_Photo {
 	}
 
 	public static function getThumbnail($image_name, $owner = null) {
-		if (!$owner) $owner = OCP\USER::getUser();
+		if (!$owner)
+			$owner = OCP\USER::getUser();
 		$view = OCP\Files::getStorage('gallery');
 		$save_dir = dirname($image_name);
 		if (!$view->is_dir($save_dir)) {
 			$view->mkdir($save_dir);
 		}
-		$view->chroot($view->getRoot().'/'.$save_dir);
+		$view->chroot($view->getRoot() . '/' . $save_dir);
 		$thumb_file = basename($image_name);
 		if ($view->file_exists($thumb_file)) {
 			$image = new OC_Image($view->fopen($thumb_file, 'r'));
 		} else {
 			$image_path = OC_Filesystem::getLocalFile($image_name);
-			if(!file_exists($image_path)) {
+			if (!file_exists($image_path)) {
 				return null;
 			}
 			$image = new OC_Image($image_path);
@@ -91,7 +94,38 @@ class OC_Gallery_Photo {
 		}
 		if ($image->valid()) {
 			return $image;
-		}else{
+		} else {
+			$image->destroy();
+		}
+		return null;
+	}
+
+	public static function getViewImage($image_name, $owner = null) {
+		if (!$owner) $owner = OCP\USER::getUser();
+		$save_dir = OCP\Config::getSystemValue("datadirectory") . '/' . $owner . '/gallery';
+		$save_dir .= dirname($image_name) . '/view/';
+		$image_path = $image_name;
+		$view_file = $save_dir . basename($image_name);
+		if (!is_dir($save_dir)) {
+			mkdir($save_dir, 0777, true);
+		}
+		if (file_exists($view_file)) {
+			$image = new OC_Image($view_file);
+		} else {
+			$image_path = OC_Filesystem::getLocalFile($image_path);
+			if (!file_exists($image_path)) {
+				return null;
+			}
+			$image = new OC_Image($image_path);
+			if ($image->valid()) {
+				$image->resize(1200);
+				$image->fixOrientation();
+				$image->save($view_file);
+			}
+		}
+		if ($image->valid()) {
+			return $image;
+		} else {
 			$image->destroy();
 		}
 		return null;
@@ -100,4 +134,5 @@ class OC_Gallery_Photo {
 	public static function getGalleryRoot() {
 		return OCP\Config::getUserValue(OCP\USER::getUser(), 'gallery', 'root', '');
 	}
+
 }
diff --git a/apps/gallery/lib/tiles.php b/apps/gallery/lib/tiles.php
index 5efe0d7a299cd09b2fcbd9eff0676e780726dc45..e36d26d31913adb9bb4fae8a2a125f42265479dc 100644
--- a/apps/gallery/lib/tiles.php
+++ b/apps/gallery/lib/tiles.php
@@ -33,7 +33,7 @@ class TilesLine {
 	}
 
 	public function setAvailableSpace($space) {
-		$available_space = $space;
+		$this->available_space = $space;
 	}
 
 	public function getTilesCount() {
@@ -95,7 +95,7 @@ class TileSingle extends TileBase {
 	public function get($extra = '') {
 		//	!HACK! file path needs to be encoded twice because files app decode twice url, so any special chars like + or & in filename
 		//	!HACK! will result in failing of opening them 
-		return '<a rel="images" title="'.htmlentities(basename($this->getPath())).'" href="'.\OCP\Util::linkTo('files', 'download.php').'?file='.urlencode(urlencode($this->getPath())).'"><img rel="images" src="'.\OCP\Util::linkTo('gallery', 'ajax/thumbnail.php').'&filepath='.urlencode($this->getPath()).'" '.$extra.'></a>';
+		return '<a rel="images" title="'.htmlentities(basename($this->getPath())).'" href="'.\OCP\Util::linkTo('gallery','ajax/viewImage.php').'?img='.urlencode(urlencode($this->getPath())).'"><img rel="images" src="'.\OCP\Util::linkTo('gallery', 'ajax/thumbnail.php').'&filepath='.urlencode($this->getPath()).'" '.$extra.'></a>';
 	}
 	
 	public function getMiniatureSrc() {
@@ -174,5 +174,3 @@ class TileStack extends TileBase {
 	private $tiles_array;
 	private $stack_name;
 }
-
-?>
diff --git a/apps/gallery/lib/tiles_test.php b/apps/gallery/lib/tiles_test.php
index 022a88f75cc2095ce03705ce0c9b778c39d68544..02d567c628d3973ead65f499054b936d82edb3c2 100644
--- a/apps/gallery/lib/tiles_test.php
+++ b/apps/gallery/lib/tiles_test.php
@@ -83,5 +83,3 @@ if ($ts->getCount() != 0) {
 }
 
 echo $tl->get();
-
-?>
diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php
index c90932cefd0169479f82b0acbdbe5b3059823ed8..b2efd5342ffa6ec0454828e3c7294f235dd23df3 100644
--- a/apps/gallery/templates/index.php
+++ b/apps/gallery/templates/index.php
@@ -22,7 +22,10 @@ $(document).ready(function() {
 		}
 	}
 		
-?><br/>
+?>
+	<div id="slideshow">
+		<input type="button" class="start" value="<?php echo $l->t('Slideshow')?>" />
+	</div>
 </div>
 <div id="gallerycontent">
 <?php
@@ -32,3 +35,50 @@ echo $_['tl']->get();
 
 ?>
 </div>
+
+<!-- start supersized block -->
+<div id="slideshow-content" style="display:none;">
+
+	<!--Thumbnail Navigation-->
+	<div id="prevthumb"></div>
+	<div id="nextthumb"></div>
+
+	<!--Arrow Navigation-->
+	<a id="prevslide" class="load-item"></a>
+	<a id="nextslide" class="load-item"></a>
+
+	<div id="thumb-tray" class="load-item">
+		<div id="thumb-back"></div>
+		<div id="thumb-forward"></div>
+	</div>
+
+	<!--Time Bar-->
+	<div id="progress-back" class="load-item">
+		<div id="progress-bar"></div>
+	</div>
+
+	<!--Control Bar-->
+	<div id="slideshow-controls-wrapper" class="load-item">
+		<div id="slideshow-controls">
+
+			<a id="play-button"><img id="pauseplay" src="<?php echo OCP\image_path('gallery', 'supersized/pause.png'); ?>"/></a>
+
+			<!--Slide counter-->
+			<div id="slidecounter">
+				<span class="slidenumber"></span> / <span class="totalslides"></span>
+			</div>
+
+			<!--Slide captions displayed here-->
+			<div id="slidecaption"></div>
+
+			<!--Thumb Tray button-->
+			<a id="tray-button"><img id="tray-arrow" src="<?php echo OCP\image_path('gallery', 'supersized/button-tray-up.png'); ?>"/></a>
+
+			<!--Navigation-->
+			<!--
+			<ul id="slide-list"></ul>
+			-->
+		</div>
+	</div>
+
+</div><!-- end supersized block -->
diff --git a/apps/gallery/templates/view_album.php b/apps/gallery/templates/view_album.php
index c16ed69c0658015fb95fc590121f8c115fb715f6..00e891103f1488f39245c5b74b3226ea0c23f679 100644
--- a/apps/gallery/templates/view_album.php
+++ b/apps/gallery/templates/view_album.php
@@ -1,9 +1,9 @@
 <?php
 OCP\Util::addStyle('gallery', 'styles');
-OCP\Util::addscript('gallery', 'albums');
-OCP\Util::addscript('gallery', 'album_cover');
-OCP\Util::addscript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack');
-OCP\Util::addscript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
+OCP\Util::addScript('gallery', 'albums');
+OCP\Util::addScript('gallery', 'album_cover');
+OCP\Util::addScript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack');
+OCP\Util::addScript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
 OCP\Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' );
 $l = OC_L10N::get('gallery');
 ?>
diff --git a/apps/media/ajax/api.php b/apps/media/ajax/api.php
index 23abc579272b6ec40cf29dd73e2556c1c4d781fe..7f5cdb22c12db9842aa46c0a86a50d96c525ae33 100644
--- a/apps/media/ajax/api.php
+++ b/apps/media/ajax/api.php
@@ -131,4 +131,3 @@ if($arguments['action']){
 			exit;
 	}
 }
-?>
diff --git a/apps/media/ajax/autoupdate.php b/apps/media/ajax/autoupdate.php
index 3122c7e6754193310f626343f3d9c226b714f284..c2dbf27a00439ae324070d3c4a202ec2c148bd5e 100644
--- a/apps/media/ajax/autoupdate.php
+++ b/apps/media/ajax/autoupdate.php
@@ -35,4 +35,3 @@ $autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true');
 OCP\Config::setUserValue(OCP\USER::getUser(),'media','autoupdate',(integer)$autoUpdate);
 
 OCP\JSON::success(array('data' => $autoUpdate));
-?>
diff --git a/apps/media/appinfo/version b/apps/media/appinfo/version
index e6adf3fc7bb711e3d88c13f2bb5e1b91f865d361..44bb5d1f74358758e75a906eebebb458e9de7fcb 100644
--- a/apps/media/appinfo/version
+++ b/apps/media/appinfo/version
@@ -1 +1 @@
-0.4
\ No newline at end of file
+0.4.1
\ No newline at end of file
diff --git a/apps/media/index.php b/apps/media/index.php
index fb51aa0b17a1a07eaddc44fb1a206b24efcae363..ae85abc8aab7ed92f96c48517b39566cb3a9f228 100644
--- a/apps/media/index.php
+++ b/apps/media/index.php
@@ -40,5 +40,3 @@ OCP\App::setActiveNavigationEntry( 'media_index' );
 
 $tmpl = new OCP\Template( 'media', 'music', 'user' );
 $tmpl->printPage();
-?>
- 
diff --git a/apps/media/lib_ampache.php b/apps/media/lib_ampache.php
index 255e6f468f89127ef96c6b52bf46a91011ea9b11..d5a093338cc2338a4dec6b3bdd7eddba93d62df1 100644
--- a/apps/media/lib_ampache.php
+++ b/apps/media/lib_ampache.php
@@ -271,7 +271,6 @@ class OC_MEDIA_AMPACHE{
 </root>");
 			return;
 		}
-		global $SITEROOT;
 		$filter=$params['filter'];
 		$albums=OC_MEDIA_COLLECTION::getAlbums($filter);
 		$artist=OC_MEDIA_COLLECTION::getArtistName($filter);
@@ -419,5 +418,3 @@ class OC_MEDIA_AMPACHE{
 		echo('</root>');
 	}
 }
-
-?>
diff --git a/apps/media/lib_collection.php b/apps/media/lib_collection.php
index 598c08d32e24a3052efc7f772ed1a535ef57adf8..cacab8e959f3716bf496cdaed758b7e9ecbe97c0 100644
--- a/apps/media/lib_collection.php
+++ b/apps/media/lib_collection.php
@@ -27,7 +27,6 @@ class OC_MEDIA_COLLECTION{
 	public static $uid;
 	private static $artistIdCache=array();
 	private static $albumIdCache=array();
-	private static $songIdCache=array();
 	private static $queries=array();
 	
 	/**
@@ -152,7 +151,7 @@ class OC_MEDIA_COLLECTION{
 			return $artistId;
 		}else{
 			$query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_artists` (`artist_name`) VALUES (?)");
-			$result=$query->execute(array($name));
+			$query->execute(array($name));
 			return self::getArtistId($name);;
 		}
 	}
@@ -387,5 +386,3 @@ class OC_MEDIA_COLLECTION{
 		$query->execute(array($newPath,$oldPath));
 	}
 }
-
-?>
diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php
index 9e687a4af2c048d626796a62e72db855a35345cd..54502f425756a3cc41b330937707891ed311abd2 100644
--- a/apps/media/lib_media.php
+++ b/apps/media/lib_media.php
@@ -27,12 +27,12 @@ class OC_MEDIA{
 	 * @param array $params, parameters passed from OC_Hook
 	 */
 	public static function loginListener($params){
-		if(isset($_POST['user']) and $_POST['password']){
-			$name=$_POST['user'];
+		if(isset($params['uid']) and $params['password']){
+			$name=$params['uid'];
 			$query=OCP\DB::prepare("SELECT user_id from *PREFIX*media_users WHERE user_id LIKE ?");
 			$uid=$query->execute(array($name))->fetchAll();
 			if(count($uid)==0){
-				$password=hash('sha256',$_POST['password']);
+				$password=hash('sha256',$params['password']);
 				$query=OCP\DB::prepare("INSERT INTO *PREFIX*media_users (user_id, user_password_sha256) VALUES (?, ?);");
 				$query->execute(array($name,$password));
 			}
diff --git a/apps/media/settings.php b/apps/media/settings.php
index 227298fafecaa7ec5f17fba5d8aa2d0ae5086da5..53738f02f9fc907ab3cedf2cccc37d22ac14a9ef 100644
--- a/apps/media/settings.php
+++ b/apps/media/settings.php
@@ -3,4 +3,3 @@
 $tmpl = new OCP\Template( 'media', 'settings');
 
 return $tmpl->fetchPage();
-?>
diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml
index fa878762a05a82ca761ba5e9b6ad22b73823b53a..1388ad9c31618374cd457cbe7e9f0993e88ebaa8 100644
--- a/apps/remoteStorage/appinfo/info.xml
+++ b/apps/remoteStorage/appinfo/info.xml
@@ -7,4 +7,7 @@
 	<author>Michiel de Jong</author>
 	<require>4</require>
 	<shipped>true</shipped>
+	<remote>
+		<remoteStorage>webdav.php</remoteStorage>
+	</remote>
 </info>
diff --git a/apps/remoteStorage/appinfo/version b/apps/remoteStorage/appinfo/version
index 490f510fc27c164443c2334282f67bd7034c1698..0e2c93950bb65d053566355a9458a743878b70d8 100644
--- a/apps/remoteStorage/appinfo/version
+++ b/apps/remoteStorage/appinfo/version
@@ -1 +1 @@
-0.6
\ No newline at end of file
+0.7
\ No newline at end of file
diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php
index 5d481f315f885f80950b83f7e808723890c03c09..e8b237628c4a6a8a944009754c08f98f03209dc1 100644
--- a/apps/remoteStorage/appinfo/webfinger.php
+++ b/apps/remoteStorage/appinfo/webfinger.php
@@ -1,8 +1,8 @@
-<?php if(OC_User::userExists(WF_USER)) { ?>
+<?php if(OC_User::userExists(WF_USER)): ?>
     {
         "rel":"remoteStorage",
-        "template":"<?php echo WF_BASEURL; ?>/apps/remoteStorage/WebDAV.php/<?php echo WF_USER; ?>/remoteStorage/{category}/",
+        "template":"<?php echo WF_BASEURL; ?>/remote.php/remoteStorage/<?php echo WF_USER; ?>/remoteStorage/{category}/",
         "api":"WebDAV",
         "auth":"<?php echo WF_BASEURL; ?>/?app=remoteStorage&getfile=auth.php&userid=<?php echo WF_USER; ?>"
     }
-<?php } ?>
+<?php endif ?>
diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php
index 42cd9c90f649a3e57706fd881bf7e49996fb356e..c1765640c5ddc08a961da051d10c0d5d22986779 100644
--- a/apps/remoteStorage/lib_remoteStorage.php
+++ b/apps/remoteStorage/lib_remoteStorage.php
@@ -17,12 +17,11 @@ class OC_remoteStorage {
 		$user=OCP\USER::getUser();
 		$query=OCP\DB::prepare("SELECT token FROM *PREFIX*authtoken WHERE user=? AND appUrl=? AND category=? LIMIT 1");
 		$result=$query->execute(array($user, $appUrl, $categories));
-		$ret = array();
 		if($row=$result->fetchRow()) {
-		  return base64_encode('remoteStorage:'.$row['token']);
-    } else {
-      return false;
-    }
+			return base64_encode('remoteStorage:'.$row['token']);
+		} else {
+			return false;
+		}
 	}
 
 	public static function getAllTokens() {
@@ -42,13 +41,13 @@ class OC_remoteStorage {
 	public static function deleteToken($token) {
 		$user=OCP\USER::getUser();
 		$query=OCP\DB::prepare("DELETE FROM *PREFIX*authtoken WHERE token=? AND user=?");
-		$result=$query->execute(array($token,$user));
+		$query->execute(array($token,$user));
 		return 'unknown';//how can we see if any rows were affected?
 	}
 	private static function addToken($token, $appUrl, $categories){
 		$user=OCP\USER::getUser();
 		$query=OCP\DB::prepare("INSERT INTO *PREFIX*authtoken (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)");
-		$result=$query->execute(array($token,$appUrl,$user,$categories));
+		$query->execute(array($token,$appUrl,$user,$categories));
 	}
 	public static function createCategories($appUrl, $categories) {
 		$token=uniqid();
diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php
index 12d02d1cf5d10b3a94da04a145b50ca73d6ffd29..bed3093c3b3ff1689e2c2bf574cc7ca2ebb0979f 100644
--- a/apps/remoteStorage/oauth_ro_auth.php
+++ b/apps/remoteStorage/oauth_ro_auth.php
@@ -9,10 +9,10 @@
 
 class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBasic {
 	private $validTokens;
-  private $category;
+	private $category;
 	public function __construct($validTokensArg, $categoryArg) {
 		$this->validTokens = $validTokensArg;
-    $this->category = $categoryArg;
+		$this->category = $categoryArg;
 	}
 
 	/**
@@ -25,16 +25,16 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa
 	 */
 	protected function validateUserPass($username, $password){
 		//always give read-only:
-		if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') 
+		if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
 		    || (isset($this->validTokens[$password]))
-        || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
-        ) {
+			|| (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
+		) {
 			OC_Util::setUpFS();
 			return true;
 		} else {
-      //var_export($_SERVER);
-      //var_export($this->validTokens);
-      //die('not getting in with "'.$username.'"/"'.$password.'"!');
+			//var_export($_SERVER);
+			//var_export($this->validTokens);
+			//die('not getting in with "'.$username.'"/"'.$password.'"!');
 			return false;	
 		}
 	}
@@ -48,8 +48,8 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa
 		$userpass = $auth->getUserPass();
 		if (!$userpass) {
 			if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
-	        ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
-          ) {
+				||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
+			) {
 				$userpass = array('', '');
 			} else {
 				$auth->requireLogin();
diff --git a/apps/remoteStorage/settings.php b/apps/remoteStorage/settings.php
index 9c48549fe6d4ab17a983f2485b736345f4ccabf4..3be8b0984d8da1dee978ed2e653a72f23448abd4 100644
--- a/apps/remoteStorage/settings.php
+++ b/apps/remoteStorage/settings.php
@@ -4,4 +4,3 @@ require_once('lib_remoteStorage.php');
 $tmpl = new OCP\Template( 'remoteStorage', 'settings');
 
 return $tmpl->fetchPage();
-?>
diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/webdav.php
similarity index 72%
rename from apps/remoteStorage/WebDAV.php
rename to apps/remoteStorage/webdav.php
index 7a81c18e0affd65131083f55cadc467770bb91cb..8d8ec6a45a1cfa4871d897fd1673cdf574be2891 100644
--- a/apps/remoteStorage/WebDAV.php
+++ b/apps/remoteStorage/webdav.php
@@ -25,22 +25,7 @@
 *
 */
 
-
-// Do not load FS ...
-$RUNTIME_NOSETUPFS = true;
-
-
-require_once('../../lib/base.php');
-
-require_once('../../lib/user.php');
-require_once('../../lib/public/user.php');
-
-require_once('../../lib/app.php');
-require_once('../../lib/public/app.php');
-
-require_once('../../3rdparty/Sabre/DAV/Auth/IBackend.php');
-require_once('../../3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php');
-require_once('../../lib/connector/sabre/auth.php');
+OC_App::loadApps(array('filesystem','authentication'));
 
 OCP\App::checkAppEnabled('remoteStorage');
 require_once('lib_remoteStorage.php');
@@ -61,14 +46,15 @@ if(isset($_SERVER['HTTP_ORIGIN'])) {
 	header('Access-Control-Allow-Origin: *');
 }
 
-$path = substr($_SERVER["REQUEST_URI"], strlen($_SERVER["SCRIPT_NAME"]));
+$path = substr($_SERVER["REQUEST_URI"], strlen($baseuri));
+
 $pathParts =  explode('/', $path);
 // for webdav:
-// 0/     1       /   2    /   3...
-//  /$ownCloudUser/remoteStorage/$category/
+//      0       /   1    /   2...
+//  $ownCloudUser/remoteStorage/$category/
 
-if(count($pathParts) >= 3 && $pathParts[0] == '') {
-	list($dummy, $ownCloudUser, $dummy2, $category) = $pathParts;
+if(count($pathParts) >= 2) {
+	list($ownCloudUser, $dummy2, $category) = $pathParts;
 
 	OC_Util::setupFS($ownCloudUser);
 
@@ -77,13 +63,13 @@ if(count($pathParts) >= 3 && $pathParts[0] == '') {
 	$server = new Sabre_DAV_Server($publicDir);
 
 	// Path to our script
-	$server->setBaseUri(OC::$WEBROOT."/apps/remoteStorage/WebDAV.php/$ownCloudUser");
+	$server->setBaseUri($baseuri.$ownCloudUser);
 
 	// Auth backend
 	$authBackend = new OC_Connector_Sabre_Auth_ro_oauth(
-      OC_remoteStorage::getValidTokens($ownCloudUser, $category),
-      $category
-      );
+		OC_remoteStorage::getValidTokens($ownCloudUser, $category),
+		$category
+	);
 
 	$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud');//should use $validTokens here
 	$server->addPlugin($authPlugin);
diff --git a/apps/tasks/ajax/addtask.php b/apps/tasks/ajax/addtask.php
index 9f35e7f21ec464165ba40d5518259c0692a60ce0..d98fdbf38881420263d0dbb66f48990784b848aa 100644
--- a/apps/tasks/ajax/addtask.php
+++ b/apps/tasks/ajax/addtask.php
@@ -3,6 +3,7 @@
 // Init owncloud
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('tasks');
+OCP\JSON::callCheck();
 
 $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
 $first_calendar = reset($calendars);
@@ -21,7 +22,7 @@ $request['description'] = null;
 $vcalendar = OC_Task_App::createVCalendarFromRequest($request);
 $id = OC_Calendar_Object::add($cid, $vcalendar->serialize());
 
-$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+$user_timezone = OC_Calendar_App::getTimezone();
 $task = OC_Task_App::arrayForJSON($id, $vcalendar->VTODO, $user_timezone);
 
 OCP\JSON::success(array('task' => $task));
diff --git a/apps/tasks/ajax/addtaskform.php b/apps/tasks/ajax/addtaskform.php
deleted file mode 100644
index d86232e2da5a74132a0b91c50e8b512bdc693635..0000000000000000000000000000000000000000
--- a/apps/tasks/ajax/addtaskform.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-// Init owncloud
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('tasks');
-
-$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
-$category_options = OC_Calendar_App::getCategoryOptions();
-$percent_options = range(0, 100, 10);
-$priority_options = OC_Task_App::getPriorityOptions();
-$tmpl = new OCP\Template('tasks','part.addtaskform');
-$tmpl->assign('calendars',$calendars);
-$tmpl->assign('category_options', $category_options);
-$tmpl->assign('percent_options', $percent_options);
-$tmpl->assign('priority_options', $priority_options);
-$tmpl->assign('details', new OC_VObject('VTODO'));
-$tmpl->assign('categories', '');
-$page = $tmpl->fetchPage();
-
-OCP\JSON::success(array('data' => array( 'page' => $page )));
diff --git a/apps/tasks/ajax/delete.php b/apps/tasks/ajax/delete.php
index e29add9b556ff4f43a08acfc19bf09d9101220d0..cc22c3e38733bcac2fd5581c77aca5b52e89b09a 100644
--- a/apps/tasks/ajax/delete.php
+++ b/apps/tasks/ajax/delete.php
@@ -23,6 +23,7 @@
 // Init owncloud
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('tasks');
+OCP\JSON::callCheck();
 
 $id = $_POST['id'];
 $task = OC_Calendar_App::getEventObject( $id );
diff --git a/apps/tasks/ajax/edittask.php b/apps/tasks/ajax/edittask.php
deleted file mode 100644
index edcc8a7cdcda2240a4e04d5ada7596cda781f34c..0000000000000000000000000000000000000000
--- a/apps/tasks/ajax/edittask.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-// Init owncloud
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('tasks');
-
-$l10n = new OC_L10N('tasks');
-
-$id = $_POST['id'];
-$vcalendar = OC_Calendar_App::getVCalendar($id);
-
-$errors = OC_Task_App::validateRequest($_POST);
-if (!empty($errors)) {
-	OCP\JSON::error(array('data' => array( 'errors' => $errors )));
-	exit();
-}
-
-OC_Task_App::updateVCalendarFromRequest($_POST, $vcalendar);
-OC_Calendar_Object::edit($id, $vcalendar->serialize());
-
-$priority_options = OC_Task_App::getPriorityOptions();
-$tmpl = new OCP\Template('tasks','part.details');
-$tmpl->assign('priority_options', $priority_options);
-$tmpl->assign('details', $vcalendar->VTODO);
-$tmpl->assign('id', $id);
-$page = $tmpl->fetchPage();
-
-$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
-$task = OC_Task_App::arrayForJSON($id, $vcalendar->VTODO, $user_timezone);
-
-OCP\JSON::success(array('data' => array( 'id' => $id, 'page' => $page, 'task' => $task )));
diff --git a/apps/tasks/ajax/edittaskform.php b/apps/tasks/ajax/edittaskform.php
deleted file mode 100644
index e5a0a7297c56908f419dddaa5e3911b5b12b6115..0000000000000000000000000000000000000000
--- a/apps/tasks/ajax/edittaskform.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-// Init owncloud
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('tasks');
-
-$id = $_GET['id'];
-$details = OC_Calendar_App::getVCalendar($id)->VTODO;
-$categories = $details->getAsString('CATEGORIES');
-
-$category_options = OC_Calendar_App::getCategoryOptions();
-$percent_options = range(0, 100, 10);
-$priority_options = OC_Task_App::getPriorityOptions();
-
-$tmpl = new OCP\Template('tasks','part.edittaskform');
-$tmpl->assign('category_options', $category_options);
-$tmpl->assign('percent_options', $percent_options);
-$tmpl->assign('priority_options', $priority_options);
-$tmpl->assign('id',$id);
-$tmpl->assign('details',$details);
-$tmpl->assign('categories', $categories);
-$page = $tmpl->fetchPage();
-
-OCP\JSON::success(array('data' => array( 'page' => $page )));
diff --git a/apps/tasks/ajax/getdetails.php b/apps/tasks/ajax/getdetails.php
deleted file mode 100644
index 4ce469e0c9ccf71fcd9eec1d885d6082bcd6cae9..0000000000000000000000000000000000000000
--- a/apps/tasks/ajax/getdetails.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-// Init owncloud
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('tasks');
-
-$l10n = new OC_L10N('tasks');
-
-$id = $_GET['id'];
-$task = OC_Calendar_Object::find($id);
-$details = OC_VObject::parse($task['calendardata']);
-if (!$details){
-	OCP\JSON::error();
-	exit;
-}
-
-$priority_options = OC_Task_App::getPriorityOptions();
-$tmpl = new OCP\Template('tasks','part.details');
-$tmpl->assign('priority_options', $priority_options);
-$tmpl->assign('details',$details->VTODO);
-$tmpl->assign('id',$id);
-$page = $tmpl->fetchPage();
-
-OCP\JSON::success(array('data' => array( 'id' => $id, 'page' => $page )));
diff --git a/apps/tasks/ajax/gettasks.php b/apps/tasks/ajax/gettasks.php
index 011730d0a13e771de2275a9f46d3e137b2479558..b6183d9cb656fbea2a851f23eac9b117568ef8dc 100644
--- a/apps/tasks/ajax/gettasks.php
+++ b/apps/tasks/ajax/gettasks.php
@@ -11,7 +11,7 @@ OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('tasks');
 
 $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
-$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+$user_timezone = OC_Calendar_App::getTimezone();
 
 $tasks = array();
 foreach( $calendars as $calendar ){
diff --git a/apps/tasks/ajax/update_property.php b/apps/tasks/ajax/update_property.php
index 46521cf6c585a2604f2ef3f6fa0993e7aa2cc6b1..679cfdefe48ccf8dca2d1f14931dc2f6cfe4da61 100644
--- a/apps/tasks/ajax/update_property.php
+++ b/apps/tasks/ajax/update_property.php
@@ -9,6 +9,7 @@
 // Init owncloud
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('tasks');
+OCP\JSON::callCheck();
 
 $id = $_POST['id'];
 $property = $_POST['type'];
@@ -38,7 +39,7 @@ switch($property) {
 		$type = null;
 		if ($due != 'false') {
 			try {
-				$timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+				$timezone = OC_Calendar_App::getTimezone();
 				$timezone = new DateTimeZone($timezone);
 				$due = new DateTime('@'.$due);
 				$due->setTimezone($timezone);
@@ -63,6 +64,6 @@ switch($property) {
 }
 OC_Calendar_Object::edit($id, $vcalendar->serialize());
 
-$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+$user_timezone = OC_Calendar_App::getTimezone();
 $task_info = OC_Task_App::arrayForJSON($id, $vtodo, $user_timezone);
 OCP\JSON::success(array('data' => $task_info));
diff --git a/apps/tasks/index.php b/apps/tasks/index.php
index 4ff304a56079b244ee7d5354159e8b1f6a5ea43a..f1c4d1e765c7e85daf3c00c9d22476041a7edd32 100644
--- a/apps/tasks/index.php
+++ b/apps/tasks/index.php
@@ -21,8 +21,8 @@ OCP\Util::addScript('3rdparty/timepicker', 'jquery.ui.timepicker');
 OCP\Util::addStyle('3rdparty/timepicker', 'jquery.ui.timepicker');
 OCP\Util::addScript('tasks', 'tasks');
 OCP\Util::addStyle('tasks', 'style');
-OCP\Util::addScript('contacts','jquery.multi-autocomplete');
-OCP\Util::addScript('','oc-vcategories');
+OCP\Util::addScript('contacts', 'jquery.multi-autocomplete');
+OCP\Util::addScript('', 'oc-vcategories');
 OCP\App::setActiveNavigationEntry('tasks_index');
 
 $categories = OC_Calendar_App::getCategoryOptions();
diff --git a/apps/tasks/js/tasks.js b/apps/tasks/js/tasks.js
index bc92965bb0b9802d47ed9ef179f7413659690f85..de627927507eed885fa863231656b86e667e3fc8 100644
--- a/apps/tasks/js/tasks.js
+++ b/apps/tasks/js/tasks.js
@@ -469,67 +469,5 @@ $(document).ready(function(){
 		return false;
 	});
 
-	$('#tasks_addtaskform input[type="submit"]').live('click',function(){
-		$.post('ajax/addtask.php',$('#tasks_addtaskform').serialize(),function(jsondata){
-			if(jsondata.status == 'success'){
-				$('#task_details').data('id',jsondata.data.id);
-				$('#task_details').html(jsondata.data.page);
-				$('#tasks_list').append(OC.Tasks.create_task_div(jsondata.data.task));
-			}
-			else{
-				alert(jsondata.data.message);
-			}
-		}, 'json');
-		return false;
-	});
-
-	$('#tasks_edit').live('click',function(){
-		var id = $('#task_details').data('id');
-		$.getJSON('ajax/edittaskform.php',{'id':id},function(jsondata){
-			if(jsondata.status == 'success'){
-				$('#task_details').html(jsondata.data.page);
-				$('#task_details #categories').multiple_autocomplete({source: categories});
-			}
-			else{
-				alert(jsondata.data.message);
-			}
-		});
-		return false;
-	});
-
-	$('#tasks_edittaskform #percent_complete').live('change',function(event){
-		if ($(event.target).val() == 100){
-			$('#tasks_edittaskform #complete').show();
-		}else{
-			$('#tasks_edittaskform #complete').hide();
-		}
-	});
-
-	$('#tasks_edittaskform input[type="submit"]').live('click',function(){
-		$.post('ajax/edittask.php',$('#tasks_edittaskform').serialize(),function(jsondata){
-			$('.error_msg').remove();
-			$('.error').removeClass('error');
-			if(jsondata.status == 'success'){
-				var id = jsondata.data.id;
-				$('#task_details').data('id',id);
-				$('#task_details').html(jsondata.data.page);
-				var task = jsondata.data.task;
-				$('#tasks .task[data-id='+id+']')
-					.data('task', task)
-					.html(OC.Tasks.create_task_div(task).html());
-			}
-			else{
-				var errors = jsondata.data.errors;
-				for (k in errors){
-					$('#'+k).addClass('error')
-						.after('<span class="error_msg">'+errors[k]+'</span>');
-				}
-				$('.error_msg').effect('highlight', {}, 3000);
-				$('.error').effect('highlight', {}, 3000);
-			}
-		}, 'json');
-		return false;
-	});
-
 	OCCategories.app = 'calendar';
 });
diff --git a/apps/tasks/lib/app.php b/apps/tasks/lib/app.php
index 1b42968f0bea7c05370402a44d7df927eae1c6ba..a97c6b95d1dfbdefc8acd08bcc2399660478dd5a 100644
--- a/apps/tasks/lib/app.php
+++ b/apps/tasks/lib/app.php
@@ -77,24 +77,24 @@ class OC_Task_App {
 	public static function validateRequest($request)
 	{
 		$errors = array();
-		if($request['summary'] == ''){
+		if($request['summary'] == '') {
 			$errors['summary'] = self::$l10n->t('Empty Summary');
 		}
 
 		try {
-			$timezone = OCP\Config::getUserValue(OCP\User::getUser(), "calendar", "timezone", "Europe/London");
+			$timezone = OC_Calendar_App::getTimezone();
 			$timezone = new DateTimeZone($timezone);
 			new DateTime($request['due'], $timezone);
 		} catch (Exception $e) {
 			$errors['due'] = self::$l10n->t('Invalid date/time');
 		}
 
-		if ($request['percent_complete'] < 0 || $request['percent_complete'] > 100){
+		if ($request['percent_complete'] < 0 || $request['percent_complete'] > 100) {
 			$errors['percent_complete'] = self::$l10n->t('Invalid percent complete');
 		}
-		if ($request['percent_complete'] == 100 && !empty($request['completed'])){
+		if ($request['percent_complete'] == 100 && !empty($request['completed'])) {
 			try {
-				$timezone = OCP\Config::getUserValue(OCP\User::getUser(), "calendar", "timezone", "Europe/London");
+				$timezone = OC_Calendar_App::getTimezone();
 				$timezone = new DateTimeZone($timezone);
 				new DateTime($request['completed'], $timezone);
 			} catch (Exception $e) {
@@ -147,7 +147,7 @@ class OC_Task_App {
 		$vtodo->setString('PRIORITY', $priority);
 
 		if ($due) {
-			$timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+			$timezone = OC_Calendar_App::getTimezone();
 			$timezone = new DateTimeZone($timezone);
 			$due = new DateTime($due, $timezone);
 			$vtodo->setDateTime('DUE', $due);
@@ -168,15 +168,15 @@ class OC_Task_App {
 			$vtodo->__unset('PERCENT-COMPLETE');
 		}
 
-		if ($percent_complete == 100){
-			if (!$completed){
+		if ($percent_complete == 100) {
+			if (!$completed) {
 				$completed = 'now';
 			}
 		} else {
 			$completed = null;
 		}
 		if ($completed) {
-			$timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+			$timezone = OC_Calendar_App::getTimezone();
 			$timezone = new DateTimeZone($timezone);
 			$completed = new DateTime($completed, $timezone);
 			$vtodo->setDateTime('COMPLETED', $completed);
diff --git a/apps/tasks/templates/part.addtaskform.php b/apps/tasks/templates/part.addtaskform.php
deleted file mode 100644
index 0fad5592aa7207934ad5ae9a2267bb0e2f362c38..0000000000000000000000000000000000000000
--- a/apps/tasks/templates/part.addtaskform.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<form id="tasks_addtaskform">
-	<?php if(count($_['calendars'])==1): ?>
-		<input type="hidden" name="id" value="<?php echo $_['calendars'][0]['id']; ?>">
-	<?php else: ?>
-		<label for="id"><?php echo $l->t('Calendar'); ?></label>
-		<select name="id" size="1">
-			<?php foreach($_['calendars'] as $calendar): ?>
-				<option value="<?php echo $calendar['id']; ?>"><?php echo $calendar['displayname']; ?></option>
-			<?php endforeach; ?>
-		</select>
-		<br>
-	<?php endif; ?>
-	<?php echo $this->inc('part.taskform'); ?>
-	<input type="submit" name="submit" value="<?php echo $l->t('Create Task'); ?>">
-</form>
diff --git a/apps/tasks/templates/part.details.php b/apps/tasks/templates/part.details.php
deleted file mode 100644
index 89636b6e7626d91d3e2d39738d2b3dc096b94a0e..0000000000000000000000000000000000000000
--- a/apps/tasks/templates/part.details.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php if(isset($_['details']->SUMMARY)): ?>
-<table>
-<?php
-echo $this->inc('part.property', array('label' => $l->t('Summary'), 'property' => $_['details']->SUMMARY));
-if(isset($_['details']->LOCATION)):
-	echo $this->inc('part.property', array('label' => $l->t('Location'), 'property' => $_['details']->LOCATION));
-endif;
-if(isset($_['details']->CATEGORIES)):
-	echo $this->inc('part.property', array('label' => $l->t('Categories'), 'property' => $_['details']->CATEGORIES));
-endif;
-if(isset($_['details']->DUE)):
-	echo $this->inc('part.property', array('label' => $l->t('Due'), 'property' => $_['details']->DUE[0]));
-endif;
-if(isset($_['details']->PRIORITY)):
-	echo $this->inc('part.property', array('label' => $l->t('Priority'), 'property' => $_['details']->PRIORITY[0], 'options' => $_['priority_options']));
-endif;
-if($_['details']->__isset('PERCENT-COMPLETE') || isset($_['details']->COMPLETED)):
-?>
-<tr>
-	<th>
-		<?php echo $l->t('Complete') ?>
-	</th>
-	<td>
-<?php if($_['details']->__isset('PERCENT-COMPLETE')):
-		echo $_['details']->__get('PERCENT-COMPLETE')->value.' % ';
-	endif;
-	if(isset($_['details']->COMPLETED)):
-		echo $l->t('on '). $l->l('datetime', $_['details']->COMPLETED[0]->getDateTime());
-	endif;
-	echo '</tr>';
-endif;
-if(isset($_['details']->DESCRIPTION)):
-	echo $this->inc('part.property', array('label' => $l->t('Description'), 'property' => $_['details']->DESCRIPTION));
-endif; ?>
-</table>
-<form>
-	<input type="button" id="tasks_delete" value="<?php echo $l->t('Delete');?>">
-	<input type="button" id="tasks_edit" value="<?php echo $l->t('Edit');?>">
-</form>
-<?php else: ?>
-<?php //var_dump($_['details']); ?>
-<?php endif ?>
diff --git a/apps/tasks/templates/part.edittaskform.php b/apps/tasks/templates/part.edittaskform.php
deleted file mode 100644
index fe123f07ac6d3699799f4bf11513ed0520fa3856..0000000000000000000000000000000000000000
--- a/apps/tasks/templates/part.edittaskform.php
+++ /dev/null
@@ -1,5 +0,0 @@
-<form id="tasks_edittaskform">
-	<input type="hidden" name="id" value="<?php echo $_['id']; ?>">
-	<?php echo $this->inc('part.taskform'); ?>
-	<input type="submit" name="submit" value="<?php echo $l->t('Update Task'); ?>">
-</form>
diff --git a/apps/tasks/templates/part.property.php b/apps/tasks/templates/part.property.php
deleted file mode 100644
index 591fd363e6f62c02d8f5d2fc2087fd2c6b4d9802..0000000000000000000000000000000000000000
--- a/apps/tasks/templates/part.property.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<tr>
-	<th>
-		<?php echo $_['label'] ?>
-	</th>
-	<td>
-		<?php
-			switch (get_class($_['property']))
-			{
-				case 'Sabre_VObject_Element_DateTime':
-					echo $l->l('datetime', $_['property']->getDateTime());
-					break;
-				default:
-					$value = $_['property']->value;
-					if (isset($_['options']))
-					{
-						$value = $_['options'][$value];
-					}
-					echo nl2br($value);
-			}
-		?>
-	</td>
-</tr>
diff --git a/apps/tasks/templates/part.taskform.php b/apps/tasks/templates/part.taskform.php
deleted file mode 100644
index 0a25ed33c91709754f30731d3887a0ab7b1703d5..0000000000000000000000000000000000000000
--- a/apps/tasks/templates/part.taskform.php
+++ /dev/null
@@ -1,36 +0,0 @@
-	<label for="summary"><?php echo $l->t('Summary'); ?></label>
-	<input type="text" id="summary" name="summary" placeholder="<?php echo $l->t('Summary of the task');?>" value="<?php echo isset($_['details']->SUMMARY) ? $_['details']->SUMMARY[0]->value : '' ?>">
-	<br>
-	<label for="location"><?php echo $l->t('Location'); ?></label>
-	<input type="text" id="location" name="location" placeholder="<?php echo $l->t('Location of the task');?>" value="<?php echo isset($_['details']->LOCATION) ? $_['details']->LOCATION[0]->value : '' ?>">
-	<br>
-	<label for="categories"><?php echo $l->t('Categories'); ?></label>
-	<input id="categories" name="categories" type="text" placeholder="<?php echo $l->t('Separate categories with commas'); ?>" value="<?php echo isset($_['categories']) ? $_['categories'] : '' ?>">
-	<a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"><img alt="<?php echo $l->t('Edit categories'); ?>" src="<?php echo OCP\image_path('core','actions/rename.svg')?>" class="svg action" style="width: 16px; height: 16px;"></a>
-	<br>
-	<label for="due"><?php echo $l->t('Due'); ?></label>
-	<input type="text" id="due" name="due" placeholder="<?php echo $l->t('Due date') ?>" value="<?php echo isset($_['details']->DUE) ? $l->l('datetime', $_['details']->DUE[0]->getDateTime()) : '' ?>">
-	<br>
-	<select name="percent_complete" id="percent_complete">
-		<?php
-		foreach($_['percent_options'] as $percent){
-			echo '<option value="' . $percent . '"' . (($_['details']->__get('PERCENT-COMPLETE') && $percent == $_['details']->__get('PERCENT-COMPLETE')->value) ? ' selected="selected"' : '') . '>' . $percent . ' %</option>';
-		}
-		?>
-	</select>
-	<label for="percent_complete"><?php echo $l->t('Complete'); ?></label>
-	<span id="complete"<?php echo ($_['details']->__get('PERCENT-COMPLETE') && $_['details']->__get('PERCENT-COMPLETE')->value == 100) ? '' : ' style="display:none;"' ?>><label for="completed"><?php echo $l->t('completed on'); ?></label>
-	<input type="text" id="completed" name="completed" value="<?php echo isset($_['details']->COMPLETED) ? $l->l('datetime', $_['details']->COMPLETED[0]->getDateTime()) : '' ?>"></span>
-	<br>
-	<label for="priority"><?php echo $l->t('Priority'); ?></label>
-	<select name="priority">
-		<?php
-		foreach($_['priority_options'] as $priority => $label){
-			echo '<option value="' . $priority . '"' . ((isset($_['details']->PRIORITY) && $priority == $_['details']->PRIORITY->value) ? ' selected="selected"' : '') . '>' . $label . '</option>';
-		}
-		?>
-	</select>
-	<br>
-	<label for="description"><?php echo $l->t('Description'); ?></label><br>
-	<textarea placeholder="<?php echo $l->t('Description of the task');?>" name="description"><?php echo isset($_['details']->DESCRIPTION) ? $_['details']->DESCRIPTION[0]->value : '' ?></textarea>
-	<br>
diff --git a/apps/tasks/templates/part.tasks.php b/apps/tasks/templates/part.tasks.php
deleted file mode 100644
index 50be1cd6beda284b5df9748d65f7035fadb38cef..0000000000000000000000000000000000000000
--- a/apps/tasks/templates/part.tasks.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php foreach( $_['tasks'] as $task ): ?>
-	<li data-id="<?php echo $task['id']; ?>"><a href="index.php?id=<?php echo $task['id']; ?>"><?php echo $task['name']; ?></a> </li>
-<?php endforeach; ?>
diff --git a/apps/user_external/appinfo/app.php b/apps/user_external/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..c7408ec30d91236787c65915c52d71f0fa29f3ca
--- /dev/null
+++ b/apps/user_external/appinfo/app.php
@@ -0,0 +1,4 @@
+<?php
+OC::$CLASSPATH['OC_User_IMAP']='apps/user_external/lib/imap.php';
+OC::$CLASSPATH['OC_User_SMB']='apps/user_external/lib/smb.php';
+OC::$CLASSPATH['OC_User_FTP']='apps/user_external/lib/ftp.php';
diff --git a/apps/user_external/appinfo/info.xml b/apps/user_external/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1d1dcee54012ef6b5374624635ed9d1ddae44a64
--- /dev/null
+++ b/apps/user_external/appinfo/info.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<info>
+	<id>user_external</id>
+	<name>External user support</name>
+	<description>Use external user authentication methods</description>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>4</require>
+	<shipped>true</shipped>
+	<types>
+		<authentication/>
+	</types>
+</info>
diff --git a/apps/user_external/appinfo/version b/apps/user_external/appinfo/version
new file mode 100644
index 0000000000000000000000000000000000000000..ceab6e11ece0bcec917c12e11d350946f085d549
--- /dev/null
+++ b/apps/user_external/appinfo/version
@@ -0,0 +1 @@
+0.1
\ No newline at end of file
diff --git a/apps/user_external/lib/ftp.php b/apps/user_external/lib/ftp.php
new file mode 100644
index 0000000000000000000000000000000000000000..e03e17d2b6a79665eeec06cc5e982cedbded025d
--- /dev/null
+++ b/apps/user_external/lib/ftp.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_User_FTP extends OC_User_Backend{
+	private $host;
+	private $secure;
+	private $protocol;
+
+	public function __construct($host,$secure=false){
+		$this->host=$host;
+		$this->secure=$secure;
+		$this->protocol='ftp';
+		if($this->secure){
+			$this->protocol.='s';
+		}
+		$this->protocol.='://';
+	}
+
+	/**
+	 * @brief Check if the password is correct
+	 * @param $uid The username
+	 * @param $password The password
+	 * @returns true/false
+	 *
+	 * Check if the password is correct without logging in the user
+	 */
+	public function checkPassword($uid, $password){
+		$url=$this->protocol.$uid.':'.$password.'@'.$this->host.'/';
+		$result=@opendir($url);
+		if(is_resource($result)){
+			return $uid;
+		}else{
+			return false;
+		}
+	}
+
+	public function userExists($uid){
+		return true;
+	}
+}
diff --git a/apps/user_external/lib/imap.php b/apps/user_external/lib/imap.php
new file mode 100644
index 0000000000000000000000000000000000000000..584e9804b18bf32ca20d082267441608c0c8e773
--- /dev/null
+++ b/apps/user_external/lib/imap.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_User_IMAP extends OC_User_Backend{
+	private $mailbox;
+	
+	public function __construct($mailbox){
+		$this->mailbox=$mailbox;
+	}
+	
+	/**
+	 * @brief Check if the password is correct
+	 * @param $uid The username
+	 * @param $password The password
+	 * @returns true/false
+	 *
+	 * Check if the password is correct without logging in the user
+	 */
+	public function checkPassword($uid, $password){
+		$mbox = @imap_open($this->mailbox, $uid, $password);
+		imap_errors();
+		imap_alerts();
+		if($mbox){
+			imap_close($mbox);
+			return $uid;
+		}else{
+			return false;
+		}
+	}
+
+	public function userExists($uid){
+		return true;
+	}
+}
+
diff --git a/apps/user_external/lib/smb.php b/apps/user_external/lib/smb.php
new file mode 100644
index 0000000000000000000000000000000000000000..44d2b7903d8c3fdb0b19b5947437168afafde9a5
--- /dev/null
+++ b/apps/user_external/lib/smb.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_User_SMB extends OC_User_Backend{
+	private $host;
+
+	const smbclient='smbclient';
+	const loginError='NT_STATUS_LOGON_FAILURE';
+	
+	public function __construct($host){
+		$this->host=$host;
+	}
+	
+	/**
+	 * @brief Check if the password is correct
+	 * @param $uid The username
+	 * @param $password The password
+	 * @returns true/false
+	 *
+	 * Check if the password is correct without logging in the user
+	 */
+	public function checkPassword($uid, $password){
+		$uidEscaped=escapeshellarg($uid);
+		$password=escapeshellarg($password);
+		$result=array();
+		$command=self::smbclient.' //'.$this->host.'/dummy -U'.$uidEscaped.'%'.$password;
+		$result=exec($command,$result);
+		if(substr($result,-strlen(self::loginError))==self::loginError){
+			return false;
+		}else{
+			return $uid;
+		}
+	}
+	
+	public function userExists($uid){
+		return true;
+	}
+}
\ No newline at end of file
diff --git a/apps/user_external/tests/config.php b/apps/user_external/tests/config.php
new file mode 100644
index 0000000000000000000000000000000000000000..64ee141d32d6dda1cf8dfd56f7ad3b790d604d67
--- /dev/null
+++ b/apps/user_external/tests/config.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+return array(
+	'imap'=>array(
+		'run'=>false,
+		'mailbox'=>'{imap.gmail.com:993/imap/ssl}INBOX', //see http://php.net/manual/en/function.imap-open.php
+		'user'=>'foo',//valid username/password combination
+		'password'=>'bar',
+	),
+	'smb'=>array(
+		'run'=>false,
+		'host'=>'localhost',
+		'user'=>'test',//valid username/password combination
+		'password'=>'test',
+	),
+	'ftp'=>array(
+		'run'=>false,
+		'host'=>'localhost',
+		'user'=>'test',//valid username/password combination
+		'password'=>'test',
+	),
+);
diff --git a/apps/user_external/tests/ftp.php b/apps/user_external/tests/ftp.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cf7565f9c65ae042ab0aea349e0191b70b49f1e
--- /dev/null
+++ b/apps/user_external/tests/ftp.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class Test_User_FTP extends UnitTestCase{
+	/**
+	 * @var OC_User_IMAP $instance
+	 */
+	private $instance;
+	
+	private function getConfig(){
+		return include(__DIR__.'/config.php');
+	}
+	
+	function skip(){
+		$config=$this->getConfig();
+		$this->skipUnless($config['ftp']['run']);
+	}
+	
+	function setUp(){
+		$config=$this->getConfig();
+		$this->instance=new OC_User_FTP($config['ftp']['host']);
+	}
+	
+	function testLogin(){
+		$config=$this->getConfig();
+		$this->assertEqual($config['ftp']['user'],$this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password']));
+		$this->assertFalse($this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password'].'foo'));
+	}
+}
diff --git a/apps/user_external/tests/imap.php b/apps/user_external/tests/imap.php
new file mode 100644
index 0000000000000000000000000000000000000000..c703b32107f76514269c97e65f0588cc8ed93e41
--- /dev/null
+++ b/apps/user_external/tests/imap.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class Test_User_Imap extends UnitTestCase{
+	/**
+	 * @var OC_User_IMAP $instance
+	 */
+	private $instance;
+
+	private function getConfig(){
+		return include(__DIR__.'/config.php');
+	}
+
+	function skip(){
+		$config=$this->getConfig();
+		$this->skipUnless($config['imap']['run']);
+	} 
+
+	function setUp(){
+		$config=$this->getConfig();
+		$this->instance=new OC_User_IMAP($config['imap']['mailbox']);
+	}
+	
+	function testLogin(){
+		$config=$this->getConfig();
+		$this->assertEqual($config['imap']['user'],$this->instance->checkPassword($config['imap']['user'],$config['imap']['password']));
+		$this->assertFalse($this->instance->checkPassword($config['imap']['user'],$config['imap']['password'].'foo'));
+	}
+}
diff --git a/apps/user_external/tests/smb.php b/apps/user_external/tests/smb.php
new file mode 100644
index 0000000000000000000000000000000000000000..1ed7eb934be9efe1f8c18c65f205f55bc0e5cf06
--- /dev/null
+++ b/apps/user_external/tests/smb.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class Test_User_SMB extends UnitTestCase{
+	/**
+	 * @var OC_User_IMAP $instance
+	 */
+	private $instance;
+	
+	private function getConfig(){
+		return include(__DIR__.'/config.php');
+	}
+	
+	function skip(){
+		$config=$this->getConfig();
+		$this->skipUnless($config['smb']['run']);
+	}
+	
+	function setUp(){
+		$config=$this->getConfig();
+		$this->instance=new OC_User_SMB($config['smb']['host']);
+	}
+	
+	function testLogin(){
+		$config=$this->getConfig();
+		$this->assertEqual($config['smb']['user'],$this->instance->checkPassword($config['smb']['user'],$config['smb']['password']));
+		$this->assertFalse($this->instance->checkPassword($config['smb']['user'],$config['smb']['password'].'foo'));
+	}
+}
diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php
index 330574c1d42fcbb5b6acae2ada9e00c8bb802284..3c6da47d71a157b1677ba0049009d610dfac7c59 100644
--- a/apps/user_ldap/appinfo/app.php
+++ b/apps/user_ldap/appinfo/app.php
@@ -21,15 +21,17 @@
 *
 */
 
-require_once('apps/user_ldap/lib_ldap.php');
-require_once('apps/user_ldap/user_ldap.php');
-require_once('apps/user_ldap/group_ldap.php');
+OCP\App::registerAdmin('user_ldap', 'settings');
 
-OCP\App::registerAdmin('user_ldap','settings');
+$connector = new OCA\user_ldap\lib\Connection('user_ldap');
+$userBackend  = new OCA\user_ldap\USER_LDAP();
+$userBackend->setConnector($connector);
+$groupBackend = new OCA\user_ldap\GROUP_LDAP();
+$groupBackend->setConnector($connector);
 
 // register user backend
-OC_User::useBackend( 'LDAP' );
-OC_Group::useBackend( new OC_GROUP_LDAP() );
+OC_User::useBackend($userBackend);
+OC_Group::useBackend($groupBackend);
 
 // add settings page to navigation
 $entry = array(
diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index d438c7d84dfa6b29e98ef87b54f93d7fb368a334..b9f4bdf199039592da85c8189fdf29de1a4dae45 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -21,24 +21,22 @@
  *
  */
 
-class OC_GROUP_LDAP extends OC_Group_Backend {
-// 	//group specific settings
-	protected $ldapGroupFilter;
-	protected $ldapGroupMemberAssocAttr;
-	protected $configured = false;
+namespace OCA\user_ldap;
+
+class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
+	protected $enabled = false;
 
 	protected $_group_user = array();
 	protected $_user_groups = array();
 	protected $_group_users = array();
 	protected $_groups = array();
 
-	public function __construct() {
-		$this->ldapGroupFilter          = OCP\Config::getAppValue('user_ldap', 'ldap_group_filter', '(objectClass=posixGroup)');
-		$this->ldapGroupMemberAssocAttr = OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember');
-
-		if(!empty($this->ldapGroupFilter) && !empty($this->ldapGroupMemberAssocAttr)) {
-			$this->configured = true;
+	public function setConnector(lib\Connection &$connection) {
+		parent::setConnector($connection);
+		if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) {
+			$this->enabled = false;
 		}
+		$this->enabled = true;
 	}
 
 	/**
@@ -50,31 +48,31 @@ class OC_GROUP_LDAP extends OC_Group_Backend {
 	 * Checks whether the user is member of a group or not.
 	 */
 	public function inGroup($uid, $gid) {
-		if(!$this->configured) {
+		if(!$this->enabled) {
 			return false;
 		}
 		if(isset($this->_group_user[$gid][$uid])) {
 			return $this->_group_user[$gid][$uid];
 		}
-		$dn_user = OC_LDAP::username2dn($uid);
-		$dn_group = OC_LDAP::groupname2dn($gid);
+		$dn_user = $this->username2dn($uid);
+		$dn_group = $this->groupname2dn($gid);
 		// just in case
 		if(!$dn_group || !$dn_user) {
 			return false;
 		}
 		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
-		$members = OC_LDAP::readAttribute($dn_group, $this->ldapGroupMemberAssocAttr);
+		$members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr);
 		if(!$members) {
 			return false;
 		}
 
 		//extra work if we don't get back user DNs
 		//TODO: this can be done with one LDAP query
-		if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') {
+		if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') {
 			$dns = array();
 			foreach($members as $mid) {
-				$filter = str_replace('%uid', $mid, OC_LDAP::conf('ldapLoginFilter'));
-				$ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn');
+				$filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter);
+				$ldap_users = $this->fetchListOfUsers($filter, 'dn');
 				if(count($ldap_users) < 1) {
 					continue;
 				}
@@ -96,36 +94,37 @@ class OC_GROUP_LDAP extends OC_Group_Backend {
 	 * if the user exists at all.
 	 */
 	public function getUserGroups($uid) {
-		if(!$this->configured) {
+		if(!$this->enabled) {
 			return array();
 		}
 		if(isset($this->_user_groups[$uid])) {
 			return $this->_user_groups[$uid];
 		}
-		$userDN = OC_LDAP::username2dn($uid);
+		$userDN = $this->username2dn($uid);
 		if(!$userDN) {
 			$this->_user_groups[$uid] = array();
 			return array();
 		}
 
 		//uniqueMember takes DN, memberuid the uid, so we need to distinguish
-		if((strtolower($this->ldapGroupMemberAssocAttr) == 'uniquemember')
-			|| (strtolower($this->ldapGroupMemberAssocAttr) == 'member')) {
+		if((strtolower($this->connection->ldapGroupMemberAssocAttr) == 'uniquemember')
+			|| (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'member')
+		) {
 			$uid = $userDN;
-		} else if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') {
-			$result = OC_LDAP::readAttribute($userDN, 'uid');
+		} else if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') {
+			$result = $this->readAttribute($userDN, 'uid');
 			$uid = $result[0];
 		} else {
 			// just in case
 			$uid = $userDN;
 		}
 
-		$filter = OC_LDAP::combineFilterWithAnd(array(
-			$this->ldapGroupFilter,
-			$this->ldapGroupMemberAssocAttr.'='.$uid
+		$filter = $this->combineFilterWithAnd(array(
+			$this->connection->ldapGroupFilter,
+			$this->connection->ldapGroupMemberAssocAttr.'='.$uid
 		));
-		$groups = OC_LDAP::fetchListOfGroups($filter, array(OC_LDAP::conf('ldapGroupDisplayName'),'dn'));
-		$this->_user_groups[$uid] = array_unique(OC_LDAP::ownCloudGroupNames($groups), SORT_LOCALE_STRING);
+		$groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn'));
+		$this->_user_groups[$uid] = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
 
 		return $this->_user_groups[$uid];
 	}
@@ -135,44 +134,44 @@ class OC_GROUP_LDAP extends OC_Group_Backend {
 	 * @returns array with user ids
 	 */
 	public function usersInGroup($gid) {
-		if(!$this->configured) {
+		if(!$this->enabled) {
 			return array();
 		}
 		if(isset($this->_group_users[$gid])) {
 			return $this->_group_users[$gid];
 		}
 
-		$groupDN = OC_LDAP::groupname2dn($gid);
+		$groupDN = $this->groupname2dn($gid);
 		if(!$groupDN) {
 			$this->_group_users[$gid] = array();
 			return array();
 		}
 
-		$members = OC_LDAP::readAttribute($groupDN, $this->ldapGroupMemberAssocAttr);
+		$members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr);
 		if(!$members) {
 			$this->_group_users[$gid] = array();
 			return array();
 		}
 
 		$result = array();
-		$isMemberUid = (strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid');
+		$isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid');
 		foreach($members as $member) {
 			if($isMemberUid) {
-				$filter = OCP\Util::mb_str_replace('%uid', $member, OC_LDAP::conf('ldapLoginFilter'), 'UTF-8');
-				$ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn');
+				$filter = \OCP\Util::mb_str_replace('%uid', $member, $this->connection->ldapLoginFilter, 'UTF-8');
+				$ldap_users = $this->fetchListOfUsers($filter, 'dn');
 				if(count($ldap_users) < 1) {
 					continue;
 				}
-				$result[] = OC_LDAP::dn2username($ldap_users[0]);
+				$result[] = $this->dn2username($ldap_users[0]);
 				continue;
 			} else {
-				if($ocname = OC_LDAP::dn2username($member)){
+				if($ocname = $this->dn2username($member)) {
 					$result[] = $ocname;
 				}
 			}
 		}
 		if(!$isMemberUid) {
-			$result = array_intersect($result, OCP\User::getUsers());
+			$result = array_intersect($result, \OCP\User::getUsers());
 		}
 		$this->_group_users[$gid] = array_unique($result, SORT_LOCALE_STRING);
 		return $this->_group_users[$gid];
@@ -185,12 +184,12 @@ class OC_GROUP_LDAP extends OC_Group_Backend {
 	 * Returns a list with all groups
 	 */
 	public function getGroups() {
-		if(!$this->configured) {
+		if(!$this->enabled) {
 			return array();
 		}
 		if(empty($this->_groups)) {
-			$ldap_groups = OC_LDAP::fetchListOfGroups($this->ldapGroupFilter, array(OC_LDAP::conf('ldapGroupDisplayName'), 'dn'));
-			$this->_groups = OC_LDAP::ownCloudGroupNames($ldap_groups);
+			$ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn'));
+			$this->_groups = $this->ownCloudGroupNames($ldap_groups);
 		}
 		return $this->_groups;
 	}
@@ -203,4 +202,17 @@ class OC_GROUP_LDAP extends OC_Group_Backend {
 	public function groupExists($gid){
 		return in_array($gid, $this->getGroups());
 	}
+
+	/**
+	* @brief Check if backend implements actions
+	* @param $actions bitwise-or'ed actions
+	* @returns boolean
+	*
+	* Returns the supported actions as int to be
+	* compared with OC_USER_BACKEND_CREATE_USER etc.
+	*/
+	public function implementsActions($actions) {
+		//always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now.
+		return false;
+	}
 }
\ No newline at end of file
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
new file mode 100644
index 0000000000000000000000000000000000000000..870f6330edd01d48de753cd5ecb62f8bec82d886
--- /dev/null
+++ b/apps/user_ldap/lib/access.php
@@ -0,0 +1,593 @@
+<?php
+
+/**
+ * ownCloud – LDAP Access
+ *
+ * @author Arthur Schiwon
+ * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+abstract class Access {
+	protected $connection;
+
+	public function setConnector(Connection &$connection) {
+		$this->connection = $connection;
+	}
+
+	private function checkConnection() {
+		return ($this->connection instanceof Connection);
+	}
+
+	/**
+	 * @brief reads a given attribute for an LDAP record identified by a DN
+	 * @param $dn the record in question
+	 * @param $attr the attribute that shall be retrieved
+	 * @returns the values in an array on success, false otherwise
+	 *
+	 * Reads an attribute from an LDAP entry
+	 */
+	public function readAttribute($dn, $attr) {
+		if(!$this->checkConnection()) {
+			\OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN);
+			return false;
+		}
+		$cr = $this->connection->getConnectionResource();
+		$rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr));
+		if(!is_resource($rr)) {
+			\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
+			//in case an error occurs , e.g. object does not exist
+			return false;
+		}
+		$er = ldap_first_entry($cr, $rr);
+		//LDAP attributes are not case sensitive
+		$result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
+		$attr = mb_strtolower($attr, 'UTF-8');
+
+		if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
+			$values = array();
+			for($i=0;$i<$result[$attr]['count'];$i++) {
+				$values[] = $this->resemblesDN($attr) ? $this->sanitizeDN($result[$attr][$i]) : $result[$attr][$i];
+			}
+			return $values;
+		}
+		return false;
+	}
+
+	/**
+	 * @brief checks wether the given attribute`s valua is probably a DN
+	 * @param $attr the attribute in question
+	 * @return if so true, otherwise false
+	 */
+	private function resemblesDN($attr) {
+		$resemblingAttributes = array(
+			'dn',
+			'uniquemember',
+			'member'
+		);
+		return in_array($attr, $resemblingAttributes);
+	}
+
+	/**
+	 * @brief sanitizes a DN received from the LDAP server
+	 * @param $dn the DN in question
+	 * @return the sanitized DN
+	 */
+	private function sanitizeDN($dn) {
+		//OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this!
+		$dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
+
+		//make comparisons and everything work
+		$dn = mb_strtolower($dn, 'UTF-8');
+
+		return $dn;
+	}
+
+	/**
+	 * gives back the database table for the query
+	 */
+	private function getMapTable($isUser) {
+		if($isUser) {
+			return '*PREFIX*ldap_user_mapping';
+		} else {
+			return '*PREFIX*ldap_group_mapping';
+		}
+	}
+
+	/**
+	 * @brief returns the LDAP DN for the given internal ownCloud name of the group
+	 * @param $name the ownCloud name in question
+	 * @returns string with the LDAP DN on success, otherwise false
+	 *
+	 * returns the LDAP DN for the given internal ownCloud name of the group
+	 */
+	public function groupname2dn($name) {
+		return $this->ocname2dn($name, false);
+	}
+
+	/**
+	 * @brief returns the LDAP DN for the given internal ownCloud name of the user
+	 * @param $name the ownCloud name in question
+	 * @returns string with the LDAP DN on success, otherwise false
+	 *
+	 * returns the LDAP DN for the given internal ownCloud name of the user
+	 */
+	public function username2dn($name) {
+		$dn = $this->ocname2dn($name, true);
+		if($dn) {
+			return $dn;
+		} else {
+			//fallback: user is not mapped
+			$filter = $this->combineFilterWithAnd(array(
+				$this->connection->ldapUserFilter,
+				$this->connection->ldapUserDisplayName . '=' . $name,
+			));
+			$result = $this->searchUsers($filter, 'dn');
+			if(isset($result[0]['dn'])) {
+				$this->mapComponent($result[0], $name, true);
+				return $result[0];
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * @brief returns the LDAP DN for the given internal ownCloud name
+	 * @param $name the ownCloud name in question
+	 * @param $isUser is it a user? otherwise group
+	 * @returns string with the LDAP DN on success, otherwise false
+	 *
+	 * returns the LDAP DN for the given internal ownCloud name
+	 */
+	private function ocname2dn($name, $isUser) {
+		$table = $this->getMapTable($isUser);
+
+		$query = \OCP\DB::prepare('
+			SELECT ldap_dn
+			FROM '.$table.'
+			WHERE owncloud_name = ?
+		');
+
+		$record = $query->execute(array($name))->fetchOne();
+		return $record;
+	}
+
+	/**
+	 * @brief returns the internal ownCloud name for the given LDAP DN of the group
+	 * @param $dn the dn of the group object
+	 * @param $ldapname optional, the display name of the object
+	 * @returns string with with the name to use in ownCloud, false on DN outside of search DN
+	 *
+	 * returns the internal ownCloud name for the given LDAP DN of the group
+	 */
+	public function dn2groupname($dn, $ldapname = null) {
+		if(mb_strripos($dn, $this->connection->ldapBaseGroups, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->connection->ldapBaseGroups, 'UTF-8'))) {
+			return false;
+		}
+		return $this->dn2ocname($dn, $ldapname, false);
+	}
+
+	/**
+	 * @brief returns the internal ownCloud name for the given LDAP DN of the user
+	 * @param $dn the dn of the user object
+	 * @param $ldapname optional, the display name of the object
+	 * @returns string with with the name to use in ownCloud
+	 *
+	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN
+	 */
+	public function dn2username($dn, $ldapname = null) {
+		if(mb_strripos($dn, $this->connection->ldapBaseUsers, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->connection->ldapBaseUsers, 'UTF-8'))) {
+			return false;
+		}
+		return $this->dn2ocname($dn, $ldapname, true);
+	}
+
+	/**
+	 * @brief returns an internal ownCloud name for the given LDAP DN
+	 * @param $dn the dn of the user object
+	 * @param $ldapname optional, the display name of the object
+	 * @param $isUser optional, wether it is a user object (otherwise group assumed)
+	 * @returns string with with the name to use in ownCloud
+	 *
+	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN
+	 */
+	public function dn2ocname($dn, $ldapname = null, $isUser = true) {
+		$dn = $this->sanitizeDN($dn);
+		$table = $this->getMapTable($isUser);
+		if($isUser) {
+			$nameAttribute = $this->connection->ldapUserDisplayName;
+		} else {
+			$nameAttribute = $this->connection->ldapGroupDisplayName;
+		}
+
+		$query = \OCP\DB::prepare('
+			SELECT owncloud_name
+			FROM '.$table.'
+			WHERE ldap_dn = ?
+		');
+
+		$component = $query->execute(array($dn))->fetchOne();
+		if($component) {
+			return $component;
+		}
+
+		if(is_null($ldapname)) {
+			$ldapname = $this->readAttribute($dn, $nameAttribute);
+			$ldapname = $ldapname[0];
+		}
+		$ldapname = $this->sanitizeUsername($ldapname);
+
+		//a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot.
+		if($this->mapComponent($dn, $ldapname, $isUser)) {
+			return $ldapname;
+		}
+
+		//doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located.
+		$oc_name = $this->alternateOwnCloudName($ldapname, $dn);
+		if($this->mapComponent($dn, $oc_name, $isUser)) {
+			return $oc_name;
+		}
+
+		//TODO: do not simple die away!
+		//and this of course should never been thrown :)
+		throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.');
+	}
+
+	/**
+	 * @brief gives back the user names as they are used ownClod internally
+	 * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... )
+	 * @returns an array with the user names to use in ownCloud
+	 *
+	 * gives back the user names as they are used ownClod internally
+	 */
+	public function ownCloudUserNames($ldapUsers) {
+		return $this->ldap2ownCloudNames($ldapUsers, true);
+	}
+
+	/**
+	 * @brief gives back the group names as they are used ownClod internally
+	 * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... )
+	 * @returns an array with the group names to use in ownCloud
+	 *
+	 * gives back the group names as they are used ownClod internally
+	 */
+	public function ownCloudGroupNames($ldapGroups) {
+		return $this->ldap2ownCloudNames($ldapGroups, false);
+	}
+
+	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
+		if($isUsers) {
+			$knownObjects = $this->mappedUsers();
+			$nameAttribute = $this->connection->ldapUserDisplayName;
+		} else {
+			$knownObjects = $this->mappedGroups();
+			$nameAttribute = $this->connection->ldapGroupDisplayName;
+		}
+		$ownCloudNames = array();
+
+		foreach($ldapObjects as $ldapObject) {
+			$key = \OCP\Util::recursiveArraySearch($knownObjects, $ldapObject['dn']);
+
+			//everything is fine when we know the group
+			if($key !== false) {
+				$ownCloudNames[] = $knownObjects[$key]['owncloud_name'];
+				continue;
+			}
+
+			//a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters.
+			$ocname = $this->sanitizeUsername($ldapObject[$nameAttribute]);
+			if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) {
+				$ownCloudNames[] = $ocname;
+				continue;
+			}
+
+			//doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located.
+			$ocname = $this->alternateOwnCloudName($ocname, $ldapObject['dn']);
+			if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) {
+				$ownCloudNames[] = $ocname;
+				continue;
+			}
+
+			//TODO: do not simple die away
+			//and this of course should never been thrown :)
+			throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.');
+		}
+		return $ownCloudNames;
+	}
+
+	/**
+	 * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
+	 * @param $name the display name of the object
+	 * @param $dn the dn of the object
+	 * @returns string with with the name to use in ownCloud
+	 *
+	 * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
+	 */
+	private function alternateOwnCloudName($name, $dn) {
+		$ufn = ldap_dn2ufn($dn);
+		$name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8'));
+		$name = $this->sanitizeUsername($name);
+		return $name;
+	}
+
+	/**
+	 * @brief retrieves all known groups from the mappings table
+	 * @returns array with the results
+	 *
+	 * retrieves all known groups from the mappings table
+	 */
+	private function mappedGroups() {
+		return $this->mappedComponents(false);
+	}
+
+	/**
+	 * @brief retrieves all known users from the mappings table
+	 * @returns array with the results
+	 *
+	 * retrieves all known users from the mappings table
+	 */
+	private function mappedUsers() {
+		return $this->mappedComponents(true);
+	}
+
+	private function mappedComponents($isUsers) {
+		$table = $this->getMapTable($isUsers);
+
+		$query = \OCP\DB::prepare('
+			SELECT ldap_dn, owncloud_name
+			FROM '. $table
+		);
+
+		return $query->execute()->fetchAll();
+	}
+
+	/**
+	 * @brief inserts a new user or group into the mappings table
+	 * @param $dn the record in question
+	 * @param $ocname the name to use in ownCloud
+	 * @param $isUser is it a user or a group?
+	 * @returns true on success, false otherwise
+	 *
+	 * inserts a new user or group into the mappings table
+	 */
+	private function mapComponent($dn, $ocname, $isUser = true) {
+		$table = $this->getMapTable($isUser);
+		$dn = $this->sanitizeDN($dn);
+
+		$sqlAdjustment = '';
+		$dbtype = \OCP\Config::getSystemValue('dbtype');
+		if($dbtype == 'mysql') {
+			$sqlAdjustment = 'FROM dual';
+		}
+
+		$insert = \OCP\DB::prepare('
+			INSERT INTO '.$table.' (ldap_dn, owncloud_name)
+				SELECT ?,?
+				'.$sqlAdjustment.'
+				WHERE NOT EXISTS (
+					SELECT 1
+					FROM '.$table.'
+					WHERE ldap_dn = ?
+						OR owncloud_name = ? )
+		');
+
+		$res = $insert->execute(array($dn, $ocname, $dn, $ocname));
+
+		if(\OCP\DB::isError($res)) {
+			return false;
+		}
+
+		$insRows = $res->numRows();
+
+		if($insRows == 0) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public function fetchListOfUsers($filter, $attr) {
+		return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1));
+	}
+
+	public function fetchListOfGroups($filter, $attr) {
+		return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1));
+	}
+
+	private function fetchList($list, $manyAttributes) {
+		if(is_array($list)) {
+			if($manyAttributes) {
+				return $list;
+			} else {
+				return array_unique($list, SORT_LOCALE_STRING);
+			}
+		}
+
+		//error cause actually, maybe throw an exception in future.
+		return array();
+	}
+
+	/**
+	 * @brief executes an LDAP search, optimized for Users
+	 * @param $filter the LDAP filter for the search
+	 * @param $attr optional, when a certain attribute shall be filtered out
+	 * @returns array with the search result
+	 *
+	 * Executes an LDAP search
+	 */
+	public function searchUsers($filter, $attr = null) {
+		return $this->search($filter, $this->connection->ldapBaseUsers, $attr);
+	}
+
+	/**
+	 * @brief executes an LDAP search, optimized for Groups
+	 * @param $filter the LDAP filter for the search
+	 * @param $attr optional, when a certain attribute shall be filtered out
+	 * @returns array with the search result
+	 *
+	 * Executes an LDAP search
+	 */
+	public function searchGroups($filter, $attr = null) {
+		return $this->search($filter, $this->connection->ldapBaseGroups, $attr);
+	}
+
+	/**
+	 * @brief executes an LDAP search
+	 * @param $filter the LDAP filter for the search
+	 * @param $base the LDAP subtree that shall be searched
+	 * @param $attr optional, when a certain attribute shall be filtered out
+	 * @returns array with the search result
+	 *
+	 * Executes an LDAP search
+	 */
+	private function search($filter, $base, $attr = null) {
+		if(!is_null($attr) && !is_array($attr)) {
+			$attr = array(mb_strtolower($attr, 'UTF-8'));
+		}
+
+		// See if we have a resource
+		$link_resource = $this->connection->getConnectionResource();
+		if(is_resource($link_resource)) {
+			$sr = ldap_search($link_resource, $base, $filter, $attr);
+			$findings = ldap_get_entries($link_resource, $sr );
+
+			// if we're here, probably no connection resource is returned.
+			// to make ownCloud behave nicely, we simply give back an empty array.
+			if(is_null($findings)) {
+				return array();
+			}
+		} else {
+			// Seems like we didn't find any resource.
+			// Return an empty array just like before.
+			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
+			return array();
+		}
+
+		if(!is_null($attr)) {
+			$selection = array();
+			$multiarray = false;
+			if(count($attr) > 1) {
+				$multiarray = true;
+				$i = 0;
+			}
+			foreach($findings as $item) {
+				if(!is_array($item)) {
+					continue;
+				}
+				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
+
+				if($multiarray) {
+					foreach($attr as $key) {
+						$key = mb_strtolower($key, 'UTF-8');
+						if(isset($item[$key])) {
+							if($key != 'dn') {
+								$selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0];
+							} else {
+								$selection[$i][$key] = $this->sanitizeDN($item[$key]);
+							}
+						}
+
+					}
+					$i++;
+				} else {
+					//tribute to case insensitivity
+					$key = mb_strtolower($attr[0], 'UTF-8');
+
+					if(isset($item[$key])) {
+						if($this->resemblesDN($key)) {
+							$selection[] = $this->sanitizeDN($item[$key]);
+						} else {
+							$selection[] = $item[$key];
+						}
+					}
+				}
+			}
+			return $selection;
+		}
+		return $findings;
+	}
+
+	public function sanitizeUsername($name) {
+		if($this->connection->ldapIgnoreNamingRules) {
+			return $name;
+		}
+
+		//REPLACEMENTS
+		$name = \OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8');
+
+		//every remaining unallowed characters will be removed
+		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
+
+		return $name;
+	}
+
+	/**
+	 * @brief combines the input filters with AND
+	 * @param $filters array, the filters to connect
+	 * @returns the combined filter
+	 *
+	 * Combines Filter arguments with AND
+	 */
+	public function combineFilterWithAnd($filters) {
+		return $this->combineFilter($filters, '&');
+	}
+
+	/**
+	 * @brief combines the input filters with AND
+	 * @param $filters array, the filters to connect
+	 * @returns the combined filter
+	 *
+	 * Combines Filter arguments with AND
+	 */
+	public function combineFilterWithOr($filters) {
+		return $this->combineFilter($filters, '|');
+	}
+
+	/**
+	 * @brief combines the input filters with given operator
+	 * @param $filters array, the filters to connect
+	 * @param $operator either & or |
+	 * @returns the combined filter
+	 *
+	 * Combines Filter arguments with AND
+	 */
+	private function combineFilter($filters, $operator) {
+		$combinedFilter = '('.$operator;
+		foreach($filters as $filter) {
+		    if($filter[0] != '(') {
+				$filter = '('.$filter.')';
+		    }
+		    $combinedFilter.=$filter;
+		}
+		$combinedFilter.=')';
+		return $combinedFilter;
+	}
+
+	public function areCredentialsValid($name, $password) {
+		$testConnection = clone $this->connection;
+		$credentials = array(
+			'ldapAgentName' => $name,
+			'ldapAgentPassword' => $password
+		);
+		if(!$testConnection->setConfiguration($credentials)) {
+			return false;
+		}
+		return $testConnection->bind();
+	}
+}
\ No newline at end of file
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
new file mode 100644
index 0000000000000000000000000000000000000000..c8ba9dad70ef857ec6d56d83fd395cc378ae7f6f
--- /dev/null
+++ b/apps/user_ldap/lib/connection.php
@@ -0,0 +1,245 @@
+<?php
+
+/**
+ * ownCloud – LDAP Access
+ *
+ * @author Arthur Schiwon
+ * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+class Connection {
+	private $ldapConnectionRes = null;
+	private $configID;
+	private $configured = false;
+
+	//cached settings
+	protected $config = array(
+		'ldapHost' => null,
+		'ldapPort' => null,
+		'ldapBase' => null,
+		'ldapBaseUsers' => null,
+		'ldapBaseGroups' => null,
+		'ldapAgentName' => null,
+		'ldapAgentPassword' => null,
+		'ldapTLS' => null,
+		'ldapNoCase' => null,
+		'ldapIgnoreNamingRules' => null,
+		'ldapUserDisplayName' => null,
+		'ldapUserFilter' => null,
+		'ldapGroupFilter' => null,
+		'ldapGroupDisplayName' => null,
+		'ldapLoginFilter' => null,
+		'ldapQuotaAttribute' => null,
+		'ldapQuotaDefault' => null,
+		'ldapEmailAttribute' => null,
+	);
+
+	public function __construct($configID = 'user_ldap') {
+		$this->configID = $configID;
+	}
+
+	public function __destruct() {
+		@ldap_unbind($this->ldapConnectionRes);
+	}
+
+	public function __get($name) {
+		if(!$this->configured) {
+			$this->readConfiguration();
+		}
+
+		if(isset($this->config[$name])) {
+			return $this->config[$name];
+		}
+	}
+
+	/**
+	 * @brief initializes the LDAP backend
+	 * @param $force read the config settings no matter what
+	 *
+	 * initializes the LDAP backend
+	 */
+	public function init($force = false) {
+		$this->readConfiguration($force);
+		$this->establishConnection();
+	}
+
+	/**
+	 * Returns the LDAP handler
+	 */
+	public function getConnectionResource() {
+		if(!$this->ldapConnectionRes) {
+			$this->init();
+		}
+		if(is_null($this->ldapConnectionRes)) {
+			\OCP\Util::writeLog('user_ldap', 'Connection could not be established', \OCP\Util::ERROR);
+		}
+		return $this->ldapConnectionRes;
+	}
+
+	/**
+	 * Caches the general LDAP configuration.
+	 */
+	private function readConfiguration($force = false) {
+		\OCP\Util::writeLog('user_ldap','Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG);
+		if((!$this->configured || $force) && !is_null($this->configID)) {
+			\OCP\Util::writeLog('user_ldap','Reading the configuration', \OCP\Util::DEBUG);
+			$this->config['ldapHost']              = \OCP\Config::getAppValue($this->configID, 'ldap_host', '');
+			$this->config['ldapPort']              = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389);
+			$this->config['ldapAgentName']         = \OCP\Config::getAppValue($this->configID, 'ldap_dn','');
+			$this->config['ldapAgentPassword']     = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password',''));
+			$this->config['ldapBase']              = \OCP\Config::getAppValue($this->configID, 'ldap_base', '');
+			$this->config['ldapBaseUsers']         = \OCP\Config::getAppValue($this->configID, 'ldap_base_users',$this->config['ldapBase']);
+			$this->config['ldapBaseGroups']        = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']);
+			$this->config['ldapTLS']               = \OCP\Config::getAppValue($this->configID, 'ldap_tls',0);
+			$this->config['ldapNoCase']            = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0);
+			$this->config['ldapUserDisplayName']   = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8');
+			$this->config['ldapUserFilter']        = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter','objectClass=person');
+			$this->config['ldapGroupFilter']        = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter','(objectClass=posixGroup)');
+			$this->config['ldapLoginFilter']       = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)');
+			$this->config['ldapGroupDisplayName']  = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8');
+			$this->config['ldapQuotaAttribute']    = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', '');
+			$this->config['ldapQuotaDefault']      = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', '');
+			$this->config['ldapEmailAttribute']      = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', '');
+			$this->config['ldapGroupMemberAssocAttr']      = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember');
+			$this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false);
+
+			$this->configured = $this->validateConfiguration();
+		}
+	}
+
+	/**
+	 * @brief set LDAP configuration with values delivered by an array, not read from configuration
+	 * @param $config array that holds the config parameters in an associated array
+	 * @param &$setParameters optional; array where the set fields will be given to
+	 * @return true if config validates, false otherwise. Check with $setParameters for detailed success on single parameters
+	 */
+	public function setConfiguration($config, &$setParameters = null) {
+		if(!is_array($config)) {
+			return false;
+		}
+
+		foreach($config as $parameter => $value) {
+		    if(isset($this->config[$parameter])) {
+				$this->config[$parameter] = $value;
+				if(is_array($setParameters)) {
+					$setParameters[] = $parameter;
+				}
+		    }
+		}
+
+		$this->configured = $this->validateConfiguration();
+
+		return $this->configured;
+	}
+
+	/**
+	 * @brief Validates the user specified configuration
+	 * @returns true if configuration seems OK, false otherwise
+	 */
+	private function validateConfiguration() {
+		//first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message
+		if(empty($this->config['ldapBaseUsers'])) {
+			\OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO);
+			$this->config['ldapBaseUsers'] = $this->config['ldapBase'];
+		}
+		if(empty($this->config['ldapBaseGroups'])) {
+			\OCP\Util::writeLog('user_ldap', 'Base tree for Groups is empty, using Base DN', \OCP\Util::INFO);
+			$this->config['ldapBaseGroups'] = $this->config['ldapBase'];
+		}
+		if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) {
+			\OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO);
+		}
+
+		//second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning.
+		$configurationOK = true;
+		if(empty($this->config['ldapHost'])) {
+			\OCP\Util::writeLog('user_ldap', 'No LDAP host given, won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		if(empty($this->config['ldapPort'])) {
+			\OCP\Util::writeLog('user_ldap', 'No LDAP Port given, won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword']))
+			|| (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) {
+			\OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		//TODO: check if ldapAgentName is in DN form
+		if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) {
+			\OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		if(empty($this->config['ldapUserDisplayName'])) {
+			\OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		if(empty($this->config['ldapGroupDisplayName'])) {
+			\OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		if(empty($this->config['ldapLoginFilter'])) {
+			\OCP\Util::writeLog('user_ldap', 'No login filter specified, won`t connect.', \OCP\Util::WARN);
+			$configurationOK = false;
+		}
+		if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) {
+			\OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN);
+			\OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG);
+			$configurationOK = false;
+		}
+
+		return $configurationOK;
+	}
+
+	/**
+	 * Connects and Binds to LDAP
+	 */
+	private function establishConnection() {
+		if(!$this->configured) {
+			\OCP\Util::writeLog('user_ldap', 'Configuration is invalid, cannot connect', \OCP\Util::WARN);
+			return false;
+		}
+		if(!$this->ldapConnectionRes) {
+			$this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']);
+			if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+					if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
+						if($this->config['ldapTLS']) {
+							ldap_start_tls($this->ldapConnectionRes);
+						}
+					}
+			}
+
+			return $this->bind();
+		}
+	}
+
+	/**
+	 * Binds to LDAP
+	 */
+	public function bind() {
+		$ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']);
+		if(!$ldapLogin) {
+			\OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR);
+			$this->ldapConnectionRes = null;
+			return false;
+		}
+		return true;
+	}
+
+}
\ No newline at end of file
diff --git a/apps/user_ldap/lib_ldap.php b/apps/user_ldap/lib_ldap.php
deleted file mode 100644
index 08b09304d780cd224e680010011582a05f131aaa..0000000000000000000000000000000000000000
--- a/apps/user_ldap/lib_ldap.php
+++ /dev/null
@@ -1,721 +0,0 @@
-<?php
-
-/**
- * ownCloud – LDAP lib
- *
- * @author Arthur Schiwon
- * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-define('LDAP_GROUP_MEMBER_ASSOC_ATTR','uniqueMember');
-define('LDAP_GROUP_DISPLAY_NAME_ATTR','cn');
-
-//needed to unbind, because we use OC_LDAP only statically
-class OC_LDAP_DESTRUCTOR {
-	public function __destruct() {
-		OC_LDAP::destruct();
-	}
-}
-
-class OC_LDAP {
-	static protected $ldapConnectionRes = false;
-	static protected $configured = false;
-
-	//cached settings
-	static protected $ldapHost;
-	static protected $ldapPort;
-	static protected $ldapBase;
-	static protected $ldapBaseUsers;
-	static protected $ldapBaseGroups;
-	static protected $ldapAgentName;
-	static protected $ldapAgentPassword;
-	static protected $ldapTLS;
-	static protected $ldapNoCase;
-	static protected $ldapIgnoreNamingRules;
-	// user and group settings, that are needed in both backends
-	static protected $ldapUserDisplayName;
-	static protected $ldapUserFilter;
-	static protected $ldapGroupDisplayName;
-	static protected $ldapLoginFilter;
-
-	static protected $__d;
-
-	/**
-	 * @brief initializes the LDAP backend
-	 * @param $force read the config settings no matter what
-	 *
-	 * initializes the LDAP backend
-	 */
-	static public function init($force = false) {
-		if(is_null(self::$__d)) {
-			self::$__d = new OC_LDAP_DESTRUCTOR();
-		}
-		self::readConfiguration($force);
-		self::establishConnection();
-	}
-
-	static public function destruct() {
-		@ldap_unbind(self::$ldapConnectionRes);
-	}
-
-	/**
-	 * @brief returns a read-only configuration value
-	 * @param $key the name of the configuration value
-	 * @returns the value on success, otherwise null
-	 *
-	 * returns a read-only configuration values
-	 *
-	 * we cannot work with getters, because it is a static class
-	 */
-	static public function conf($key) {
-		if(!self::$configured) {
-			self::init();
-		}
-
-		$availableProperties = array(
-			'ldapUserDisplayName',
-			'ldapGroupDisplayName',
-			'ldapLoginFilter'
-		);
-
-		if(in_array($key, $availableProperties)) {
-			return self::$$key;
-		}
-
-		return null;
-	}
-
-	/**
-	 * gives back the database table for the query
-	 */
-	static private function getMapTable($isUser) {
-		if($isUser) {
-			return '*PREFIX*ldap_user_mapping';
-		} else {
-			return '*PREFIX*ldap_group_mapping';
-		}
-	}
-
-	/**
-	 * @brief returns the LDAP DN for the given internal ownCloud name of the group
-	 * @param $name the ownCloud name in question
-	 * @returns string with the LDAP DN on success, otherwise false
-	 *
-	 * returns the LDAP DN for the given internal ownCloud name of the group
-	 */
-	static public function groupname2dn($name) {
-		return self::ocname2dn($name, false);
-	}
-
-	/**
-	 * @brief returns the LDAP DN for the given internal ownCloud name of the user
-	 * @param $name the ownCloud name in question
-	 * @returns string with the LDAP DN on success, otherwise false
-	 *
-	 * returns the LDAP DN for the given internal ownCloud name of the user
-	 */
-	static public function username2dn($name) {
-		$dn = self::ocname2dn($name, true);
-		if($dn) {
-			return $dn;
-		} else {
-			//fallback: user is not mapped
-			self::init();
-			$filter = self::combineFilterWithAnd(array(
-				self::$ldapUserFilter,
-				self::$ldapUserDisplayName . '=' . $name,
-			));
-			$result = self::searchUsers($filter, 'dn');
-			if(isset($result[0]['dn'])) {
-				self::mapUser($result[0], $name);
-				return $result[0];
-			}
-		}
-
-		return false;
-	}
-
-	static private function ocname2dn($name, $isUser) {
-		$table = self::getMapTable($isUser);
-
-		$query = OCP\DB::prepare('
-			SELECT ldap_dn
-			FROM '.$table.'
-			WHERE owncloud_name = ?
-		');
-
-		$record = $query->execute(array($name))->fetchOne();
-		return $record;
-	}
-
-	/**
-	 * @brief returns the internal ownCloud name for the given LDAP DN of the group
-	 * @param $dn the dn of the group object
-	 * @param $ldapname optional, the display name of the object
-	 * @returns string with with the name to use in ownCloud, false on DN outside of search DN
-	 *
-	 * returns the internal ownCloud name for the given LDAP DN of the group
-	 */
-	static public function dn2groupname($dn, $ldapname = null) {
-		if(mb_strripos($dn, self::$ldapBaseGroups, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen(self::$ldapBaseGroups, 'UTF-8'))) {
-			return false;
-		}
-		return self::dn2ocname($dn, $ldapname, false);
-	}
-
-	/**
-	 * @brief returns the internal ownCloud name for the given LDAP DN of the user
-	 * @param $dn the dn of the user object
-	 * @param $ldapname optional, the display name of the object
-	 * @returns string with with the name to use in ownCloud
-	 *
-	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN
-	 */
-	static public function dn2username($dn, $ldapname = null) {
-		if(mb_strripos($dn, self::$ldapBaseUsers, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen(self::$ldapBaseUsers, 'UTF-8'))) {
-			return false;
-		}
-		return self::dn2ocname($dn, $ldapname, true);
-	}
-
-	static public function dn2ocname($dn, $ldapname = null, $isUser = true) {
-		$dn = self::sanitizeDN($dn);
-		$table = self::getMapTable($isUser);
-		if($isUser) {
-			$nameAttribute = self::conf('ldapUserDisplayName');
-		} else {
-			$nameAttribute = self::conf('ldapGroupDisplayName');
-		}
-
-		$query = OCP\DB::prepare('
-			SELECT owncloud_name
-			FROM '.$table.'
-			WHERE ldap_dn = ?
-		');
-
-		$component = $query->execute(array($dn))->fetchOne();
-		if($component) {
-			return $component;
-		}
-
-		if(is_null($ldapname)) {
-			$ldapname = self::readAttribute($dn, $nameAttribute);
-			$ldapname = $ldapname[0];
-		}
-		$ldapname = self::sanitizeUsername($ldapname);
-
-		//a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot.
-		if(self::mapComponent($dn, $ldapname, $isUser)) {
-			return $ldapname;
-		}
-
-		//doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located.
-		$oc_name = self::alternateOwnCloudName($ldapname, $dn);
-		if(self::mapComponent($dn, $oc_name, $isUser)) {
-			return $oc_name;
-		}
-
-		//and this of course should never been thrown :)
-		throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.');
-	}
-
-	/**
-	 * @brief gives back the user names as they are used ownClod internally
-	 * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... )
-	 * @returns an array with the user names to use in ownCloud
-	 *
-	 * gives back the user names as they are used ownClod internally
-	 */
-	static public function ownCloudUserNames($ldapUsers) {
-		return self::ldap2ownCloudNames($ldapUsers, true);
-	}
-
-	/**
-	 * @brief gives back the group names as they are used ownClod internally
-	 * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... )
-	 * @returns an array with the group names to use in ownCloud
-	 *
-	 * gives back the group names as they are used ownClod internally
-	 */
-	static public function ownCloudGroupNames($ldapGroups) {
-		return self::ldap2ownCloudNames($ldapGroups, false);
-	}
-
-	static private function ldap2ownCloudNames($ldapObjects, $isUsers) {
-		if($isUsers) {
-			$knownObjects = self::mappedUsers();
-			$nameAttribute = self::conf('ldapUserDisplayName');
-		} else {
-			$knownObjects = self::mappedGroups();
-			$nameAttribute = self::conf('ldapGroupDisplayName');
-		}
-		$ownCloudNames = array();
-
-		foreach($ldapObjects as $ldapObject) {
-			$key = self::recursiveArraySearch($knownObjects, $ldapObject['dn']);
-
-			//everything is fine when we know the group
-			if($key !== false) {
-				$ownCloudNames[] = $knownObjects[$key]['owncloud_name'];
-				continue;
-			}
-
-			//a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters.
-			$ocname = self::sanitizeUsername($ldapObject[$nameAttribute]);
-			if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) {
-				$ownCloudNames[] = $ocname;
-				continue;
-			}
-
-			//doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located.
-			$ocname = self::alternateOwnCloudName($ocname, $ldapObject['dn']);
-			if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) {
-				$ownCloudNames[] = $ocname;
-				continue;
-			}
-
-			//and this of course should never been thrown :)
-			throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.');
-		}
-		return $ownCloudNames;
-	}
-
-	/**
-	 * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
-	 * @param $name the display name of the object
-	 * @param $dn the dn of the object
-	 * @returns string with with the name to use in ownCloud
-	 *
-	 * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
-	 */
-	static private function alternateOwnCloudName($name, $dn) {
-		$ufn = ldap_dn2ufn($dn);
-		$name = $name . '@' . trim(OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8'));
-		$name = self::sanitizeUsername($name);
-		return $name;
-	}
-
-	/**
-	 * @brief retrieves all known groups from the mappings table
-	 * @returns array with the results
-	 *
-	 * retrieves all known groups from the mappings table
-	 */
-	static private function mappedGroups() {
-		return self::mappedComponents(false);
-	}
-
-	/**
-	 * @brief retrieves all known users from the mappings table
-	 * @returns array with the results
-	 *
-	 * retrieves all known users from the mappings table
-	 */
-	static private function mappedUsers() {
-		return self::mappedComponents(true);
-	}
-
-	static private function mappedComponents($isUsers) {
-		$table = self::getMapTable($isUsers);
-
-		$query = OCP\DB::prepare('
-			SELECT ldap_dn, owncloud_name
-			FROM '. $table
-		);
-
-		return $query->execute()->fetchAll();
-	}
-
-	/**
-	 * @brief inserts a new user or group into the mappings table
-	 * @param $dn the record in question
-	 * @param $ocname the name to use in ownCloud
-	 * @param $isUser is it a user or a group?
-	 * @returns true on success, false otherwise
-	 *
-	 * inserts a new user or group into the mappings table
-	 */
-	static private function mapComponent($dn, $ocname, $isUser = true) {
-		$table = self::getMapTable($isUser);
-		$dn = self::sanitizeDN($dn);
-
-		$sqlAdjustment = '';
-		$dbtype = OCP\Config::getSystemValue('dbtype');
-		if($dbtype == 'mysql') {
-			$sqlAdjustment = 'FROM dual';
-		}
-
-		$insert = OCP\DB::prepare('
-			INSERT INTO '.$table.' (ldap_dn, owncloud_name)
-				SELECT ?,?
-				'.$sqlAdjustment.'
-				WHERE NOT EXISTS (
-					SELECT 1
-					FROM '.$table.'
-					WHERE ldap_dn = ?
-						OR owncloud_name = ? )
-		');
-
-		$res = $insert->execute(array($dn, $ocname, $dn, $ocname));
-
-		if(OCP\DB::isError($res)) {
-			return false;
-		}
-
-		$insRows = $res->numRows();
-
-		if($insRows == 0) {
-			return false;
-		}
-
-		return true;
-	}
-
-	static public function fetchListOfUsers($filter, $attr) {
-		return self::fetchList(OC_LDAP::searchUsers($filter, $attr), (count($attr) > 1));
-	}
-
-	static public function fetchListOfGroups($filter, $attr) {
-		return self::fetchList(OC_LDAP::searchGroups($filter, $attr), (count($attr) > 1));
-	}
-
-	static private function fetchList($list, $manyAttributes) {
-		if(is_array($list)) {
-			if($manyAttributes) {
-				return $list;
-			} else {
-				return array_unique($list, SORT_LOCALE_STRING);
-			}
-		}
-
-		//error cause actually, maybe throw an exception in future.
-		return array();
-	}
-
-	/**
-	 * @brief reads a given attribute for an LDAP record identified by a DN
-	 * @param $dn the record in question
-	 * @param $attr the attribute that shall be retrieved
-	 * @returns the values in an array on success, false otherwise
-	 *
-	 * Reads an attribute from an LDAP entry
-	 */
-	static public function readAttribute($dn, $attr) {
-		$cr = self::getConnectionResource();
-		$rr = ldap_read($cr, $dn, 'objectClass=*', array($attr));
-		$er = ldap_first_entry($cr, $rr);
-		//LDAP attributes are not case sensitive
-		$result = OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
-		$attr = mb_strtolower($attr, 'UTF-8');
-
-		if(isset($result[$attr]) && $result[$attr]['count'] > 0){
-			$values = array();
-			for($i=0;$i<$result[$attr]['count'];$i++) {
-				$values[] = self::resemblesDN($attr) ? self::sanitizeDN($result[$attr][$i]) : $result[$attr][$i];
-			}
-			return $values;
-		}
-		return false;
-	}
-
-	/**
-	 * @brief executes an LDAP search, optimized for Users
-	 * @param $filter the LDAP filter for the search
-	 * @param $attr optional, when a certain attribute shall be filtered out
-	 * @returns array with the search result
-	 *
-	 * Executes an LDAP search
-	 */
-	static public function searchUsers($filter, $attr = null) {
-		self::init();
-		return self::search($filter, self::$ldapBaseUsers, $attr);
-	}
-
-	/**
-	 * @brief executes an LDAP search, optimized for Groups
-	 * @param $filter the LDAP filter for the search
-	 * @param $attr optional, when a certain attribute shall be filtered out
-	 * @returns array with the search result
-	 *
-	 * Executes an LDAP search
-	 */
-	static public function searchGroups($filter, $attr = null) {
-		self::init();
-		return self::search($filter, self::$ldapBaseGroups, $attr);
-	}
-
-	/**
-	 * @brief executes an LDAP search
-	 * @param $filter the LDAP filter for the search
-	 * @param $base the LDAP subtree that shall be searched
-	 * @param $attr optional, when a certain attribute shall be filtered out
-	 * @returns array with the search result
-	 *
-	 * Executes an LDAP search
-	 */
-	static private function search($filter, $base, $attr = null) {
-		if(!is_null($attr) && !is_array($attr)) {
-			$attr = array(mb_strtolower($attr, 'UTF-8'));
-		}
-
-		// See if we have a resource
-		$link_resource = self::getConnectionResource();
-		if(is_resource($link_resource)) {
-			$sr = ldap_search($link_resource, $base, $filter, $attr);
-			$findings = ldap_get_entries($link_resource, $sr );
-
-			// if we're here, probably no connection resource is returned.
-			// to make ownCloud behave nicely, we simply give back an empty array.
-			if(is_null($findings)) {
-				return array();
-			}
-		} else {
-			// Seems like we didn't find any resource.
-			// Return an empty array just like before.
-			return array();
-		}
-
-		if(!is_null($attr)) {
-			$selection = array();
-			$multiarray = false;
-			if(count($attr) > 1) {
-				$multiarray = true;
-				$i = 0;
-			}
-			foreach($findings as $item) {
-				if(!is_array($item)) {
-					continue;
-				}
-				$item = OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
-
-				if($multiarray) {
-					foreach($attr as $key) {
-						$key = mb_strtolower($key, 'UTF-8');
-						if(isset($item[$key])) {
-							if($key != 'dn'){
-								$selection[$i][$key] = self::resemblesDN($key) ? self::sanitizeDN($item[$key][0]) : $item[$key][0];
-							} else {
-								$selection[$i][$key] = self::sanitizeDN($item[$key]);
-							}
-						}
-
-					}
-					$i++;
-				} else {
-					//tribute to case insensitivity
-					$key = mb_strtolower($attr[0], 'UTF-8');
-
-					if(isset($item[$key])) {
-						if(self::resemblesDN($key)) {
-							$selection[] = self::sanitizeDN($item[$key]);
-						} else {
-							$selection[] = $item[$key];
-						}
-					}
-				}
-
-			}
-			return $selection;
-		}
-
-		return $findings;
-	}
-
-	static private function resemblesDN($attr) {
-		$resemblingAttributes = array(
-			'dn',
-			'uniquemember',
-			'member'
-		);
-		return in_array($attr, $resemblingAttributes);
-	}
-
-	static private function sanitizeDN($dn) {
-		//OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this!
-		$dn = preg_replace('/([^\\\]),(\s+)/u','\1,',$dn);
-
-		//make comparisons and everything work
-		$dn = mb_strtolower($dn, 'UTF-8');
-
-		return $dn;
-	}
-
-	static private function sanitizeUsername($name) {
-		if(self::$ldapIgnoreNamingRules) {
-			return $name;
-		}
-
-		//REPLACEMENTS
-		$name = OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8');
-
-		//every remaining unallowed characters will be removed
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
-
-		return $name;
-	}
-
-	/**
-	 * @brief combines the input filters with AND
-	 * @param $filters array, the filters to connect
-	 * @returns the combined filter
-	 *
-	 * Combines Filter arguments with AND
-	 */
-	static public function combineFilterWithAnd($filters) {
-		return self::combineFilter($filters,'&');
-	}
-
-	/**
-	 * @brief combines the input filters with AND
-	 * @param $filters array, the filters to connect
-	 * @returns the combined filter
-	 *
-	 * Combines Filter arguments with AND
-	 */
-	static public function combineFilterWithOr($filters) {
-		return self::combineFilter($filters,'|');
-	}
-
-	/**
-	 * @brief combines the input filters with given operator
-	 * @param $filters array, the filters to connect
-	 * @param $operator either & or |
-	 * @returns the combined filter
-	 *
-	 * Combines Filter arguments with AND
-	 */
-	static private function combineFilter($filters, $operator) {
-		$combinedFilter = '('.$operator;
-		foreach($filters as $filter) {
-		    if($filter[0] != '(') {
-				$filter = '('.$filter.')';
-		    }
-		    $combinedFilter.=$filter;
-		}
-		$combinedFilter.=')';
-		return $combinedFilter;
-	}
-
-	/**
-	 * Returns the LDAP handler
-	 */
-	static private function getConnectionResource() {
-		if(!self::$ldapConnectionRes) {
-			self::init();
-		}
-		if(is_null(self::$ldapConnectionRes)) {
-			OCP\Util::writeLog('ldap', 'Connection could not be established', OCP\Util::INFO);
-		}
-		return self::$ldapConnectionRes;
-	}
-
-	/**
-	 * Caches the general LDAP configuration.
-	 */
-	static private function readConfiguration($force = false) {
-		if(!self::$configured || $force) {
-			self::$ldapHost              = OCP\Config::getAppValue('user_ldap', 'ldap_host', '');
-			self::$ldapPort              = OCP\Config::getAppValue('user_ldap', 'ldap_port', 389);
-			self::$ldapAgentName         = OCP\Config::getAppValue('user_ldap', 'ldap_dn','');
-			self::$ldapAgentPassword     = base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password',''));
-			self::$ldapBase              = OCP\Config::getAppValue('user_ldap', 'ldap_base', '');
-			self::$ldapBaseUsers         = OCP\Config::getAppValue('user_ldap', 'ldap_base_users',self::$ldapBase);
-			self::$ldapBaseGroups        = OCP\Config::getAppValue('user_ldap', 'ldap_base_groups', self::$ldapBase);
-			self::$ldapTLS               = OCP\Config::getAppValue('user_ldap', 'ldap_tls',0);
-			self::$ldapNoCase            = OCP\Config::getAppValue('user_ldap', 'ldap_nocase', 0);
-			self::$ldapUserDisplayName   = mb_strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid'), 'UTF-8');
-			self::$ldapUserFilter        = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter','objectClass=person');
-			self::$ldapLoginFilter       = OCP\Config::getAppValue('user_ldap', 'ldap_login_filter', '(uid=%uid)');
-			self::$ldapGroupDisplayName  = mb_strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', LDAP_GROUP_DISPLAY_NAME_ATTR), 'UTF-8');
-			self::$ldapIgnoreNamingRules = OCP\Config::getSystemValue('ldapIgnoreNamingRules', false);
-
-			if(empty(self::$ldapBaseUsers)) {
-				OCP\Util::writeLog('ldap', 'Base for Users is empty, using Base DN', OCP\Util::INFO);
-				self::$ldapBaseUsers = self::$ldapBase;
-			}
-			if(empty(self::$ldapBaseGroups)) {
-				OCP\Util::writeLog('ldap', 'Base for Groups is empty, using Base DN', OCP\Util::INFO);
-				self::$ldapBaseGroups = self::$ldapBase;
-			}
-
-			if(
-				   !empty(self::$ldapHost)
-				&& !empty(self::$ldapPort)
-				&& (
-					   (!empty(self::$ldapAgentName) && !empty(self::$ldapAgentPassword))
-					|| ( empty(self::$ldapAgentName) &&  empty(self::$ldapAgentPassword))
-				)
-				&& !empty(self::$ldapBase)
-				&& !empty(self::$ldapUserDisplayName)
-			)
-			{
-				self::$configured = true;
-			}
-		}
-	}
-
-	/**
-	 * Connects and Binds to LDAP
-	 */
-	static private function establishConnection() {
-		if(!self::$configured) {
-			OCP\Util::writeLog('ldap', 'Configuration is invalid, cannot connect', OCP\Util::INFO);
-			return false;
-		}
-		if(!self::$ldapConnectionRes) {
-			self::$ldapConnectionRes = ldap_connect(self::$ldapHost, self::$ldapPort);
-			if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
-					if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
-						if(self::$ldapTLS) {
-							ldap_start_tls(self::$ldapConnectionRes);
-						}
-					}
-			}
-
-			$ldapLogin = @ldap_bind(self::$ldapConnectionRes, self::$ldapAgentName, self::$ldapAgentPassword );
-			if(!$ldapLogin) {
-				OCP\Util::writeLog('ldap', 'Bind failed: ' . ldap_errno(self::$ldapConnectionRes) . ': ' . ldap_error(self::$ldapConnectionRes), OCP\Util::ERROR);
-				self::$ldapConnectionRes = null;
-				return false;
-			}
-		}
-	}
-
-	static public function areCredentialsValid($name, $password) {
-		return @ldap_bind(self::getConnectionResource(), $name, $password);
-	}
-
-	/**
-	* taken from http://www.php.net/manual/en/function.array-search.php#97645
-	* TODO: move somewhere, where its better placed since it is not LDAP specific. OC_Helper maybe?
-	*/
-	static public function recursiveArraySearch($haystack, $needle, $index = null) {
-		$aIt = new RecursiveArrayIterator($haystack);
-		$it = new RecursiveIteratorIterator($aIt);
-
-		while($it->valid()) {
-			if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
-				return $aIt->key();
-			}
-
-			$it->next();
-		}
-
-		return false;
-	}
-
- }
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php
index 2be6b46fb23c05eff40b242240d09b9187d2fbce..106459580fa0e8e900f4c88c694724d441946576 100644
--- a/apps/user_ldap/tests/group_ldap.php
+++ b/apps/user_ldap/tests/group_ldap.php
@@ -26,8 +26,8 @@ class Test_Group_Ldap extends UnitTestCase {
 	}
 
 	function testSingleBackend(){
-		OC_Group::useBackend(new OC_GROUP_LDAP());
-		$group_ldap = new OC_GROUP_LDAP();
+		OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP());
+		$group_ldap = new OCA\user_ldap\GROUP_LDAP();
 
  		$this->assertIsA(OC_Group::getGroups(),gettype(array()));
 		$this->assertIsA($group_ldap->getGroups(),gettype(array()));
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index b51d9a55cc79466ee892fddf69d92eecf0aecc7e..a4a8921d08d77451f176d003b9d3c726a54f488c 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -23,13 +23,9 @@
  *
  */
 
-class OC_USER_LDAP extends OC_User_Backend {
+namespace OCA\user_ldap;
 
-	// cached settings
-	protected $ldapUserFilter;
-	protected $ldapQuotaAttribute;
-	protected $ldapQuotaDefault;
-	protected $ldapEmailAttribute;
+class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 
 	// will be retrieved from LDAP server
 	protected $ldap_dc = false;
@@ -37,39 +33,32 @@ class OC_USER_LDAP extends OC_User_Backend {
 	// cache getUsers()
 	protected $_users = null;
 
-	public function __construct() {
-		$this->ldapUserFilter      = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter', '(objectClass=posixAccount)');
-		$this->ldapQuotaAttribute  = OCP\Config::getAppValue('user_ldap', 'ldap_quota_attr', '');
-		$this->ldapQuotaDefault    = OCP\Config::getAppValue('user_ldap', 'ldap_quota_def', '');
-		$this->ldapEmailAttribute  = OCP\Config::getAppValue('user_ldap', 'ldap_email_attr', '');
-	}
-
 	private function updateQuota($dn) {
 		$quota = null;
-		if(!empty($this->ldapQuotaDefault)) {
-			$quota = $this->ldapQuotaDefault;
+		if(!empty($this->connection->ldapQuotaDefault)) {
+			$quota = $this->connection->ldapQuotaDefault;
 		}
-		if(!empty($this->ldapQuotaAttribute)) {
-			$aQuota = OC_LDAP::readAttribute($dn, $this->ldapQuotaAttribute);
+		if(!empty($this->connection->ldapQuotaAttribute)) {
+			$aQuota = $this->readAttribute($dn, $this->connection->ldapQuotaAttribute);
 
 			if($aQuota && (count($aQuota) > 0)) {
 				$quota = $aQuota[0];
 			}
 		}
 		if(!is_null($quota)) {
-			OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'files', 'quota', OCP\Util::computerFileSize($quota));
+			\OCP\Config::setUserValue($this->dn2username($dn), 'files', 'quota', \OCP\Util::computerFileSize($quota));
 		}
 	}
 
 	private function updateEmail($dn) {
 		$email = null;
-		if(!empty($this->ldapEmailAttribute)) {
-			$aEmail = OC_LDAP::readAttribute($dn, $this->ldapEmailAttribute);
+		if(!empty($this->connection->ldapEmailAttribute)) {
+			$aEmail = $this->readAttribute($dn, $this->connection->ldapEmailAttribute);
 			if($aEmail && (count($aEmail) > 0)) {
 				$email = $aEmail[0];
 			}
-			if(!is_null($email)){
-				OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'settings', 'email', $email);
+			if(!is_null($email)) {
+				\OCP\Config::setUserValue($this->dn2username($dn), 'settings', 'email', $email);
 			}
 		}
 	}
@@ -84,15 +73,15 @@ class OC_USER_LDAP extends OC_User_Backend {
 	 */
 	public function checkPassword($uid, $password){
 		//find out dn of the user name
-		$filter = OCP\Util::mb_str_replace('%uid', $uid, OC_LDAP::conf('ldapLoginFilter'), 'UTF-8');
-		$ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn');
+		$filter = \OCP\Util::mb_str_replace('%uid', $uid, $this->connection->ldapLoginFilter, 'UTF-8');
+		$ldap_users = $this->fetchListOfUsers($filter, 'dn');
 		if(count($ldap_users) < 1) {
 			return false;
 		}
 		$dn = $ldap_users[0];
 
 		//are the credentials OK?
-		if(!OC_LDAP::areCredentialsValid($dn, $password)) {
+		if(!$this->areCredentialsValid($dn, $password)) {
 			return false;
 		}
 
@@ -101,7 +90,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 		$this->updateEmail($dn);
 
 		//give back the display name
-		return OC_LDAP::dn2username($dn);
+		return $this->dn2username($dn);
 	}
 
 	/**
@@ -112,8 +101,8 @@ class OC_USER_LDAP extends OC_User_Backend {
 	 */
 	public function getUsers(){
 		if(is_null($this->_users)) {
-			$ldap_users = OC_LDAP::fetchListOfUsers($this->ldapUserFilter, array(OC_LDAP::conf('ldapUserDisplayName'), 'dn'));
-			$this->_users = OC_LDAP::ownCloudUserNames($ldap_users);
+			$ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn'));
+			$this->_users = $this->ownCloudUserNames($ldap_users);
 		}
 		return $this->_users;
 	}
@@ -125,13 +114,13 @@ class OC_USER_LDAP extends OC_User_Backend {
 	 */
 	public function userExists($uid){
 		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
-		$dn = OC_LDAP::username2dn($uid);
+		$dn = $this->username2dn($uid);
 		if(!$dn) {
 			return false;
 		}
 
 		//if user really still exists, we will be able to read his cn
-		$cn = OC_LDAP::readAttribute($dn, 'cn');
+		$cn = $this->readAttribute($dn, 'cn');
 		if(!$cn || empty($cn)) {
 			return false;
 		}
@@ -139,4 +128,27 @@ class OC_USER_LDAP extends OC_User_Backend {
 		return true;
 	}
 
+	/**
+	* @brief delete a user
+	* @param $uid The username of the user to delete
+	* @returns true/false
+	*
+	* Deletes a user
+	*/
+	public function deleteUser($uid) {
+		return false;
+	}
+
+	/**
+	* @brief Check if backend implements actions
+	* @param $actions bitwise-or'ed actions
+	* @returns boolean
+	*
+	* Returns the supported actions as int to be
+	* compared with OC_USER_BACKEND_CREATE_USER etc.
+	*/
+	public function implementsActions($actions) {
+		return (bool)(OC_USER_BACKEND_CHECK_PASSWORD & $actions);
+	}
+
 }
\ No newline at end of file
diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php
index 9d314b59ce764cf6009c32476db015bf7537b57c..366c4004932d4b516230aefafd406e2609fb566c 100644
--- a/apps/user_migrate/appinfo/app.php
+++ b/apps/user_migrate/appinfo/app.php
@@ -31,4 +31,3 @@ $entry = array(
 	'href' => OCP\Util::linkTo( "user_migrate", "admin.php" ),
 	'name' => 'Import'
 );
-?>
diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php
index 1718abe9e0f9d903d97b555a631435df345d37ee..bce5fb2d7cace289b83f9e0336e7aecb3638913b 100644
--- a/apps/user_migrate/templates/settings.php
+++ b/apps/user_migrate/templates/settings.php
@@ -12,7 +12,7 @@
 		<?php } ?>
         <legend><strong><?php echo $l->t('Import user account');?></strong></legend>
         </p>
-        <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:180px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label>
+        <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:280px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label>
         </p>
         <input type="submit" name="user_import" value="<?php echo $l->t('Import'); ?>" />
     </fieldset>
diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php
index 392424795f837af7281aabbb83fabbbd7ef1f49c..88571ba618eba756bcde28ae8810ece3275e5b4f 100644
--- a/apps/user_openid/user.php
+++ b/apps/user_openid/user.php
@@ -44,7 +44,4 @@ if(!OCP\User::userExists($USERNAME)){
 }
 $IDENTITY=OCP\Util::linkToAbsolute( "user_openid", "user.php" ).'/'.$USERNAME;
 
-require_once 'phpmyid.php';
-
-
-?>
+require_once 'openid/phpmyid.php';
diff --git a/apps/user_openid/user_openid.php b/apps/user_openid/user_openid.php
index 70b193a30b13cf8b61b174f3501ad021d46fab65..19f2f719b06d6d22db7a309a3749b0cebf418de9 100644
--- a/apps/user_openid/user_openid.php
+++ b/apps/user_openid/user_openid.php
@@ -21,7 +21,7 @@
  *
  */
 
-require_once('class.openid.v3.php');
+require_once('openid/class.openid.v3.php');
 
 /**
  * Class for user OpenId backend
diff --git a/apps/user_webfinger/.htaccess b/apps/user_webfinger/.htaccess
new file mode 100644
index 0000000000000000000000000000000000000000..1b13cf788ffb3c5b25c02353819c6be516ee48e0
--- /dev/null
+++ b/apps/user_webfinger/.htaccess
@@ -0,0 +1,5 @@
+<IfModule mod_rewrite.c>
+RewriteEngine on
+RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+RewriteRule ^host-meta host-meta.php [QSA,L]
+</IfModule>
diff --git a/apps/user_webfinger/appinfo/app.php b/apps/user_webfinger/appinfo/app.php
index 3336af66820e6460ca7a8702a1762db9b0e44bda..b3d9bbc7f3711e882119cd6b3af051245d859d04 100644
--- a/apps/user_webfinger/appinfo/app.php
+++ b/apps/user_webfinger/appinfo/app.php
@@ -1,3 +1 @@
 <?php
-OCP\CONFIG::setAppValue('core', 'public_host-meta', '/apps/user_webfinger/host-meta.php');
-OCP\CONFIG::setAppValue('core', 'public_webfinger', '/apps/user_webfinger/webfinger.php');
diff --git a/apps/user_webfinger/appinfo/info.xml b/apps/user_webfinger/appinfo/info.xml
index 4e28814a26cc10fe087f48fe61ef8af82af725fe..f74e5d3f93a23e75e4ee5f9ccced7422daff2ebf 100644
--- a/apps/user_webfinger/appinfo/info.xml
+++ b/apps/user_webfinger/appinfo/info.xml
@@ -7,4 +7,8 @@
 	<author>Michiel de Jong, Florian Hülsmann</author>
 	<require>4</require>
 	<shipped>true</shipped>
+	<public>
+		<host-meta>host-meta.php</host-meta>
+		<webfinger>webfinger.php</webfinger>
+	</public>
 </info>
diff --git a/apps/user_webfinger/appinfo/version b/apps/user_webfinger/appinfo/version
index 1d71ef97443918d538e8188167c94d7bbafaf753..a2268e2de4458c0966915535aa599e828b6d22ae 100644
--- a/apps/user_webfinger/appinfo/version
+++ b/apps/user_webfinger/appinfo/version
@@ -1 +1 @@
-0.3
\ No newline at end of file
+0.3.1
\ No newline at end of file
diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php
index 32ffb512057f83237cd00463f6faa62807d01251..4ac37b1ea0922d0a319b7321afd9bb0d0a6b6112 100644
--- a/apps/user_webfinger/host-meta.php
+++ b/apps/user_webfinger/host-meta.php
@@ -1,10 +1,27 @@
 <?php
+
+if(class_exists('OC')){
+	$WEBROOT=OC::$WEBROOT;
+}else{//not called trough remote.php try to guess the webroot the best we can from here
+	// calculate the root directories
+	$SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-strlen('apps/user_webfinger/host-meta.php')));
+	$WEBROOT=substr($SERVERROOT,strlen(realpath($_SERVER['DOCUMENT_ROOT'])));
+
+	if($WEBROOT!='' and $WEBROOT[0]!=='/'){
+		$WEBROOT='/'.$WEBROOT;
+	}
+}
+
+if(substr($WEBROOT,-1)==='/'){
+	$WEBROOT=substr($WEBROOT,0,-1);
+}
+
 $hostMetaHeader = array(
 	'Access-Control-Allow-Origin' => '*',
 	'Content-Type' => 'application/xrd+json'
 );
 $serverName = $_SERVER['SERVER_NAME'];
-$hostMetaContents = '{"links":[{"rel":"lrdd","template":"http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$serverName.'/public.php?service=webfinger&q={uri}"}]}';
+$hostMetaContents = '{"links":[{"rel":"lrdd","template":"http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$serverName.$WEBROOT.'/public.php?service=webfinger&q={uri}"}]}';
 foreach($hostMetaHeader as $header => $value) {
 	header($header . ": " . $value);
 }
diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php
index 6b64a7e2860e9277ed4a7c172f4833f3e97fb9bb..e5b7f042d5abb0fa1c87bd6f77a49caed1c852bc 100644
--- a/apps/user_webfinger/webfinger.php
+++ b/apps/user_webfinger/webfinger.php
@@ -58,4 +58,3 @@ foreach($apps as $app) {
 	}
 }
 echo "]}";
-?>
diff --git a/autotest.sh b/autotest.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a42c6ab059e4dd479de42c72f742a6903030670a
--- /dev/null
+++ b/autotest.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+#
+# ownCloud
+#
+# @author Thomas Müller
+# @copyright 2012 Thomas Müller thomas.mueller@tmit.eu
+#
+
+DATADIR=data-autotest
+BASEDIR=$PWD
+
+# create autoconfig for sqlite, mysql and (soon) postgresql
+cat > ./tests/autoconfig-sqlite.php <<DELIM
+<?php
+\$AUTOCONFIG = array (
+  'installed' => false,
+  'dbtype' => 'sqlite',
+  'dbtableprefix' => 'oc_',
+  'adminlogin' => 'admin',
+  'adminpass' => 'admin',
+  'directory' => '$BASEDIR/$DATADIR',
+);
+DELIM
+
+cat > ./tests/autoconfig-mysql.php <<DELIM
+<?php
+\$AUTOCONFIG = array (
+  'installed' => false,
+  'dbtype' => 'mysql',
+  'dbtableprefix' => 'oc_',
+  'adminlogin' => 'admin',
+  'adminpass' => 'admin',
+  'directory' => '$BASEDIR/$DATADIR',
+  'dbuser' => 'oc_autotest',	
+  'dbname' => 'oc_autotest',	
+  'dbhost' => 'localhost',
+  'dbpass' => 'owncloud',	
+);
+DELIM
+
+cat > ./tests/autoconfig-pgsql.php <<DELIM
+<?php
+\$AUTOCONFIG = array (
+  'installed' => false,
+  'dbtype' => 'pgsql',
+  'dbtableprefix' => 'oc_',
+  'adminlogin' => 'admin',
+  'adminpass' => 'admin',
+  'directory' => '$BASEDIR/$DATADIR',
+  'dbuser' => 'oc_autotest',	
+  'dbname' => 'oc_autotest',	
+  'dbhost' => 'localhost',
+  'dbpass' => 'owncloud',	
+);
+DELIM
+
+function execute_tests {
+	echo "Setup environment for $1 testing ..."
+	# back to root folder
+	cd $BASEDIR
+
+	# revert changes to tests/data
+	git checkout tests/data/*
+
+	# reset data directory
+	rm -rf $DATADIR
+	mkdir $DATADIR
+
+	# remove the old config file
+	rm -rf config/config.php
+
+	# drop database
+	if [ "$1" == "mysql" ] ; then
+		mysql -u oc_autotest -powncloud -e "DROP DATABASE oc_autotest"
+	fi
+	if [ "$1" == "pgsql" ] ; then
+		dropdb -U oc_autotest oc_autotest
+	fi
+
+	# copy autoconfig
+	cp $BASEDIR/tests/autoconfig-$1.php $BASEDIR/config/autoconfig.php
+
+	# trigger installation
+	php -f index.php
+
+	#test execution
+	echo "Testing with $1 ..."
+	cd tests
+	php -f index.php -- xml $1 > autotest-results-$1.xml
+}
+
+#
+# start test execution
+#
+execute_tests "sqlite"
+execute_tests 'mysql'
+execute_tests 'pgsql'
+
+#
+# NOTES on mysql:
+#  - CREATE USER 'oc_autotest'@'localhost' IDENTIFIED BY 'owncloud';
+#  - grant access permissions: grant all on oc_autotest.* to 'oc_autotest'@'localhost';
+#
+# NOTES on pgsql:
+#  - su - postgres
+#  - createuser -P (enter username and password and enable superuser)
+#  - to enable dropdb I decided to add following line to pg_hba.conf (this is not the safest way but I don't care for the testing machine):
+# local	all	all	trust
+#
+
diff --git a/core/ajax/grouplist.php b/core/ajax/grouplist.php
index cc15102bbc343ece229cfe21bde953fc9638a325..e3e92fcfa16956b00c4d76608460ab3ed4ae19ce 100644
--- a/core/ajax/grouplist.php
+++ b/core/ajax/grouplist.php
@@ -45,5 +45,3 @@ foreach( OC_Group::getGroups() as $i ){
 }
 
 OC_JSON::encodedPrint($groups);
-
-?>
diff --git a/core/ajax/translations.php b/core/ajax/translations.php
index a6433b1964afd4a0d6c9090c7007e374a5a583af..2bd6b7ed634d9b8032ed223a26393190cc788b5f 100644
--- a/core/ajax/translations.php
+++ b/core/ajax/translations.php
@@ -29,4 +29,3 @@ $app = $_POST["app"];
 $l = OC_L10N::get( $app );
 
 OC_JSON::success(array('data' => $l->getTranslations()));
-?>
diff --git a/core/ajax/userlist.php b/core/ajax/userlist.php
index c8168eaf460869eca114f43a218eb2720157b020..85ca004ae664597a44a6e15dd0587753f0f99f50 100644
--- a/core/ajax/userlist.php
+++ b/core/ajax/userlist.php
@@ -44,5 +44,3 @@ foreach( OC_User::getUsers() as $i ){
 }
 
 OC_JSON::encodedPrint($users);
-
-?>
diff --git a/core/ajax/validateuser.php b/core/ajax/validateuser.php
index 258bd50fcad01bd14838e507ecee2856b5418a28..78ec451fac269a2c7863abe8f91907838206fb10 100644
--- a/core/ajax/validateuser.php
+++ b/core/ajax/validateuser.php
@@ -36,5 +36,3 @@ if(!isset($_SERVER['PHP_AUTH_USER'])){
 	        OC_JSON::encodedPrint(array("username" => $_SERVER["PHP_AUTH_USER"], "user_valid" => "false"));
 	}
 }
-
-?>
diff --git a/core/ajax/vcategories/add.php b/core/ajax/vcategories/add.php
index a58489228d81ede597e59b365980452278821ea0..e69f8bb726b14b3941c3793b1b559b4d1a3146bc 100644
--- a/core/ajax/vcategories/add.php
+++ b/core/ajax/vcategories/add.php
@@ -39,5 +39,3 @@ if($categories->hasCategory($category)) {
 }
 
 OC_JSON::success(array('data' => array('categories'=>$categories->categories())));
-
-?>
diff --git a/core/ajax/vcategories/delete.php b/core/ajax/vcategories/delete.php
index 75def433d30289f15fd03fa8f7b616d13cf71371..a41fa083c3889c914d6b4b23a0395ec3d94c5741 100644
--- a/core/ajax/vcategories/delete.php
+++ b/core/ajax/vcategories/delete.php
@@ -34,5 +34,3 @@ if(is_null($categories)) {
 $vcategories = new OC_VCategories($app);
 $vcategories->delete($categories);
 OC_JSON::success(array('data' => array('categories'=>$vcategories->categories())));
-
-?>
diff --git a/core/ajax/vcategories/edit.php b/core/ajax/vcategories/edit.php
index 252b3d3454c9d143baaa40b76a31b0044e63fe9f..3e5540cbc227dc521172821a1fb49f79ff39dbe7 100644
--- a/core/ajax/vcategories/edit.php
+++ b/core/ajax/vcategories/edit.php
@@ -31,5 +31,3 @@ $categories = $vcategories->categories();
 debug(print_r($categories, true));
 $tmpl->assign('categories',$categories);
 $tmpl->printpage();
-
-?>
diff --git a/core/css/styles.css b/core/css/styles.css
index 338518e5b2f13d99c8484aaedeba52be7dde7b31..b818caa279f1513cc9ca82ff1cd8fe628ea18f11 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -139,6 +139,8 @@ li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ff
 
 a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px }
 
+.exception{color: #000000;}
+.exception textarea{width:95%;height: 200px;background:#ffe;border:0;}
 
 /* ---- DIALOGS ---- */
 #dirtree {width: 100%;}
diff --git a/core/js/eventsource.js b/core/js/eventsource.js
index 08259e02cae46adef9b943744b091e11d1e38917..e3ad7e3a671fc54e2d67c41ab80d99631c2e77db 100644
--- a/core/js/eventsource.js
+++ b/core/js/eventsource.js
@@ -40,6 +40,7 @@ OC.EventSource=function(src,data){
 			dataStr+=name+'='+encodeURIComponent(data[name])+'&';
 		}
 	}
+	dataStr+='requesttoken='+OC.EventSource.requesttoken;
 	if(!this.useFallBack && typeof EventSource !='undefined'){
 		this.source=new EventSource(src+'?'+dataStr);
 		this.source.onmessage=function(e){
diff --git a/core/strings.php b/core/strings.php
index 8c3f64ef14f63d045a48f9a26ad68ee076663787..01ab386608967276f2104bd60070f8d11dcaebdc 100644
--- a/core/strings.php
+++ b/core/strings.php
@@ -7,4 +7,3 @@ $l->t("Users");
 $l->t("Apps");
 $l->t("Admin");
 $l->t("Help");
-?>
diff --git a/core/templates/exception.php b/core/templates/exception.php
new file mode 100644
index 0000000000000000000000000000000000000000..7f58ce252cf72f6b2eeb8e2caf91e182b148abfd
--- /dev/null
+++ b/core/templates/exception.php
@@ -0,0 +1,30 @@
+<ul>
+	<li class='error'>
+		<details>
+		<summary class="error">We're sorry, but something went terribly wrong.<br></summary>
+		<p class="exception">
+		<?php
+		if($_['showsysinfo'] == true){
+			echo 'If you would like to support ownCloud\'s developers and report this error in our <a href="http://bugs.owncloud.org">Bugtracker</a>, please copy the following informations into the description. <br><br><textarea readonly>';
+			echo 'Message: '  . $_['message'] . "\n";
+			echo 'Error Code: '  . $_['code'] . "\n";
+			echo 'File: '  . $_['file'] . "\n";
+			echo 'Line: '  . $_['line'] . "\n\n";
+			echo 'PHP: '  . $_['sysinfo']['phpversion'] . "\n";
+			echo 'OS: '  . $_['sysinfo']['os'] . "\n";
+			echo 'OS Release: '  . $_['sysinfo']['osrelease'] . "\n";
+			echo 'OS Arch.: '  . $_['sysinfo']['osarchitecture'] . "\n";
+			echo 'PHP-Server-Interface: '  . $_['sysinfo']['phpserverinterface'] . "\n";
+			echo 'Protocol: '  . $_['sysinfo']['serverprotocol'] . "\n";
+			echo 'HTTPS: '  . $_['sysinfo']['https'] . "\n";
+			echo 'Request Method: '  . $_['sysinfo']['requestmethod'] . "\n";
+			echo 'Database: '  . $_['sysinfo']['database'] . "\n";
+			echo '</textarea>';
+		}else{
+			echo 'Your administrator has disabled systeminformations.';
+		}
+		?>
+		</p>
+		</details>
+	</li>
+</ul>
\ No newline at end of file
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 7e98fdedc2d90087f9a337fb2cc30fedbc4730e3..dc303ffc1a751c5081dcca76e53f4d86690158ae 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -33,6 +33,7 @@
 		<script type="text/javascript">
 			$(function() {
 				requesttoken = '<?php echo $_['requesttoken']; ?>';
+				OC.EventSource.requesttoken=requesttoken;
 				$(document).bind('ajaxSend', function(elm, xhr, s){
 					if(requesttoken) {
 						xhr.setRequestHeader('requesttoken', requesttoken);
diff --git a/core/templates/login.php b/core/templates/login.php
index 985cf90c2a243b414ed9ce180bd9d03909ca2156..b35c4a33be86d14c8e24b95f7c407dce6d93c081 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -7,7 +7,7 @@
 		<?php endif; ?>
 		<p class="infield">
 			<label for="user" class="infield"><?php echo $l->t( 'Username' ); ?></label>
-			<input type="text" name="user" id="user" value="<?php echo !empty($_POST['user'])?OC_Util::sanitizeHTML($_POST['user'],ENT_COMPAT,'utf-8').'"':'" autofocus'; ?> autocomplete="off" required />
+			<input type="text" name="user" id="user" value="<?php echo !empty($_POST['user'])?OC_Util::sanitizeHTML($_POST['user'],ENT_COMPAT,'utf-8').'"':'" autofocus'; ?> autocomplete="on" required />
 		</p>
 		<p class="infield">
 			<label for="password" class="infield"><?php echo $l->t( 'Password' ); ?></label>
diff --git a/lib/MDB2/Driver/Function/sqlite3.php b/lib/MDB2/Driver/Function/sqlite3.php
index a013aea165ad50dbb92173c029ceb974678ffd39..1af262fd7a7a9ca165202b1bba66bf0dc70d212b 100644
--- a/lib/MDB2/Driver/Function/sqlite3.php
+++ b/lib/MDB2/Driver/Function/sqlite3.php
@@ -134,4 +134,3 @@ class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common
 
     // }}}
 }
-?>
diff --git a/lib/MDB2/Driver/Manager/sqlite3.php b/lib/MDB2/Driver/Manager/sqlite3.php
index 8f4e1312eb8f09faf456d22be3998a5ff490bd28..10255a3619a08543c3d73fdecdbdf4990e9425cb 100644
--- a/lib/MDB2/Driver/Manager/sqlite3.php
+++ b/lib/MDB2/Driver/Manager/sqlite3.php
@@ -1360,4 +1360,3 @@ class MDB2_Driver_Manager_sqlite3 extends MDB2_Driver_Manager_Common
 
     // }}}
 }
-?>
diff --git a/lib/MDB2/Driver/sqlite3.php b/lib/MDB2/Driver/sqlite3.php
index 39d3fb6727d836e96e93b01eaf6dce4773d9c77a..6bfccadad9ad6d602b6c061361c98e4f1a9d7c45 100644
--- a/lib/MDB2/Driver/sqlite3.php
+++ b/lib/MDB2/Driver/sqlite3.php
@@ -1221,7 +1221,7 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common
             return $affected_rows;
         }
 
-        $result =& $this->db->_wrapResult($result, $this->result_types,
+        $result = $this->db->_wrapResult($result, $this->result_types,
             $result_class, $result_wrap_class, $this->limit, $this->offset);
         $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
         return $result;
@@ -1332,5 +1332,3 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common
 		$this->free();
     }
 }
-
-?>
diff --git a/lib/app.php b/lib/app.php
index 4c2c43ec26be72c97b1061d7b444e402e6396d0d..56132c0867172adf10942ba0f24fd0a485f4c161 100755
--- a/lib/app.php
+++ b/lib/app.php
@@ -27,7 +27,6 @@
  * upgrading and removing apps.
  */
 class OC_App{
-	static private $init = false;
 	static private $activeapp = '';
 	static private $navigation = array();
 	static private $settingsForms = array();
@@ -196,7 +195,7 @@ class OC_App{
 			// check if the app is compatible with this version of ownCloud
 			$info=OC_App::getAppInfo($app);
 			$version=OC_Util::getVersion();
-	                if(!isset($info['require']) or ($version[0]>$info['require'])){
+			if(!isset($info['require']) or ($version[0]>$info['require'])){
 				OC_Log::write('core','App "'.$info['name'].'" can\'t be installed because it is not compatible with this version of ownCloud',OC_Log::ERROR);
 				return false;
 			}else{
@@ -332,8 +331,8 @@ class OC_App{
 	}
 
 	/**
-	* Get the path where to install apps
-  */
+	 * Get the path where to install apps
+	 */
 	public static function getInstallPath() {
 		if(OC_Config::getValue('appstoreenabled', true)==false) {
 			return false;
@@ -608,7 +607,7 @@ class OC_App{
 		//set remote/public handelers
 		$appData=self::getAppInfo($appid);
 		foreach($appData['remote'] as $name=>$path){
-			OCP\CONFIG::setAppValue('core', 'remote_'.$name, $path);
+			OCP\CONFIG::setAppValue('core', 'remote_'.$name, $appid.'/'.$path);
 		}
 		foreach($appData['public'] as $name=>$path){
 			OCP\CONFIG::setAppValue('core', 'public_'.$name, $appid.'/'.$path);
diff --git a/lib/archive/zip.php b/lib/archive/zip.php
index 6631a649b169380bebfea156f94a539135d7924d..b2d6674d63977142a6f5dedf6372cf77c9e87ed2 100644
--- a/lib/archive/zip.php
+++ b/lib/archive/zip.php
@@ -11,7 +11,6 @@ class OC_Archive_ZIP extends OC_Archive{
 	 * @var ZipArchive zip
 	 */
 	private $zip=null;
-	private $success=false;
 	private $path;
 	
 	function __construct($source){
@@ -74,8 +73,7 @@ class OC_Archive_ZIP extends OC_Archive{
 	 * @return int
 	 */
 	function mtime($path){
-		$stat=$this->zip->statName($path);
-		return $stat['mtime'];
+		return filemtime($this->path);
 	}
 	/**
 	 * get the files in a folder
diff --git a/lib/base.php b/lib/base.php
index fe69ad70c0f1bb2b3cefd7387cb8837137079f83..5041f43648e159e6439a36227ef441f0407dea23 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -83,9 +83,15 @@ class OC{
 		elseif(strpos($className,'OCP\\')===0){
 			require_once 'public/'.strtolower(str_replace('\\','/',substr($className,3)) . '.php');
 		}
+		elseif(strpos($className,'OCA\\')===0){
+			require_once 'apps/'.strtolower(str_replace('\\','/',substr($className,3)) . '.php');
+		}
 		elseif(strpos($className,'Sabre_')===0) {
 			require_once str_replace('_','/',$className) . '.php';
 		}
+		elseif(strpos($className,'Symfony\\')===0){
+			require_once str_replace('\\','/',$className) . '.php';
+		}
 		elseif(strpos($className,'Test_')===0){
 			require_once 'tests/lib/'.strtolower(str_replace('_','/',substr($className,5)) . '.php');
 		}
@@ -345,7 +351,7 @@ class OC{
 			$_SESSION['user_id'] = '';
 		}
 
-		OC_User::useBackend( OC_Config::getValue( "userbackend", "database" ));
+		OC_User::useBackend(new OC_User_Database());
 		OC_Group::useBackend(new OC_Group_Database());
 
 		// Load Apps
diff --git a/lib/cache.php b/lib/cache.php
index 1f269174fad663cf6bb2b6ad3611379d7afee971..55f189a5da8e1f5400c3a47cabc5ebf0900f31d3 100644
--- a/lib/cache.php
+++ b/lib/cache.php
@@ -7,48 +7,96 @@
  */
 
 class OC_Cache {
+	/**
+	 * @var OC_Cache $user_cache
+	 */
 	static protected $user_cache;
+	/**
+	 * @var OC_Cache $global_cache
+	 */
 	static protected $global_cache;
+	/**
+	 * @var OC_Cache $global_cache_fast
+	 */
+	static protected $global_cache_fast;
+	/**
+	 * @var OC_Cache $user_cache_fast
+	 */
+	static protected $user_cache_fast;
+	static protected $isFast=null;
 
-	static public function getGlobalCache() {
+	/**
+	 * get the global cache
+	 * @return OC_Cache
+	 */
+	static public function getGlobalCache($fast=false) {
 		if (!self::$global_cache) {
-			$fast_cache = null;
-			if (!$fast_cache && function_exists('xcache_set')) {
-				$fast_cache = new OC_Cache_XCache(true);
+			self::$global_cache_fast = null;
+			if (!self::$global_cache_fast && function_exists('xcache_set')) {
+				self::$global_cache_fast = new OC_Cache_XCache(true);
 			}
-			if (!$fast_cache && function_exists('apc_store')) {
-				$fast_cache = new OC_Cache_APC(true);
+			if (!self::$global_cache_fast && function_exists('apc_store')) {
+				self::$global_cache_fast = new OC_Cache_APC(true);
 			}
+			
 			self::$global_cache = new OC_Cache_FileGlobal();
-			if ($fast_cache) {
-				self::$global_cache = new OC_Cache_Broker($fast_cache, self::$global_cache);
+			if (self::$global_cache_fast) {
+				self::$global_cache = new OC_Cache_Broker(self::$global_cache_fast, self::$global_cache);
+			}
+		}
+		if($fast){
+			if(self::$global_cache_fast){
+				return self::$global_cache_fast;
+			}else{
+				return false;
 			}
 		}
 		return self::$global_cache;
 	}
 
-	static public function getUserCache() {
+	/**
+	 * get the user cache
+	 * @return OC_Cache
+	 */
+	static public function getUserCache($fast=false) {
 		if (!self::$user_cache) {
-			$fast_cache = null;
-			if (!$fast_cache && function_exists('xcache_set')) {
-				$fast_cache = new OC_Cache_XCache();
+			self::$user_cache_fast = null;
+			if (!self::$user_cache_fast && function_exists('xcache_set')) {
+				self::$user_cache_fast = new OC_Cache_XCache();
 			}
-			if (!$fast_cache && function_exists('apc_store')) {
-				$fast_cache = new OC_Cache_APC();
+			if (!self::$user_cache_fast && function_exists('apc_store')) {
+				self::$user_cache_fast = new OC_Cache_APC();
 			}
+			
 			self::$user_cache = new OC_Cache_File();
-			if ($fast_cache) {
-				self::$user_cache = new OC_Cache_Broker($fast_cache, self::$user_cache);
+			if (self::$user_cache_fast) {
+				self::$user_cache = new OC_Cache_Broker(self::$user_cache_fast, self::$user_cache);
+			}
+		}
+
+		if($fast){
+			if(self::$user_cache_fast){
+				return self::$user_cache_fast;
+			}else{
+				return false;
 			}
 		}
 		return self::$user_cache;
 	}
 
+	/**
+	 * get a value from the user cache
+	 * @return mixed
+	 */
 	static public function get($key) {
 		$user_cache = self::getUserCache();
 		return $user_cache->get($key);
 	}
 
+	/**
+	 * set a value in the user cache
+	 * @return bool
+	 */
 	static public function set($key, $value, $ttl=0) {
 		if (empty($key)) {
 			return false;
@@ -57,19 +105,43 @@ class OC_Cache {
 		return $user_cache->set($key, $value, $ttl);
 	}
 
+	/**
+	 * check if a value is set in the user cache
+	 * @return bool
+	 */
 	static public function hasKey($key) {
 		$user_cache = self::getUserCache();
 		return $user_cache->hasKey($key);
 	}
 
+	/**
+	 * remove an item from the user cache
+	 * @return bool
+	 */
 	static public function remove($key) {
 		$user_cache = self::getUserCache();
 		return $user_cache->remove($key);
 	}
 
-	static public function clear() {
+	/**
+	 * clear the user cache of all entries starting with a prefix
+	 * @param string prefix (optional)
+	 * @return bool
+	 */
+	static public function clear($prefix='') {
 		$user_cache = self::getUserCache();
-		return $user_cache->clear();
+		return $user_cache->clear($prefix);
+	}
+
+	/**
+	 * check if a fast memory based cache is available
+	 * @return true
+	 */
+	static public function isFast() {
+		if(is_null(self::$isFast)){
+			self::$isFast=function_exists('xcache_set') || function_exists('apc_store');
+		}
+		return self::$isFast;
 	}
 
 }
diff --git a/lib/cache/apc.php b/lib/cache/apc.php
index 6cf47d0c1586cfedaeb7937bcacc14f3e4f870e1..c192fe2f196817fbefeba9c6742215f221ac4fe3 100644
--- a/lib/cache/apc.php
+++ b/lib/cache/apc.php
@@ -43,14 +43,15 @@ class OC_Cache_APC {
 		return apc_delete($this->getNamespace().$key);
 	}
 
-	public function clear(){
-		$ns = $this->getNamespace();
+	public function clear($prefix=''){
+		$ns = $this->getNamespace().$prefix;
 		$cache = apc_cache_info('user');
 		foreach($cache['cache_list'] as $entry) {
 			if (strpos($entry['info'], $ns) === 0) {
 				apc_delete($entry['info']);
 			}
 		}
+		return true;
 	}
 }
 if(!function_exists('apc_exists')) {
diff --git a/lib/cache/broker.php b/lib/cache/broker.php
index 931d0dd407e64546d3ef41bf27ecb75b0f28594f..c2aceabaf53d8656e6864be14e8e1f537e59d8dd 100644
--- a/lib/cache/broker.php
+++ b/lib/cache/broker.php
@@ -46,8 +46,8 @@ class OC_Cache_Broker {
 		return $this->slow_cache->remove($key);
 	}
 
-	public function clear(){
-		$this->fast_cache->clear();
-		$this->slow_cache->clear();
+	public function clear($prefix=''){
+		$this->fast_cache->clear($prefix);
+		$this->slow_cache->clear($prefix);
 	}
 }
diff --git a/lib/cache/file.php b/lib/cache/file.php
index 0b7d3e30508d4c9934bf7bf4a72bebf82ee2c288..562c3d1716780f162f8be62492c6aaca8bf92acc 100644
--- a/lib/cache/file.php
+++ b/lib/cache/file.php
@@ -62,15 +62,16 @@ class OC_Cache_File{
 		return $storage->unlink($key);
 	}
 
-	public function clear(){
+	public function clear($prefix=''){
 		$storage = $this->getStorage();
 		if($storage and $storage->is_dir('/')){
 			$dh=$storage->opendir('/');
 			while($file=readdir($dh)){
-				if($file!='.' and $file!='..'){
+				if($file!='.' and $file!='..' and ($prefix==='' || strpos($file, $prefix) === 0)){
 					$storage->unlink('/'.$file);
 				}
 			}
 		}
+		return true;
 	}
 }
diff --git a/lib/cache/xcache.php b/lib/cache/xcache.php
index bd55cee8f6bbcb186d6a8781ffe528e36ebf6512..951f9b47545f6ef1a2790074d00ef4ee05d1f411 100644
--- a/lib/cache/xcache.php
+++ b/lib/cache/xcache.php
@@ -43,7 +43,8 @@ class OC_Cache_XCache {
 		return xcache_unset($this->getNamespace().$key);
 	}
 
-	public function clear(){
-		return xcache_unset_by_prefix($this->getNamespace());
+	public function clear($prefix=''){
+		xcache_unset_by_prefix($this->getNamespace().$prefix);
+		return true;
 	}
 }
diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php
index ee68039162647beab589a2711f0de4767bd40e4e..99f696e3a0711963b689a3d34aa3a132528c0248 100644
--- a/lib/connector/sabre/auth.php
+++ b/lib/connector/sabre/auth.php
@@ -31,13 +31,18 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic {
 	 * @return bool
 	 */
 	protected function validateUserPass($username, $password){
-		OC_Util::setUpFS();//login hooks may need early access to the filesystem
-		if(OC_User::login($username,$password)){
-			OC_Util::setUpFS($username);
+		if (OC_User::isLoggedIn()) {
+			OC_Util::setupFS($username);
 			return true;
-		}
-		else{
-			return false;
+		} else {
+			OC_Util::setUpFS();//login hooks may need early access to the filesystem
+			if(OC_User::login($username,$password)){
+				OC_Util::setUpFS($username);
+				return true;
+			}
+			else{
+				return false;
+			}
 		}
 	}
 } 
diff --git a/lib/connector/sabre/client.php b/lib/connector/sabre/client.php
index b799b541a055b5dd5af23f95bb8e7131ed29b6dd..7e8f21264f963733f45c62db812f58040934b373 100644
--- a/lib/connector/sabre/client.php
+++ b/lib/connector/sabre/client.php
@@ -23,29 +23,18 @@
 
 class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
 	
-	protected $curlSettings;
-	
-	public function __construct(array $settings) {
-		//set default curl settings
-		$this->curlSettings = array(
-				CURLOPT_RETURNTRANSFER => true,
-				// Return headers as part of the response
-				CURLOPT_HEADER => true,
-				// Automatically follow redirects
-				CURLOPT_FOLLOWLOCATION => true,
-				CURLOPT_MAXREDIRS => 5,
-				CURLOPT_SSL_VERIFYPEER => true,
-				//CURLOPT_SSL_VERIFYPEER	=> false,
-		);
-		parent::__construct($settings);
-	}
-	
-	public function setCurlSettings($settings) {
-		if (is_array($settings)) {
-			foreach ($settings as $k => $v) {
-				$this->curlSettings[$k] = $v;
-			}
-		}
+	protected $trustedCertificates;
+
+	/**
+	 * Add trusted root certificates to the webdav client.
+	 *
+	 * The parameter certificates should be a absulute path to a file which contains
+	 * all trusted certificates
+	 *
+	 * @param string $certificates
+	 */
+	public function addTrustedCertificates($certificates) {
+		$this->trustedCertificates = $certificates;
 	}
 	
 	/**
@@ -68,14 +57,24 @@ class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
 	 * @return array
 	 */
 	public function request($method, $url = '', $body = null, $headers = array()) {
-
-		$this->curlSettings[CURLOPT_POSTFIELDS] = $body;	 
+	
 		$url = $this->getAbsoluteUrl($url);
 	
+		$curlSettings = array(
+				CURLOPT_RETURNTRANSFER => true,
+				// Return headers as part of the response
+				CURLOPT_HEADER => true,
+				CURLOPT_POSTFIELDS => $body,
+				// Automatically follow redirects
+				CURLOPT_FOLLOWLOCATION => true,
+				CURLOPT_MAXREDIRS => 5,
+		);
+	
+		if($this->trustedCertificates) {
+			$curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates;
+		}
+		
 		switch ($method) {
-			case 'PUT':
-				$this->curlSettings[CURLOPT_PUT] = true;
-				break;
 			case 'HEAD' :
 	
 				// do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
@@ -83,12 +82,12 @@ class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
 				// specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with
 				// ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the
 				// response body
-				$this->curlSettings[CURLOPT_NOBODY] = true;
-				$this->curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD';
+				$curlSettings[CURLOPT_NOBODY] = true;
+				$curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD';
 				break;
 	
 			default:
-				$this->curlSettings[CURLOPT_CUSTOMREQUEST] = $method;
+				$curlSettings[CURLOPT_CUSTOMREQUEST] = $method;
 				break;
 	
 		}
@@ -100,15 +99,22 @@ class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
 			$nHeaders[] = $key . ': ' . $value;
 	
 		}
-		$this->curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
+		$curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
 	
 		if ($this->proxy) {
-			$this->curlSettings[CURLOPT_PROXY] = $this->proxy;
+			$curlSettings[CURLOPT_PROXY] = $this->proxy;
 		}
 	
-		if ($this->userName) {
-			$this->curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
-			$this->curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
+		if ($this->userName && $this->authType) {
+			$curlType = 0;
+			if ($this->authType & self::AUTH_BASIC) {
+				$curlType |= CURLAUTH_BASIC;
+			}
+			if ($this->authType & self::AUTH_DIGEST) {
+				$curlType |= CURLAUTH_DIGEST;
+			}
+			$curlSettings[CURLOPT_HTTPAUTH] = $curlType;
+			$curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
 		}
 	
 		list(
@@ -116,7 +122,7 @@ class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
 				$curlInfo,
 				$curlErrNo,
 				$curlError
-		) = $this->curlRequest($url, $this->curlSettings);
+		) = $this->curlRequest($url, $curlSettings);
 	
 		$headerBlob = substr($response, 0, $curlInfo['header_size']);
 		$response = substr($response, $curlInfo['header_size']);
@@ -163,6 +169,5 @@ class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
 	
 		return $response;
 	
-	}
-	
+	}	
 }
\ No newline at end of file
diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php
index b75bb5c50f50d3e0d19876a86de2401d9fc8c91b..0842fc4fc652b812ecbb3130d5dc7defb73a18fc 100644
--- a/lib/connector/sabre/directory.php
+++ b/lib/connector/sabre/directory.php
@@ -26,17 +26,33 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
 	/**
 	 * Creates a new file in the directory
 	 *
-	 * data is a readable stream resource
+	 * Data will either be supplied as a stream resource, or in certain cases
+	 * as a string. Keep in mind that you may have to support either.
+	 *
+	 * After succesful creation of the file, you may choose to return the ETag
+	 * of the new file here.
+	 *
+	 * The returned ETag must be surrounded by double-quotes (The quotes should
+	 * be part of the actual string).
+	 *
+	 * If you cannot accurately determine the ETag, you should not return it.
+	 * If you don't store the file exactly as-is (you're transforming it
+	 * somehow) you should also not return an ETag.
+	 *
+	 * This means that if a subsequent GET to this new file does not exactly
+	 * return the same contents of what was submitted here, you are strongly
+	 * recommended to omit the ETag.
 	 *
 	 * @param string $name Name of the file
-	 * @param resource $data Initial payload
-	 * @return void
+	 * @param resource|string $data Initial payload
+	 * @return null|string
 	 */
 	public function createFile($name, $data = null) {
 
 		$newPath = $this->path . '/' . $name;
 		OC_Filesystem::file_put_contents($newPath,$data);
 
+		return OC_Connector_Sabre_Node::getETagPropertyForFile($newPath);
 	}
 
 	/**
@@ -146,7 +162,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
 	 * @return array
 	 */
 	public function getQuotaInfo() {
-		$rootInfo=OC_FileCache::get('');
+		$rootInfo=OC_FileCache_Cached::get('');
 		return array(
 			$rootInfo['size'],
 			OC_Filesystem::free_space()
diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php
index dd25df78c2993c1138c27aa6bda438b8a757c86f..80f0a0ab4d89ca631ee57d4c9f10200ab709823b 100644
--- a/lib/connector/sabre/file.php
+++ b/lib/connector/sabre/file.php
@@ -26,13 +26,28 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D
 	/**
 	 * Updates the data
 	 *
+	 * The data argument is a readable stream resource.
+	 *
+	 * After a succesful put operation, you may choose to return an ETag. The
+	 * etag must always be surrounded by double-quotes. These quotes must
+	 * appear in the actual string you're returning.
+	 *
+	 * Clients may use the ETag from a PUT request to later on make sure that
+	 * when they update the file, the contents haven't changed in the mean
+	 * time.
+	 *
+	 * If you don't plan to store the file byte-by-byte, and you return a
+	 * different object on a subsequent GET you are strongly recommended to not
+	 * return an ETag, and just return null.
+	 *
 	 * @param resource $data
-	 * @return void
+	 * @return string|null
 	 */
 	public function put($data) {
 
 		OC_Filesystem::file_put_contents($this->path,$data);
 
+		return OC_Connector_Sabre_Node::getETagPropertyForFile($this->path);
 	}
 
 	/**
@@ -42,7 +57,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D
 	 */
 	public function get() {
 
-		return OC_Filesystem::fopen($this->path,'r');
+		return OC_Filesystem::fopen($this->path,'rb');
 
 	}
 
@@ -79,9 +94,11 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D
 	 * @return mixed
 	 */
 	public function getETag() {
-
-		return null;
-
+		$properties = $this->getProperties(array(self::GETETAG_PROPERTYNAME));
+		if (isset($properties[self::GETETAG_PROPERTYNAME])) {
+			return $properties[self::GETETAG_PROPERTYNAME];
+		}
+		return $this->getETagPropertyForFile($this->path);
 	}
 
 	/**
diff --git a/lib/connector/sabre/locks.php b/lib/connector/sabre/locks.php
index 94382e68a1a23981fa0d3368e412d6ae4047c276..e95dcf02d271d1d3f514928edf8f741118f4e065 100644
--- a/lib/connector/sabre/locks.php
+++ b/lib/connector/sabre/locks.php
@@ -108,7 +108,7 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract {
 
 		$locks = $this->getLocks($uri,false);
 		$exists = false;
-		foreach($locks as $k=>$lock) {
+		foreach($locks as $lock) {
 			if ($lock->token == $lockInfo->token) $exists = true;
 		}
 	
diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php
index be315a0ffd96cc98f52e18c3de85c762a19d3b8f..663970487fbd8a30be036be3da361ec33206adc9 100644
--- a/lib/connector/sabre/node.php
+++ b/lib/connector/sabre/node.php
@@ -22,6 +22,7 @@
  */
 
 abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties {
+	const GETETAG_PROPERTYNAME = '{DAV:}getetag';
 
 	/**
 	 * The path to the current node
@@ -141,6 +142,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
 	public function updateProperties($properties) {
 		$existing = $this->getProperties(array());
 		foreach($properties as $propertyName => $propertyValue) {
+			$propertyName = preg_replace("/^{.*}/", "", $propertyName); // remove leading namespace from property name
 			// If it was null, we need to delete the property
 			if (is_null($propertyValue)) {
 				if(array_key_exists( $propertyName, $existing )){
@@ -178,7 +180,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
 	 * @param array $properties
 	 * @return void
 	 */
-	function getProperties($properties) {
+	public function getProperties($properties) {
 		if (is_null($this->property_cache)) {
 			$query = OC_DB::prepare( 'SELECT * FROM *PREFIX*properties WHERE userid = ? AND propertypath = ?' );
 			$result = $query->execute( array( OC_User::getUser(), $this->path ));
@@ -200,4 +202,29 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
 		}
 		return $props;
 	}
+
+	/**
+	 * Returns the ETag surrounded by double-quotes for this path.
+	 * @param string $path Path of the file
+	 * @return string|null Returns null if the ETag can not effectively be determined
+	 */
+	static public function getETagPropertyForFile($path) {
+		$tag = OC_Filesystem::hash('md5', $path);
+		if (empty($tag)) {
+			return null;
+		}
+		$etag = '"'.$tag.'"';
+		$query = OC_DB::prepare( 'INSERT INTO *PREFIX*properties (userid,propertypath,propertyname,propertyvalue) VALUES(?,?,?,?)' );
+		$query->execute( array( OC_User::getUser(), $path, self::GETETAG_PROPERTYNAME, $etag ));
+		return $etag;
+	}
+
+	/**
+	 * Remove the ETag from the cache.
+	 * @param string $path Path of the file
+	 */
+	static public function removeETagPropertyForFile($path) {
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*properties WHERE userid = ? AND propertypath = ? AND propertyname = ?' );
+		$query->execute( array( OC_User::getUser(), $path, self::GETETAG_PROPERTYNAME ));
+	}
 }
diff --git a/lib/db.php b/lib/db.php
index 2a06d72ea323db8e4e0e53f168fe982335814523..6971fe4a58351267275fafee903905eb072059fb 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -33,8 +33,6 @@ class OC_DB {
 	static private $MDB2=false;
 	static private $PDO=false;
 	static private $schema=false;
-	static private $affected=0;
-	static private $result=false;
 	static private $inTransaction=false;
 	static private $prefix=null;
 	static private $type=null;
@@ -222,7 +220,7 @@ class OC_DB {
 				echo( '<b>can not connect to database, using '.$type.'. ('.self::$MDB2->getUserInfo().')</center>');
 				OC_Log::write('core',self::$MDB2->getUserInfo(),OC_Log::FATAL);
 				OC_Log::write('core',self::$MDB2->getMessage(),OC_Log::FATAL);
-				die( $error );
+				die();
 			}
 			
 			// We always, really always want associative arrays
@@ -370,9 +368,6 @@ class OC_DB {
 		if( $definition instanceof MDB2_Schema_Error ){
 			die( $definition->getMessage().': '.$definition->getUserInfo());
 		}
-// 		if(OC_Config::getValue('dbtype','sqlite')=='sqlite'){
-// 			$definition['overwrite']=true;//always overwrite for sqlite
-// 		}
 		$ret=self::$schema->createDatabase( $definition );
 
 		// Die in case something went wrong
@@ -519,8 +514,9 @@ class OC_DB {
 		
 		// Delete our temporary file
 		unlink( $file2 );
-		foreach($definition['tables'] as $name=>$table){
-			self::dropTable($name);
+		$tables=array_keys($definition['tables']);
+		foreach($tables as $table){
+			self::dropTable($table);
 		}
 	}
 	
@@ -528,8 +524,7 @@ class OC_DB {
 	 * @brief replaces the owncloud tables with a new set
 	 * @param $file string path to the MDB2 xml db export file
 	 */
-	 public static function replaceDB( $file ){
-	 	
+	public static function replaceDB( $file ){
 	 	$apps = OC_App::getAllApps();
 	 	self::beginTransaction();
 	 	// Delete the old tables
diff --git a/lib/eventsource.php b/lib/eventsource.php
index cf10660b94cef8eef84f1de02b8dca807dd97b69..95af2e471bca36cd7b80694b55d030f6616c2efd 100644
--- a/lib/eventsource.php
+++ b/lib/eventsource.php
@@ -36,12 +36,15 @@ class OC_EventSource{
 		header('Cache-Control: no-cache');
 		$this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true';
 		if($this->fallback){
-			$fallBackId=$_GET['fallback_id'];
+			$this->fallBackId=$_GET['fallback_id'];
 			header("Content-Type: text/html");
 			echo str_repeat('<span></span>'.PHP_EOL,10); //dummy data to keep IE happy
 		}else{
 			header("Content-Type: text/event-stream");
 		}
+		if( !OC_Util::isCallRegistered()){
+			exit();
+		}
 		flush();
 
 	}
diff --git a/lib/exception.php b/lib/exception.php
new file mode 100644
index 0000000000000000000000000000000000000000..db516fc12d217d18367f0854e466381406f19cce
--- /dev/null
+++ b/lib/exception.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Georg Ehrke
+ * @copyright 2012 georg@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+class OC_Exception extends Exception{
+	
+	function __construct($message = null, $code = 0, $file = null, $line = null){
+		parent::__construct($message, $code);
+		if(!is_null($file)){
+			$this->file = $file;
+		}
+		if(!is_null($line)){
+			$this->line = $line;
+		}
+		$this->writelog();
+	}
+	
+	private function writelog(){
+		@OC_Log::write(OC_App::getCurrentApp(), $this->getMessage() . '-' . $this->getFile() . '-' . $this->getLine(), OC_Log::FATAL);
+	}
+	
+	private function generatesysinfo(){
+		return array('phpversion' => PHP_VERSION,
+					 'os' => php_uname('s'),
+					 'osrelease' => php_uname('r'),
+					 'osarchitecture' => php_uname('m'),
+					 'phpserverinterface' => php_sapi_name(),
+					 'serverprotocol' => $_SERVER['SERVER_PROTOCOL'],
+					 'requestmethod' => $_SERVER['REQUEST_METHOD'],
+					 'https' => ($_SERVER['HTTPS']==''?'false':'true'),
+					 'database'=>(@OC_Config::getValue('dbtype')!=''?@OC_Config::getValue('dbtype'):'')
+					);
+	}
+	
+	function __toString(){
+		$tmpl = new OC_Template('core', 'exception', 'guest');
+		$tmpl->assign('showsysinfo', true);
+		$tmpl->assign('message', $this->getMessage());
+		$tmpl->assign('code', $this->getCode());
+		$tmpl->assign('file', $this->getFile());
+		$tmpl->assign('line', $this->getLine());
+		$tmpl->assign('sysinfo', $this->generatesysinfo());
+		$tmpl->printPage();
+	}
+}
+
+function oc_exceptionhandler($exception){
+	switch($exception->getCode()){
+		case E_NOTICE:
+		case E_DEPRECATED:
+		case E_USER_NOTICE:
+		case E_USER_DEPRECATED:
+			break;
+		default:
+			throw new OC_Exception($exception->getMessage(), $exception->getCode(), $exception->getFile(), $exception->getLine());
+			break;
+	}
+	return true;
+}
+
+function oc_errorhandler($errno , $errstr , $errfile , $errline){
+	switch($errno){
+		case E_NOTICE:
+		case E_DEPRECATED:
+		case E_USER_NOTICE:
+		case E_USER_DEPRECATED:
+			break;
+		default:
+			throw new OC_Exception($errstr, $errno, $errfile, $errline);
+			break;	
+	}
+	return true;
+}
+set_exception_handler('oc_exceptionhandler');
+set_error_handler('oc_errorhandler');
+error_reporting(E_ERROR | E_WARNING | E_PARSE);
\ No newline at end of file
diff --git a/lib/filecache.php b/lib/filecache.php
index d956f34dc48790be0611be8729751c0102786922..22f7427ae42e3335d5599f5ef4469a2ff2a8c4a0 100644
--- a/lib/filecache.php
+++ b/lib/filecache.php
@@ -98,6 +98,10 @@ class OC_FileCache{
 		if(OC_DB::isError($result)){
 			OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR);
 		}
+
+		if($cache=OC_Cache::getUserCache(true)){
+			$cache->remove('fileid/'.$path);//ensure we don't have -1 cached
+		}
 	}
 
 	/**
@@ -126,7 +130,7 @@ class OC_FileCache{
 		$query=OC_DB::prepare($sql);
 		$result=$query->execute($arguments);
 		if(OC_DB::isError($result)){
-			OC_Log::write('files','error while updating file('.$path.') in cache',OC_Log::ERROR);
+			OC_Log::write('files','error while updating file('.$id.') in cache',OC_Log::ERROR);
 		}
 	}
 
@@ -146,6 +150,11 @@ class OC_FileCache{
 		$query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=?, path_hash=? WHERE path_hash=?');
 		$query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath)));
 
+		if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$oldPath)){
+			$cache->set('fileid/'.$newPath,$cache->get('fileid/'.$oldPath));
+			$cache->remove('fileid/'.$oldPath);
+		}
+
 		$query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE path LIKE ?');
 		$oldLength=strlen($oldPath);
 		$updateQuery=OC_DB::prepare('UPDATE *PREFIX*fscache SET path=?, path_hash=? WHERE path_hash=?');
@@ -153,6 +162,11 @@ class OC_FileCache{
 			$old=$row['path'];
 			$new=$newPath.substr($old,$oldLength);
 			$updateQuery->execute(array($new,md5($new),md5($old)));
+
+			if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$old)){
+				$cache->set('fileid/'.$new,$cache->get('fileid/'.$old));
+				$cache->remove('fileid/'.$old);
+			}
 		}
 	}
 
@@ -171,6 +185,8 @@ class OC_FileCache{
 		//delete everything inside the folder
 		$query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE path LIKE ?');
 		$query->execute(array($root.$path.'/%'));
+
+		OC_Cache::remove('fileid/'.$root.$path);
 	}
 	
 	/**
@@ -245,9 +261,14 @@ class OC_FileCache{
 		if($root===false){
 			$root=OC_Filesystem::getRoot();
 		}
+
+		$fullPath=$root.$path;
+		if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$fullPath)){
+			return $cache->get('fileid/'.$fullPath);
+		}
 		
 		$query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path_hash=?');
-		$result=$query->execute(array(md5($root.$path)));
+		$result=$query->execute(array(md5($fullPath)));
 		if(OC_DB::isError($result)){
 			OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
 			return -1;
@@ -255,10 +276,15 @@ class OC_FileCache{
 		
 		$result=$result->fetchRow();
 		if(is_array($result)){
-			return $result['id'];
+			$id=$result['id'];
 		}else{
-			return -1;
+			$id=-1;
 		}
+		if($cache=OC_Cache::getUserCache(true)){
+			$cache->set('fileid/'.$fullPath,$id);
+		}
+		
+		return $id;
 	}
 	
 	/**
@@ -303,7 +329,7 @@ class OC_FileCache{
 	 */
 	public static function increaseSize($path,$sizeDiff, $root=false){
 		if($sizeDiff==0) return;
-		$id=self::getId($path,'');
+		$id=self::getId($path,$root);
 		while($id!=-1){//walk up the filetree increasing the size of all parent folders
 			$query=OC_DB::prepare('UPDATE *PREFIX*fscache SET size=size+? WHERE id=?');
 			$query->execute(array($sizeDiff,$id));
diff --git a/lib/filecache/update.php b/lib/filecache/update.php
index dd77f491ca053afbe22cb2024eefe155bfaba5bf..93b632acb4e9daf4a814a5725ebf0e87730ef83c 100644
--- a/lib/filecache/update.php
+++ b/lib/filecache/update.php
@@ -207,7 +207,6 @@ class OC_FileCache_Update{
 
 		$cached=OC_FileCache_Cached::get($oldPath,$root);
 		$oldSize=$cached['size'];
-		$size=$view->filesize($newPath);
 		OC_FileCache::increaseSize(dirname($oldPath),-$oldSize,$root);
 		OC_FileCache::increaseSize(dirname($newPath),$oldSize,$root);
 		OC_FileCache::move($oldPath,$newPath);
diff --git a/lib/filestorage.php b/lib/filestorage.php
index 71ef4aed00be83d86e30b4de67de306cc438edf2..e786127d525b6dc12c30e52ba818e15a0f37da7a 100644
--- a/lib/filestorage.php
+++ b/lib/filestorage.php
@@ -24,7 +24,7 @@
  * Provde a common interface to all different storage options
  */
 abstract class OC_Filestorage{
-	public function __construct($parameters){}
+	abstract public function __construct($parameters);
 	abstract public function mkdir($path);
 	abstract public function rmdir($path);
 	abstract public function opendir($path);
diff --git a/lib/filestorage/common.php b/lib/filestorage/common.php
index ba78fca80e5e529d1f3b5319bbd3c523bb6e2b74..fd389d3e2d7ac0e930e443f32f5ed54e45d67361 100644
--- a/lib/filestorage/common.php
+++ b/lib/filestorage/common.php
@@ -220,7 +220,7 @@ abstract class OC_Filestorage_Common extends OC_Filestorage {
 		}
 		$tmpFile=OC_Helper::tmpFile($extension);
 		$target=fopen($tmpFile,'w');
-		$count=OC_Helper::streamCopy($source,$target);
+		OC_Helper::streamCopy($source,$target);
 		return $tmpFile;
 	}
 // 	abstract public function touch($path, $mtime=null);
diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php
index b2eba051515828ad60966bbaf651cc1667f977b2..d60f32b15be050ebab581872df0b15b2a163026d 100644
--- a/lib/filestorage/local.php
+++ b/lib/filestorage/local.php
@@ -4,7 +4,6 @@
  */
 class OC_Filestorage_Local extends OC_Filestorage_Common{
 	protected $datadir;
-	private static $mimetypes=null;
 	public function __construct($arguments){
 		$this->datadir=$arguments['datadir'];
 		if(substr($this->datadir,-1)!=='/'){
@@ -41,7 +40,7 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{
 	}
 	public function filesize($path){
 		if($this->is_dir($path)){
-			return $this->getFolderSize($path);
+			return 0;
 		}else{
 			return filesize($this->datadir.$path);
 		}
@@ -157,7 +156,7 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{
 		return $return;
 	}
 
-	public function hash($type,$path,$raw){
+	public function hash($path,$type,$raw=false){
 		return hash_file($type,$this->datadir.$path,$raw);
 	}
 
@@ -186,15 +185,6 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{
 		return $files;
 	}
 
-	/**
-	 * @brief get the size of folder and it's content
-	 * @param string $path file path
-	 * @return int size of folder and it's content
-	 */
-	public function getFolderSize($path){
-		return 0;//depricated, use OC_FileCach instead
-	}
-
 	/**
 	 * check if a file or folder has been updated since $time
 	 * @param int $time
diff --git a/lib/filesystem.php b/lib/filesystem.php
index 65318fa3ab695214e52723d16aa289fa2610c19d..a5edcf5bab3d385917643e1599ad546ce20cdd7c 100644
--- a/lib/filesystem.php
+++ b/lib/filesystem.php
@@ -46,105 +46,106 @@
 class OC_Filesystem{
 	static private $storages=array();
 	static private $mounts=array();
-	static private $storageTypes=array();
 	public static $loaded=false;
-	private $fakeRoot='';
+	/**
+	 * @var OC_Filestorage $defaultInstance
+	 */
 	static private $defaultInstance;
 
 
-  /**
-   * classname which used for hooks handling
-   * used as signalclass in OC_Hooks::emit()
-   */
-  const CLASSNAME = 'OC_Filesystem';
-
-  /**
-   * signalname emited before file renaming
-   * @param oldpath
-   * @param newpath
-   */
-  const signal_rename = 'rename';
-
-  /**
-   * signal emited after file renaming
-   * @param oldpath
-   * @param newpath
-   */
-  const signal_post_rename = 'post_rename';
-	
-  /**
-   * signal emited before file/dir creation
-   * @param path
-   * @param run changing this flag to false in hook handler will cancel event
-   */
-  const signal_create = 'create';
-
-  /**
-   * signal emited after file/dir creation
-   * @param path
-   * @param run changing this flag to false in hook handler will cancel event
-   */
-  const signal_post_create = 'post_create';
-
-  /**
-   * signal emits before file/dir copy
-   * @param oldpath
-   * @param newpath
-   * @param run changing this flag to false in hook handler will cancel event
-   */
-  const signal_copy = 'copy';
-
-  /**
-   * signal emits after file/dir copy
-   * @param oldpath
-   * @param newpath
-   */
-  const signal_post_copy = 'post_copy';
-
-  /**
-   * signal emits before file/dir save
-   * @param path
-   * @param run changing this flag to false in hook handler will cancel event
-   */
-  const signal_write = 'write';
-
-  /**
-   * signal emits after file/dir save
-   * @param path
-   */
-  const signal_post_write = 'post_write';
-	
-  /**
-   * signal emits when reading file/dir
-   * @param path
-   */
-  const signal_read = 'read';
-
-  /**
-   * signal emits when removing file/dir
-   * @param path
-   */
-  const signal_delete = 'delete';
-
-  /**
-   * parameters definitions for signals
-   */
-  const signal_param_path = 'path';
-  const signal_param_oldpath = 'oldpath';
-  const signal_param_newpath = 'newpath';
-
-  /**
-   * run - changing this flag to false in hook handler will cancel event
-   */
-  const signal_param_run = 'run';
-
-  /**
-	* get the mountpoint of the storage object for a path
-	( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
-	*
-	* @param string path
-	* @return string
-	*/
+	/**
+	 * classname which used for hooks handling
+	 * used as signalclass in OC_Hooks::emit()
+	 */
+	const CLASSNAME = 'OC_Filesystem';
+
+	/**
+	 * signalname emited before file renaming
+	 * @param oldpath
+	 * @param newpath
+	 */
+	const signal_rename = 'rename';
+
+	/**
+	 * signal emited after file renaming
+	 * @param oldpath
+	 * @param newpath
+	 */
+	const signal_post_rename = 'post_rename';
+
+	/**
+	 * signal emited before file/dir creation
+	 * @param path
+	 * @param run changing this flag to false in hook handler will cancel event
+	 */
+	const signal_create = 'create';
+
+	/**
+	 * signal emited after file/dir creation
+	 * @param path
+	 * @param run changing this flag to false in hook handler will cancel event
+	 */
+	const signal_post_create = 'post_create';
+
+	/**
+	 * signal emits before file/dir copy
+	* @param oldpath
+	 * @param newpath
+	  * @param run changing this flag to false in hook handler will cancel event
+	 */
+	const signal_copy = 'copy';
+
+	/**
+	 * signal emits after file/dir copy
+	 * @param oldpath
+	 * @param newpath
+	 */
+	const signal_post_copy = 'post_copy';
+
+	/**
+	 * signal emits before file/dir save
+	 * @param path
+	 * @param run changing this flag to false in hook handler will cancel event
+	 */
+	const signal_write = 'write';
+
+	/**
+	 * signal emits after file/dir save
+	 * @param path
+	 */
+	const signal_post_write = 'post_write';
+
+	/**
+	 * signal emits when reading file/dir
+	 * @param path
+	 */
+	const signal_read = 'read';
+
+	/**
+	 * signal emits when removing file/dir
+	 * @param path
+	 */
+	const signal_delete = 'delete';
+
+	/**
+	 * parameters definitions for signals
+	 */
+	const signal_param_path = 'path';
+	const signal_param_oldpath = 'oldpath';
+	const signal_param_newpath = 'newpath';
+
+	/**
+	 * run - changing this flag to false in hook handler will cancel event
+	 */
+	const signal_param_run = 'run';
+
+	/**
+	 * get the mountpoint of the storage object for a path
+	 ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
+	 *
+	 * @param string path
+	  * @return string
+	 */
 	static public function getMountPoint($path){
 		OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path));
 		if(!$path){
@@ -155,7 +156,8 @@ class OC_Filesystem{
 		}
 		$path=str_replace('//', '/',$path);
 		$foundMountPoint='';
-		foreach(OC_Filesystem::$mounts as $mountpoint=>$storage){
+		$mountPoints=array_keys(OC_Filesystem::$mounts);
+		foreach($mountPoints as $mountpoint){
 			if($mountpoint==$path){
 				return $mountpoint;
 			}
@@ -260,10 +262,7 @@ class OC_Filesystem{
 	 * tear down the filesystem, removing all storage providers
 	 */
 	static public function tearDown(){
-		foreach(self::$storages as $mountpoint=>$storage){
-			unset(self::$storages[$mountpoint]);
-		}
-		$fakeRoot='';
+		self::$storages=array();
 	}
 	
 	/**
@@ -287,7 +286,7 @@ class OC_Filesystem{
 	* @return bool
 	*/
 	static public function chroot($fakeRoot){
-		return self::$defaultInstance->chroot($path);
+		return self::$defaultInstance->chroot($fakeRoot);
 	}
 
 	/**
@@ -320,22 +319,8 @@ class OC_Filesystem{
 		if(substr($mountpoint,-1)!=='/'){
 			$mountpoint=$mountpoint.'/';
 		}
-		if (self::getView() != null && $mountpoint != '/' && !self::is_dir(basename($mountpoint))) {
-			self::mkdir(basename($mountpoint));
-		}
 		self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments);
 	}
-
-	/**
-	 * create all storage backends mounted in the filesystem
-	 */
-	static private function mountAll(){
-		foreach(self::$mounts as $mountPoint=>$mount){
-			if(!isset(self::$storages[$mountPoint])){
-				self::$storages[$mountPoint]=self::createStorage($mount['type'],$mount['arguments']);
-			}
-		}
-	}
 	
 	/**
 	* return the path to a local version of the file
@@ -485,9 +470,17 @@ class OC_Filesystem{
 	 * @return bool
 	 */
 	static public function hasUpdated($path,$time){
-		return self::$defaultInstance->hasUpdated($path);
+		return self::$defaultInstance->hasUpdated($path,$time);
+	}
+
+	static public function removeETagHook($params) {
+		$path=$params['path'];
+		OC_Connector_Sabre_Node::removeETagPropertyForFile($path);
 	}
 }
+OC_Hook::connect('OC_Filesystem','post_write', 'OC_Filesystem','removeETagHook');
+OC_Hook::connect('OC_Filesystem','post_delete','OC_Filesystem','removeETagHook');
+OC_Hook::connect('OC_Filesystem','post_rename','OC_Filesystem','removeETagHook');
 
 OC_Util::setupFS();
 require_once('filecache.php');
diff --git a/lib/filesystemview.php b/lib/filesystemview.php
index 448663bb0811b5906499d2c15a11ab910e343e69..9beda01e5a1917692c190086024e05485a36be88 100644
--- a/lib/filesystemview.php
+++ b/lib/filesystemview.php
@@ -22,19 +22,19 @@
 
 
 /**
- * Class to provide access to ownCloud filesystem via a "view", and methods for 
- * working with files within that view (e.g. read, write, delete, etc.). Each 
- * view is restricted to a set of directories via a virtual root. The default view 
- * uses the currently logged in user's data directory as root (parts of 
+ * Class to provide access to ownCloud filesystem via a "view", and methods for
+ * working with files within that view (e.g. read, write, delete, etc.). Each
+ * view is restricted to a set of directories via a virtual root. The default view
+ * uses the currently logged in user's data directory as root (parts of
  * OC_Filesystem are merely a wrapper for OC_FilesystemView).
- * 
+ *
  * Apps that need to access files outside of the user data folders (to modify files
  * belonging to a user other than the one currently logged in, for example) should
  * use this class directly rather than using OC_Filesystem, or making use of PHP's
- * built-in file manipulation functions. This will ensure all hooks and proxies 
+ * built-in file manipulation functions. This will ensure all hooks and proxies
  * are triggered correctly.
  *
- * Filesystem functions are not called directly; they are passed to the correct 
+ * Filesystem functions are not called directly; they are passed to the correct
  * OC_Filestorage object
  */
 
@@ -43,11 +43,11 @@ class OC_FilesystemView {
 	private $internal_path_cache=array();
 	private $storage_cache=array();
 
-	public function __construct($root){
+	public function __construct($root) {
 		$this->fakeRoot=$root;
 	}
 
-	public function getAbsolutePath($path){
+	public function getAbsolutePath($path) {
 		if(!$path){
 			$path='/';
 		}
@@ -63,9 +63,9 @@ class OC_FilesystemView {
 	* @param  string  fakeRoot
 	* @return bool
 	*/
-	public function chroot($fakeRoot){
+	public function chroot($fakeRoot) {
 		if(!$fakeRoot==''){
-			if($fakeRoot[0]!=='/'){
+			if($fakeRoot[0]!=='/') {
 				$fakeRoot='/'.$fakeRoot;
 			}
 		}
@@ -76,7 +76,7 @@ class OC_FilesystemView {
 	 * get the fake root
 	 * @return string
 	 */
-	public function getRoot(){
+	public function getRoot() {
 		return $this->fakeRoot;
 	}
 
@@ -85,7 +85,7 @@ class OC_FilesystemView {
 	* @param  string  path
 	* @return bool
 	*/
-	public function getInternalPath($path){
+	public function getInternalPath($path) {
 		if (!isset($this->internal_path_cache[$path])) {
 			$this->internal_path_cache[$path] = OC_Filesystem::getInternalPath($this->getAbsolutePath($path));
 		}
@@ -97,23 +97,23 @@ class OC_FilesystemView {
 	 * @param string path
 	 * @return string
 	 */
-	public function getRelativePath($path){
-		if($this->fakeRoot==''){
+	public function getRelativePath($path) {
+		if($this->fakeRoot=='') {
 			return $path;
 		}
-		if(strpos($path,$this->fakeRoot)!==0){
+		if(strpos($path, $this->fakeRoot)!==0) {
 			return null;
 		}else{
-			return substr($path,strlen($this->fakeRoot));
+			return substr($path, strlen($this->fakeRoot));
 		}
 	}
-	
+
 	/**
 	* get the storage object for a path
 	* @param string path
 	* @return OC_Filestorage
 	*/
-	public function getStorage($path){
+	public function getStorage($path) {
 		if (!isset($this->storage_cache[$path])) {
 			$this->storage_cache[$path] = OC_Filesystem::getStorage($this->getAbsolutePath($path));
 		}
@@ -127,7 +127,7 @@ class OC_FilesystemView {
 	* @param string path
 	* @return string
 	*/
-	public function getMountPoint($path){
+	public function getMountPoint($path) {
 		return OC_Filesystem::getMountPoint($this->getAbsolutePath($path));
 	}
 
@@ -137,55 +137,55 @@ class OC_FilesystemView {
 	* @param string path
 	* @return string
 	*/
-	public function getLocalFile($path){
-		$parent=substr($path,0,strrpos($path,'/'));
-		if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)){
+	public function getLocalFile($path) {
+		$parent=substr($path, 0, strrpos($path,'/'));
+		if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) {
 			return $storage->getLocalFile($this->getInternalPath($path));
 		}
 	}
 
 	/**
-	 * the following functions operate with arguments and return values identical 
-	 * to those of their PHP built-in equivalents. Mostly they are merely wrappers 
+	 * the following functions operate with arguments and return values identical
+	 * to those of their PHP built-in equivalents. Mostly they are merely wrappers
 	 * for OC_Filestorage via basicOperation().
 	 */
-	public function mkdir($path){
-		return $this->basicOperation('mkdir',$path,array('create','write'));
+	public function mkdir($path) {
+		return $this->basicOperation('mkdir', $path, array('create', 'write'));
 	}
-	public function rmdir($path){
-		return $this->basicOperation('rmdir',$path,array('delete'));
+	public function rmdir($path) {
+		return $this->basicOperation('rmdir', $path, array('delete'));
 	}
-	public function opendir($path){
-		return $this->basicOperation('opendir',$path,array('read'));
+	public function opendir($path) {
+		return $this->basicOperation('opendir', $path, array('read'));
 	}
-	public function readdir($handle){
+	public function readdir($handle) {
 		$fsLocal= new OC_Filestorage_Local( array( 'datadir' => '/' ) );
 		return $fsLocal->readdir( $handle );
 	}
-	public function is_dir($path){
+	public function is_dir($path) {
 		if($path=='/'){
 			return true;
 		}
-		return $this->basicOperation('is_dir',$path);
+		return $this->basicOperation('is_dir', $path);
 	}
-	public function is_file($path){
+	public function is_file($path) {
 		if($path=='/'){
 			return false;
 		}
-		return $this->basicOperation('is_file',$path);
+		return $this->basicOperation('is_file', $path);
 	}
-	public function stat($path){
-		return $this->basicOperation('stat',$path);
+	public function stat($path) {
+		return $this->basicOperation('stat', $path);
 	}
-	public function filetype($path){
-		return $this->basicOperation('filetype',$path);
+	public function filetype($path) {
+		return $this->basicOperation('filetype', $path);
 	}
-	public function filesize($path){
-		return $this->basicOperation('filesize',$path);
+	public function filesize($path) {
+		return $this->basicOperation('filesize', $path);
 	}
-	public function readfile($path){
+	public function readfile($path) {
 		@ob_end_clean();
-		$handle=$this->fopen($path,'rb');
+		$handle=$this->fopen($path, 'rb');
 		if ($handle) {
 			$chunkSize = 8192;// 8 MB chunks
 			while (!feof($handle)) {
@@ -197,137 +197,210 @@ class OC_FilesystemView {
 		}
 		return false;
 	}
-	public function is_readable($path){
-		return $this->basicOperation('is_readable',$path);
+	public function is_readable($path) {
+		return $this->basicOperation('is_readable', $path);
 	}
-	public function is_writable($path){
-		return $this->basicOperation('is_writable',$path);
+	public function is_writable($path) {
+		return $this->basicOperation('is_writable', $path);
 	}
-	public function file_exists($path){
+	public function file_exists($path) {
 		if($path=='/'){
 			return true;
 		}
-		return $this->basicOperation('file_exists',$path);
+		return $this->basicOperation('file_exists', $path);
 	}
-	public function filectime($path){
-		return $this->basicOperation('filectime',$path);
+	public function filectime($path) {
+		return $this->basicOperation('filectime', $path);
 	}
-	public function filemtime($path){
-		return $this->basicOperation('filemtime',$path);
+	public function filemtime($path) {
+		return $this->basicOperation('filemtime', $path);
 	}
-	public function touch($path, $mtime=null){
+	public function touch($path, $mtime=null) {
 		return $this->basicOperation('touch', $path, array('write'), $mtime);
 	}
-	public function file_get_contents($path){
-		return $this->basicOperation('file_get_contents',$path,array('read'));
-	}
-	public function file_put_contents($path,$data){
-		if(is_resource($data)){//not having to deal with streams in file_put_contents makes life easier
-			$exists=$this->file_exists($path);
-			$run=true;
-			if(!$exists){
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
+	public function file_get_contents($path) {
+		return $this->basicOperation('file_get_contents', $path, array('read'));
+	}
+	public function file_put_contents($path, $data) {
+		if(is_resource($data)) {//not having to deal with streams in file_put_contents makes life easier
+			$exists = $this->file_exists($path);
+			$run = true;
+			if(!$exists) {
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_create,
+					array(
+						OC_Filesystem::signal_param_path => $path,
+						OC_Filesystem::signal_param_run => &$run
+					)
+				);
 			}
-			OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
-			if(!$run){
+			OC_Hook::emit(
+				OC_Filesystem::CLASSNAME,
+				OC_Filesystem::signal_write,
+				array(
+					OC_Filesystem::signal_param_path => $path,
+					OC_Filesystem::signal_param_run => &$run
+				)
+			);
+			if(!$run) {
 				return false;
 			}
-			$target=$this->fopen($path,'w');
-			if($target){
-				$count=OC_Helper::streamCopy($data,$target);
+			$target=$this->fopen($path, 'w');
+			if($target) {
+				$count=OC_Helper::streamCopy($data, $target);
 				fclose($target);
 				fclose($data);
-				if(!$exists){
-					OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path));
+				if(!$exists) {
+					OC_Hook::emit(
+						OC_Filesystem::CLASSNAME,
+						OC_Filesystem::signal_post_create,
+						array( OC_Filesystem::signal_param_path => $path)
+					);
 				}
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path));
-				return $count>0;
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_post_write,
+					array( OC_Filesystem::signal_param_path => $path)
+				);
+				return $count > 0;
 			}else{
 				return false;
 			}
 		}else{
-			return $this->basicOperation('file_put_contents',$path,array('create','write'),$data);
+			return $this->basicOperation('file_put_contents', $path, array('create', 'write'), $data);
 		}
 	}
-	public function unlink($path){
-		return $this->basicOperation('unlink',$path,array('delete'));
+	public function unlink($path) {
+		return $this->basicOperation('unlink', $path, array('delete'));
 	}
 	public function deleteAll( $directory, $empty = false ) {
 		return $this->basicOperation( 'deleteAll', $directory, array('delete'), $empty );
 	}
-	public function rename($path1,$path2){
-		$absolutePath1=$this->getAbsolutePath($path1);
-		$absolutePath2=$this->getAbsolutePath($path2);
-		if(OC_FileProxy::runPreProxies('rename',$absolutePath1,$absolutePath2) and OC_Filesystem::isValidPath($path2)){
-			$path1=$this->getRelativePath($absolutePath1);
-			$path2=$this->getRelativePath($absolutePath2);
-			if($path1==null or $path2==null){
+	public function rename($path1, $path2) {
+		$absolutePath1 = $this->getAbsolutePath($path1);
+		$absolutePath2 = $this->getAbsolutePath($path2);
+		if(OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) {
+			$path1 = $this->getRelativePath($absolutePath1);
+			$path2 = $this->getRelativePath($absolutePath2);
+			if($path1 == null or $path2 == null) {
 				return false;
 			}
 			$run=true;
-			OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run));
-			if($run){
-				$mp1=$this->getMountPoint($path1);
-				$mp2=$this->getMountPoint($path2);
-				if($mp1==$mp2){
-					if($storage=$this->getStorage($path1)){
-						$result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2));
+			OC_Hook::emit(
+				OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename,
+					array(
+						OC_Filesystem::signal_param_oldpath => $path1,
+						OC_Filesystem::signal_param_newpath => $path2,
+						OC_Filesystem::signal_param_run => &$run
+					)
+			);
+			if($run) {
+				$mp1 = $this->getMountPoint($path1);
+				$mp2 = $this->getMountPoint($path2);
+				if($mp1 == $mp2) {
+					if($storage = $this->getStorage($path1)) {
+						$result = $storage->rename($this->getInternalPath($path1), $this->getInternalPath($path2));
 					}
-				}else{
-					$source=$this->fopen($path1,'r');
-					$target=$this->fopen($path2,'w');
-					$count=OC_Helper::streamCopy($source,$target);
-					$storage1=$this->getStorage($path1);
+				} else {
+					$source = $this->fopen($path1, 'r');
+					$target = $this->fopen($path2, 'w');
+					$count = OC_Helper::streamCopy($source, $target);
+					$storage1 = $this->getStorage($path1);
 					$storage1->unlink($this->getInternalPath($path1));
-					$result=$count>0;
+					$result = $count>0;
 				}
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2));
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_post_rename,
+					array(
+						OC_Filesystem::signal_param_oldpath => $path1,
+						OC_Filesystem::signal_param_newpath => $path2
+					)
+				);
 				return $result;
 			}
 		}
 	}
-	public function copy($path1,$path2){
-		$absolutePath1=$this->getAbsolutePath($path1);
-		$absolutePath2=$this->getAbsolutePath($path2);
-		if(OC_FileProxy::runPreProxies('copy',$absolutePath1,$absolutePath2) and OC_Filesystem::isValidPath($path2)){
-			$path1=$this->getRelativePath($absolutePath1);
-			$path2=$this->getRelativePath($absolutePath2);
-			if($path1==null or $path2==null){
+	public function copy($path1, $path2) {
+		$absolutePath1 = $this->getAbsolutePath($path1);
+		$absolutePath2 = $this->getAbsolutePath($path2);
+		if(OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) {
+			$path1 = $this->getRelativePath($absolutePath1);
+			$path2 = $this->getRelativePath($absolutePath2);
+			if($path1 == null or $path2 == null) {
 				return false;
 			}
 			$run=true;
-			OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run));
+			OC_Hook::emit(
+				OC_Filesystem::CLASSNAME,
+				OC_Filesystem::signal_copy,
+				array(
+					OC_Filesystem::signal_param_oldpath => $path1,
+					OC_Filesystem::signal_param_newpath=>$path2,
+					OC_Filesystem::signal_param_run => &$run
+				)
+			);
 			$exists=$this->file_exists($path2);
-			if($run and !$exists){
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run));
+			if($run and !$exists) {
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_create,
+					array(
+						OC_Filesystem::signal_param_path => $path2,
+						OC_Filesystem::signal_param_run => &$run
+					)
+				);
 			}
-			if($run){
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run));
+			if($run) {
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_write,
+					array(
+						OC_Filesystem::signal_param_path => $path2,
+						OC_Filesystem::signal_param_run => &$run
+					)
+				);
 			}
-			if($run){
+			if($run) {
 				$mp1=$this->getMountPoint($path1);
 				$mp2=$this->getMountPoint($path2);
-				if($mp1==$mp2){
-					if($storage=$this->getStorage($path1)){
-						$result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2));
+				if($mp1 == $mp2){
+					if($storage = $this->getStorage($path1)) {
+						$result=$storage->copy($this->getInternalPath($path1), $this->getInternalPath($path2));
 					}
-				}else{
-					$source=$this->fopen($path1,'r');
-					$target=$this->fopen($path2,'w');
-					$result=OC_Helper::streamCopy($source,$target);
+				} else {
+					$source = $this->fopen($path1, 'r');
+					$target = $this->fopen($path2, 'w');
+					$result = OC_Helper::streamCopy($source, $target);
 				}
-        OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2));
-				if(!$exists){
-          OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path2));
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_post_copy,
+					array(
+						OC_Filesystem::signal_param_oldpath => $path1,
+						OC_Filesystem::signal_param_newpath=>$path2
+					)
+				);
+				if(!$exists) {
+					OC_Hook::emit(
+						OC_Filesystem::CLASSNAME,
+						OC_Filesystem::signal_post_create,
+						array(OC_Filesystem::signal_param_path => $path2)
+					);
 				}
-        OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path2));
+				OC_Hook::emit(
+					OC_Filesystem::CLASSNAME,
+					OC_Filesystem::signal_post_write,
+					array( OC_Filesystem::signal_param_path => $path2)
+				);
 				return $result;
 			}
 		}
 	}
-	public function fopen($path,$mode){
+	public function fopen($path, $mode) {
 		$hooks=array();
-		switch($mode){
+		switch($mode) {
 			case 'r':
 			case 'rb':
 				$hooks[]='read';
@@ -355,49 +428,49 @@ class OC_FilesystemView {
 				OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR);
 		}
 
-		return $this->basicOperation('fopen',$path,$hooks,$mode);
+		return $this->basicOperation('fopen', $path, $hooks, $mode);
 	}
-	public function toTmpFile($path){
-		if(OC_Filesystem::isValidPath($path)){
-			$source=$this->fopen($path,'r');
-			if($source){
+	public function toTmpFile($path) {
+		if(OC_Filesystem::isValidPath($path)) {
+			$source = $this->fopen($path, 'r');
+			if($source) {
 				$extension='';
-				$extOffset=strpos($path,'.');
+				$extOffset=strpos($path, '.');
 				if($extOffset !== false) {
-					$extension=substr($path,strrpos($path,'.'));
+					$extension=substr($path, strrpos($path,'.'));
 				}
-				$tmpFile=OC_Helper::tmpFile($extension);
-				file_put_contents($tmpFile,$source);
+				$tmpFile = OC_Helper::tmpFile($extension);
+				file_put_contents($tmpFile, $source);
 				return $tmpFile;
 			}
 		}
 	}
-	public function fromTmpFile($tmpFile,$path){
-		if(OC_Filesystem::isValidPath($path)){
-			if(!$tmpFile){
+	public function fromTmpFile($tmpFile, $path) {
+		if(OC_Filesystem::isValidPath($path)) {
+			if(!$tmpFile) {
 				debug_print_backtrace();
 			}
-			$source=fopen($tmpFile,'r');
-			if($source){
-				$this->file_put_contents($path,$source);
+			$source=fopen($tmpFile, 'r');
+			if($source) {
+				$this->file_put_contents($path, $source);
 				unlink($tmpFile);
 				return true;
-			}else{
+			} else {
 			}
-		}else{
+		} else {
 			return false;
 		}
 	}
 
-	public function getMimeType($path){
-		return $this->basicOperation('getMimeType',$path);
+	public function getMimeType($path) {
+		return $this->basicOperation('getMimeType', $path);
 	}
-	public function hash($type,$path){
-		return $this->basicOperation('hash',$path,array('read'));
+	public function hash($type, $path) {
+		return $this->basicOperation('hash', $path, array('read'), $type);
 	}
 
-	public function free_space($path='/'){
-		return $this->basicOperation('free_space',$path);
+	public function free_space($path='/') {
+		return $this->basicOperation('free_space', $path);
 	}
 
 	/**
@@ -407,41 +480,56 @@ class OC_FilesystemView {
 	 * @param array (optional) hooks
 	 * @param mixed (optional) $extraParam
 	 * @return mixed
-	 * 
-	 * This method takes requests for basic filesystem functions (e.g. reading & writing 
-	 * files), processes hooks and proxies, sanitises paths, and finally passes them on to 
+	 *
+	 * This method takes requests for basic filesystem functions (e.g. reading & writing
+	 * files), processes hooks and proxies, sanitises paths, and finally passes them on to
 	 * OC_Filestorage for delegation to a storage backend for execution
 	 */
-	private function basicOperation($operation,$path,$hooks=array(),$extraParam=null){
-		$absolutePath=$this->getAbsolutePath($path);
-		if(OC_FileProxy::runPreProxies($operation,$absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)){
-			$path=$this->getRelativePath($absolutePath);
-			if($path==null){
+	private function basicOperation($operation, $path, $hooks=array(), $extraParam=null) {
+		$absolutePath = $this->getAbsolutePath($path);
+		if(OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)) {
+			$path = $this->getRelativePath($absolutePath);
+			if($path == null) {
 				return false;
 			}
-			$internalPath=$this->getInternalPath($path);
-			$run=true;
-			if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){
-				foreach($hooks as $hook){
-					if($hook!='read'){
-						OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
-					}else{
-						OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path));
+			$internalPath = $this->getInternalPath($path);
+			$run = true;
+			if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) {
+				foreach($hooks as $hook) {
+					if($hook!='read') {
+						OC_Hook::emit(
+							OC_Filesystem::CLASSNAME,
+							$hook,
+							array(
+								OC_Filesystem::signal_param_path => $path,
+								OC_Filesystem::signal_param_run => &$run
+							)
+						);
+					} else {
+						OC_Hook::emit(
+							OC_Filesystem::CLASSNAME,
+							$hook,
+							array( OC_Filesystem::signal_param_path => $path)
+						);
 					}
 				}
 			}
-			if($run and $storage=$this->getStorage($path)){
-				if(!is_null($extraParam)){
-					$result=$storage->$operation($internalPath,$extraParam);
-				}else{
-					$result=$storage->$operation($internalPath);
+			if($run and $storage = $this->getStorage($path)) {
+				if(!is_null($extraParam)) {
+					$result = $storage->$operation($internalPath, $extraParam);
+				} else {
+					$result = $storage->$operation($internalPath);
 				}
-				$result=OC_FileProxy::runPostProxies($operation,$this->getAbsolutePath($path),$result);
-				if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){
-					if($operation!='fopen'){//no post hooks for fopen, the file stream is still open
-						foreach($hooks as $hook){
+				$result = OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result);
+				if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) {
+					if($operation!='fopen') {//no post hooks for fopen, the file stream is still open
+						foreach($hooks as $hook) {
 							if($hook!='read'){
-								OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path));
+								OC_Hook::emit(
+									OC_Filesystem::CLASSNAME,
+									'post_'.$hook,
+									array( OC_Filesystem::signal_param_path => $path)
+								);
 							}
 						}
 					}
@@ -457,7 +545,7 @@ class OC_FilesystemView {
 	 * @param int $time
 	 * @return bool
 	 */
-	public function hasUpdated($path,$time){
-		return $this->basicOperation('hasUpdated',$path,array(),$time);
+	public function hasUpdated($path, $time) {
+		return $this->basicOperation('hasUpdated', $path, array(), $time);
 	}
 }
diff --git a/lib/group.php b/lib/group.php
index ceee5fa4edb787f0a0217ca8e69591b5078265b6..12e5f5ebb30ff82cccc343b456fec5232d6425cf 100644
--- a/lib/group.php
+++ b/lib/group.php
@@ -43,7 +43,7 @@ class OC_Group {
 	 * @returns true/false
 	 */
 	public static function useBackend( $backend ){
-		if($backend instanceof OC_Group_Backend){
+		if($backend instanceof OC_Group_Interface){
 			self::$_usedBackends[]=$backend;
 		}
 	}
@@ -168,7 +168,7 @@ class OC_Group {
 
 		if($run){
 			$succes=false;
-			
+
 			//add the user to the all backends that have the group
 			foreach(self::$_usedBackends as $backend){
 				if(!$backend->implementsActions(OC_GROUP_BACKEND_ADD_TO_GROUP))
@@ -245,7 +245,7 @@ class OC_Group {
 		asort($groups);
 		return $groups;
 	}
-	
+
 	/**
 	 * check if a group exists
 	 * @param string $gid
@@ -259,7 +259,7 @@ class OC_Group {
 		}
 		return false;
 	}
-	
+
 	/**
 	 * @brief get a list of all users in a group
 	 * @returns array with user ids
diff --git a/lib/group/backend.php b/lib/group/backend.php
index 24778afd1e56c3859684f7fb635abf5d22b964c7..ebc078f152a85d850e3597862b3289c6bc7ad240 100644
--- a/lib/group/backend.php
+++ b/lib/group/backend.php
@@ -37,7 +37,7 @@ define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP',  0x00001000);
 /**
  * Abstract base class for user management
  */
-abstract class OC_Group_Backend {
+abstract class OC_Group_Backend implements OC_Group_Interface {
 	protected $possibleActions = array(
 		OC_GROUP_BACKEND_CREATE_GROUP => 'createGroup',
 		OC_GROUP_BACKEND_DELETE_GROUP => 'deleteGroup',
diff --git a/lib/group/database.php b/lib/group/database.php
index fb173665eb8ad2ece78b04c755bc88aef9b44ef5..2770ec185c4aa1797d46723feb0b34b5631890b4 100644
--- a/lib/group/database.php
+++ b/lib/group/database.php
@@ -41,7 +41,6 @@
  * Class for group management in a SQL Database (e.g. MySQL, SQLite)
  */
 class OC_Group_Database extends OC_Group_Backend {
-	private $userGroupCache=array();
 
 	/**
 	 * @brief Try to create a new group
@@ -116,7 +115,7 @@ class OC_Group_Database extends OC_Group_Backend {
 		// No duplicate entries!
 		if( !$this->inGroup( $uid, $gid )){
 			$query = OC_DB::prepare( "INSERT INTO `*PREFIX*group_user` ( `uid`, `gid` ) VALUES( ?, ? )" );
-			$result = $query->execute( array( $uid, $gid ));
+			$query->execute( array( $uid, $gid ));
 			return true;
 		}else{
 			return false;
@@ -133,7 +132,7 @@ class OC_Group_Database extends OC_Group_Backend {
 	 */
 	public function removeFromGroup( $uid, $gid ){
 		$query = OC_DB::prepare( "DELETE FROM *PREFIX*group_user WHERE uid = ? AND gid = ?" );
-		$result = $query->execute( array( $uid, $gid ));
+		$query->execute( array( $uid, $gid ));
 
 		return true;
 	}
diff --git a/lib/group/dummy.php b/lib/group/dummy.php
index 0825b10708a16d52f264bb71b0f3f8343cc93b46..1243891023feddaa163a6b6b9a52dabcb7d58ac4 100644
--- a/lib/group/dummy.php
+++ b/lib/group/dummy.php
@@ -126,7 +126,8 @@ class OC_Group_Dummy extends OC_Group_Backend {
 	 */
 	public function getUserGroups($uid){
 		$groups=array();
-		foreach($this->groups as $group=>$user){
+		$allGroups=array_keys($this->groups);
+		foreach($allGroups as $group){
 			if($this->inGroup($uid,$group)){
 				$groups[]=$group;
 			}
diff --git a/lib/group/example.php b/lib/group/example.php
index c18562db7a44db49a86fca758719a469eb5158c3..9c9ece5ac7758003b03b7946c1ce29d4d1fddfcf 100644
--- a/lib/group/example.php
+++ b/lib/group/example.php
@@ -34,7 +34,7 @@ abstract class OC_Group_Example {
 	 * Trys to create a new group. If the group name already exists, false will
 	 * be returned.
 	 */
-	public static function createGroup($gid){}
+	abstract public static function createGroup($gid);
 
 	/**
 	 * @brief delete a group
@@ -43,7 +43,7 @@ abstract class OC_Group_Example {
 	 *
 	 * Deletes a group and removes it from the group_user-table
 	 */
-	public static function deleteGroup($gid){}
+	abstract public static function deleteGroup($gid);
 
 	/**
 	 * @brief is user in group?
@@ -53,7 +53,7 @@ abstract class OC_Group_Example {
 	 *
 	 * Checks whether the user is member of a group or not.
 	 */
-	public static function inGroup($uid, $gid){}
+	abstract public static function inGroup($uid, $gid);
 
 	/**
 	 * @brief Add a user to a group
@@ -63,7 +63,7 @@ abstract class OC_Group_Example {
 	 *
 	 * Adds a user to a group.
 	 */
-	public static function addToGroup($uid, $gid){}
+	abstract public static function addToGroup($uid, $gid);
 
 	/**
 	 * @brief Removes a user from a group
@@ -73,7 +73,7 @@ abstract class OC_Group_Example {
 	 *
 	 * removes the user from a group.
 	 */
-	public static function removeFromGroup($uid,$gid){}
+	abstract public static function removeFromGroup($uid,$gid);
 
 	/**
 	 * @brief Get all groups a user belongs to
@@ -83,7 +83,7 @@ abstract class OC_Group_Example {
 	 * This function fetches all groups a user belongs to. It does not check
 	 * if the user exists at all.
 	 */
-	public static function getUserGroups($uid){}
+	abstract public static function getUserGroups($uid);
 
 	/**
 	 * @brief get a list of all groups
@@ -91,19 +91,19 @@ abstract class OC_Group_Example {
 	 *
 	 * Returns a list with all groups
 	 */
-	public static function getGroups(){}
+	abstract public static function getGroups();
 
 	/**
 	 * check if a group exists
 	 * @param string $gid
 	 * @return bool
 	 */
-	public function groupExists($gid){}
+	abstract public function groupExists($gid);
 
 	/**
 	 * @brief get a list of all users in a group
 	 * @returns array with user ids
 	 */
-	public static function usersInGroup($gid){}
+	abstract public static function usersInGroup($gid);
 
 }
diff --git a/lib/group/interface.php b/lib/group/interface.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cca6061e10370bc7e774fc6d2e37132c833fb50
--- /dev/null
+++ b/lib/group/interface.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * ownCloud - group interface
+ *
+ * @author Arthur Schiwon
+ * @copyright 2012 Arthur Schiwon blizzz@owncloud.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+interface OC_Group_Interface {
+	/**
+	* @brief Check if backend implements actions
+	* @param $actions bitwise-or'ed actions
+	* @returns boolean
+	*
+	* Returns the supported actions as int to be
+	* compared with OC_GROUP_BACKEND_CREATE_GROUP etc.
+	*/
+	public function implementsActions($actions);
+
+	/**
+	 * @brief is user in group?
+	 * @param $uid uid of the user
+	 * @param $gid gid of the group
+	 * @returns true/false
+	 *
+	 * Checks whether the user is member of a group or not.
+	 */
+	public function inGroup($uid, $gid);
+
+	/**
+	 * @brief Get all groups a user belongs to
+	 * @param $uid Name of the user
+	 * @returns array with group names
+	 *
+	 * This function fetches all groups a user belongs to. It does not check
+	 * if the user exists at all.
+	 */
+	public function getUserGroups($uid);
+
+	/**
+	 * @brief get a list of all groups
+	 * @returns array with group names
+	 *
+	 * Returns a list with all groups
+	 */
+	public function getGroups();
+
+	/**
+	 * check if a group exists
+	 * @param string $gid
+	 * @return bool
+	 */
+	public function groupExists($gid);
+
+	/**
+	 * @brief get a list of all users in a group
+	 * @returns array with user ids
+	 */
+	public function usersInGroup($gid);
+
+}
\ No newline at end of file
diff --git a/lib/helper.php b/lib/helper.php
index 0d18098a4e756ce55d73a2c4a8d6f7f4d2e8cc85..666bc6badfc9a718f43b5b9dcaf186a6baf24347 100644
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -175,10 +175,8 @@ class OC_Helper {
 	 */
 	public static function mimetypeIcon( $mimetype ){
 		$alias=array('application/xml'=>'code/xml');
-// 		echo $mimetype;
 		if(isset($alias[$mimetype])){
 			$mimetype=$alias[$mimetype];
-// 			echo $mimetype;
 		}
 		// Replace slash with a minus
 		$mimetype = str_replace( "/", "-", $mimetype );
@@ -345,18 +343,24 @@ class OC_Helper {
 	 */
 	static function getMimeType($path){
 		$isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://');
-		$mimeType='application/octet-stream';
-		if ($mimeType=='application/octet-stream') {
-			self::$mimetypes = include('mimetypes.fixlist.php');
-			$extension=strtolower(strrchr(basename($path), "."));
-			$extension=substr($extension,1);//remove leading .
-			$mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream';
 
-		}
 		if (@is_dir($path)) {
 			// directories are easy
 			return "httpd/unix-directory";
 		}
+
+		if(strpos($path,'.')){
+			//try to guess the type by the file extension
+			if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){
+				self::$mimetypes=include('mimetypes.list.php');
+			}
+			$extension=strtolower(strrchr(basename($path), "."));
+			$extension=substr($extension,1);//remove leading .
+			$mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream';
+		}else{
+			$mimeType='application/octet-stream';
+		}
+
 		if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){
 			$info = @strtolower(finfo_file($finfo,$path));
 			if($info){
@@ -385,15 +389,6 @@ class OC_Helper {
 			}
 
 		}
-		if ($mimeType=='application/octet-stream') {
-			// Fallback solution: (try to guess the type by the file extension
-			if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){
-				self::$mimetypes=include('mimetypes.list.php');
-			}
-			$extension=strtolower(strrchr(basename($path), "."));
-			$extension=substr($extension,1);//remove leading .
-			$mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream';
-		}
 		return $mimeType;
 	}
 
@@ -676,12 +671,38 @@ class OC_Helper {
 	*/
 	public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
 		$offset = -1;
-		$length = mb_strlen($search, 'UTF-8');
-		while(($i = mb_strrpos($subject, $search, $offset, 'UTF-8'))) {
+		$length = mb_strlen($search, $encoding);
+		while(($i = mb_strrpos($subject, $search, $offset, $encoding))) {
 			$subject = OC_Helper::mb_substr_replace($subject, $replace, $i, $length);
-			$offset = $i - mb_strlen($subject, 'UTF-8') - 1;
+			$offset = $i - mb_strlen($subject, $encoding) - 1;
 			$count++;
 		}
 		return $subject;
 	}
+
+	/**
+	* @brief performs a search in a nested array
+	* @param haystack the array to be searched
+	* @param needle the search string
+	* @param $index optional, only search this key name
+	* @return the key of the matching field, otherwise false
+	*
+	* performs a search in a nested array
+	*
+	* taken from http://www.php.net/manual/en/function.array-search.php#97645
+	*/
+	public static function recursiveArraySearch($haystack, $needle, $index = null) {
+		$aIt = new RecursiveArrayIterator($haystack);
+		$it = new RecursiveIteratorIterator($aIt);
+
+		while($it->valid()) {
+			if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
+				return $aIt->key();
+			}
+
+			$it->next();
+		}
+
+		return false;
+	}
 }
diff --git a/lib/image.php b/lib/image.php
index e5c59bacdc50245a7db078b88510a4a38258a4a5..90c64320a7c8fe59715963af77a1ffd5607b1d6f 100644
--- a/lib/image.php
+++ b/lib/image.php
@@ -23,12 +23,12 @@
 
 //From user comments at http://dk2.php.net/manual/en/function.exif-imagetype.php
 if ( ! function_exists( 'exif_imagetype' ) ) {
-    function exif_imagetype ( $filename ) {
-        if ( ( list($width, $height, $type, $attr) = getimagesize( $filename ) ) !== false ) {
-            return $type;
-        }
-    return false;
-    }
+	function exif_imagetype ( $filename ) {
+		if ( ( $info = getimagesize( $filename ) ) !== false ) {
+			return $info[2];
+		}
+		return false;
+	}
 }
 
 function ellipsis($str, $maxlen) {
@@ -66,7 +66,6 @@ class OC_Image {
 	public function __construct($imageref = null) {
 		//OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG);
 		if(!extension_loaded('gd') || !function_exists('gd_info')) {
-		//if(!function_exists('imagecreatefromjpeg')) {
 			OC_Log::write('core',__METHOD__.'(): GD module not installed', OC_Log::ERROR);
 			return false;
 		}
@@ -107,6 +106,56 @@ class OC_Image {
 		return $this->valid() ? imagesy($this->resource) : -1;
 	}
 
+	/**
+	* @brief Returns the width when the image orientation is top-left.
+	* @returns int
+	*/
+	public function widthTopLeft() {
+		$o = $this->getOrientation();
+		OC_Log::write('core','OC_Image->widthTopLeft() Orientation: '.$o, OC_Log::DEBUG);
+		switch($o) {
+			case -1:
+			case 1:
+			case 2: // Not tested
+			case 3:
+			case 4: // Not tested
+				return $this->width();
+				break;
+			case 5: // Not tested
+			case 6:
+			case 7: // Not tested
+			case 8:
+				return $this->height();
+				break;
+		}
+		return $this->width();
+	}
+
+	/**
+	* @brief Returns the height when the image orientation is top-left.
+	* @returns int
+	*/
+	public function heightTopLeft() {
+		$o = $this->getOrientation();
+		OC_Log::write('core','OC_Image->heightTopLeft() Orientation: '.$o, OC_Log::DEBUG);
+		switch($o) {
+			case -1:
+			case 1:
+			case 2: // Not tested
+			case 3:
+			case 4: // Not tested
+				return $this->height();
+				break;
+			case 5: // Not tested
+			case 6:
+			case 7: // Not tested
+			case 8:
+				return $this->width();
+				break;
+		}
+		return $this->height();
+	}
+
 	/**
 	* @brief Outputs the image.
 	* @returns bool
@@ -209,34 +258,46 @@ class OC_Image {
 
 	/**
 	* (I'm open for suggestions on better method name ;)
-	* @brief Fixes orientation based on EXIF data.
-	* @returns bool.
+	* @brief Get the orientation based on EXIF data.
+	* @returns The orientation or -1 if no EXIF data is available.
 	*/
-	public function fixOrientation() {
+	public function getOrientation() {
 		if(!is_callable('exif_read_data')){
 			OC_Log::write('core','OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG);
-			return false;
+			return -1;
 		}
 		if(!$this->valid()) {
 			OC_Log::write('core','OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG);
-			return false;
+			return -1;
 		}
 		if(is_null($this->filepath) || !is_readable($this->filepath)) {
 			OC_Log::write('core','OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG);
-			return false;
+			return -1;
 		}
 		$exif = @exif_read_data($this->filepath, 'IFD0');
 		if(!$exif) {
-			return false;
+			return -1;
 		}
 		if(!isset($exif['Orientation'])) {
-			return true; // Nothing to fix
+			return -1;
 		}
-		$o = $exif['Orientation'];
+		return $exif['Orientation'];
+	}
+
+	/**
+	* (I'm open for suggestions on better method name ;)
+	* @brief Fixes orientation based on EXIF data.
+	* @returns bool.
+	*/
+	public function fixOrientation() {
+		$o = $this->getOrientation();
 		OC_Log::write('core','OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG);
 		$rotate = 0;
 		$flip = false;
 		switch($o) {
+			case -1:
+				return false; //Nothing to fix
+				break;
 			case 1:
 				$rotate = 0;
 				$flip = false;
@@ -302,7 +363,7 @@ class OC_Image {
 	public function load($imageref) {
 		if(is_resource($imageref)) {
 			if(get_resource_type($imageref) == 'gd') {
-				$this->resource = $res;
+				$this->resource = $imageref;
 				return $this->resource;
 			} elseif(in_array(get_resource_type($imageref), array('file','stream'))) {
 				return $this->loadFromFileHandle($imageref);
@@ -588,9 +649,6 @@ class OC_Image {
 			OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
 			return false;
 		}
-		$width_orig=imageSX($this->resource);
-		$height_orig=imageSY($this->resource);
-		//OC_Log::write('core',__METHOD__.'(): Original size: '.$width_orig.'x'.$height_orig, OC_Log::DEBUG);
 		$process = imagecreatetruecolor($w, $h);
 		if ($process == false) {
 			OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
diff --git a/lib/installer.php b/lib/installer.php
index 00feb6d470940c95a8943c8751ee583a0ffdd690..a8b56cb34f2eff68fc5d4797ed09e70b8dc1e2a9 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -126,19 +126,19 @@ class OC_Installer{
 			return false;
 		}
 		$info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml',true);
-                // check the code for not allowed calls
-                if(!OC_Installer::checkCode($info['id'],$extractDir)){
+		// check the code for not allowed calls
+		if(!OC_Installer::checkCode($info['id'],$extractDir)){
 			OC_Log::write('core','App can\'t be installed because of not allowed code in the App',OC_Log::ERROR);
 			OC_Helper::rmdirr($extractDir);
-                        return false;
+			return false;
 		}
 
-                // check if the app is compatible with this version of ownCloud
+		// check if the app is compatible with this version of ownCloud
 		$version=OC_Util::getVersion();	
-                if(!isset($info['require']) or ($version[0]>$info['require'])){
+		if(!isset($info['require']) or ($version[0]>$info['require'])){
 			OC_Log::write('core','App can\'t be installed because it is not compatible with this version of ownCloud',OC_Log::ERROR);
 			OC_Helper::rmdirr($extractDir);
-                        return false;
+			return false;
 		}
 
 		//check if an app with the same id is already installed
@@ -339,12 +339,12 @@ class OC_Installer{
 	}
 
 
-        /**
-         * check the code of an app with some static code checks
-         * @param string $folder the folder of the app to check
-         * @returns true for app is o.k. and false for app is not o.k.
-         */
-        public static function checkCode($appname,$folder){
+	/**
+	 * check the code of an app with some static code checks
+	 * @param string $folder the folder of the app to check
+	 * @returns true for app is o.k. and false for app is not o.k.
+	 */
+	public static function checkCode($appname,$folder){
 
 		$blacklist=array(
 			'exec(',
@@ -377,9 +377,7 @@ class OC_Installer{
 			return true;
 			
 		}else{
-          		return true;
+			return true;
 		}
-        }
-
-
+	}
 }
diff --git a/lib/json.php b/lib/json.php
index c49b831c12bd18fb7af7d5fa040dd26c9c593dd8..b46878375d5c7610b44760355f7d27599eaff498 100644
--- a/lib/json.php
+++ b/lib/json.php
@@ -94,12 +94,12 @@ class OC_JSON{
 	* Encode and print $data in json format
 	*/
 	public static function encodedPrint($data,$setContentType=true){
-			// Disable mimesniffing, don't move this to setContentTypeHeader!
-			header( 'X-Content-Type-Options: nosniff' );
-			if($setContentType){
-				self::setContentTypeHeader();
-			}
-			array_walk_recursive($data, array('OC_JSON', 'to_string'));
-			echo json_encode($data);
+		// Disable mimesniffing, don't move this to setContentTypeHeader!
+		header( 'X-Content-Type-Options: nosniff' );
+		if($setContentType){
+			self::setContentTypeHeader();
+		}
+		array_walk_recursive($data, array('OC_JSON', 'to_string'));
+		echo json_encode($data);
 	}
 }
diff --git a/lib/l10n.php b/lib/l10n.php
index de8514573d393a68db014dfdd7a4694f9f4e169c..e7f5ffea0e4938c66d3507215f3b5a8443aa012a 100644
--- a/lib/l10n.php
+++ b/lib/l10n.php
@@ -154,8 +154,15 @@ class OC_L10N{
 	 *
 	 * Returns the translation. If no translation is found, $textArray will be
 	 * returned.
+	 *
+	 *
+	 * @deprecated deprecated since ownCloud version 5.0
+	 * This method will probably be removed with ownCloud 6.0
+	 *
+	 *
 	 */
 	public function tA($textArray){
+		OC_Log::write('core', 'DEPRECATED: the method tA is deprecated and will be removed soon.',OC_Log::WARN);
 		$result = array();
 		foreach($textArray as $key => $text){
 			$result[$key] = (string)$this->t($text);
diff --git a/lib/mail.php b/lib/mail.php
index 7343f5f0d97a7271b8035e4c7913318aa3586124..7eb2c4770c56545c727f786316b5972b51f2b7f9 100644
--- a/lib/mail.php
+++ b/lib/mail.php
@@ -36,7 +36,7 @@ class OC_Mail {
 		$SMTPPASSWORD = OC_Config::getValue( 'mail_smtppassword', '' );  
 
 
-		$mailo = new PHPMailer();
+		$mailo = new PHPMailer(true);
 		if($SMTPMODE=='sendmail') {
 			$mailo->IsSendmail();
 		}elseif($SMTPMODE=='smtp'){
@@ -56,33 +56,35 @@ class OC_Mail {
 		$mailo->From =$fromaddress;
 		$mailo->FromName = $fromname;;
 		$a=explode(' ',$toaddress);
-		foreach($a as $ad) {
-			$mailo->AddAddress($ad,$toname);
+		try {
+			foreach($a as $ad) {
+				$mailo->AddAddress($ad,$toname);
+			}
+
+			if($ccaddress<>'') $mailo->AddCC($ccaddress,$ccname);
+			if($bcc<>'') $mailo->AddBCC($bcc);
+
+			$mailo->AddReplyTo($fromaddress, $fromname);
+
+			$mailo->WordWrap = 50;
+			if($html==1) $mailo->IsHTML(true); else $mailo->IsHTML(false);
+
+			$mailo->Subject = $subject;
+			if($altbody=='') {
+				$mailo->Body    = $mailtext.OC_MAIL::getfooter();
+				$mailo->AltBody = '';
+			}else{
+				$mailo->Body    = $mailtext;
+				$mailo->AltBody = $altbody;
+			}
+			$mailo->CharSet = 'UTF-8';
+
+			$mailo->Send();
+			unset($mailo);
+			OC_Log::write('mail', 'Mail from '.$fromname.' ('.$fromaddress.')'.' to: '.$toname.'('.$toaddress.')'.' subject: '.$subject, OC_Log::DEBUG);
+		} catch (Exception $exception) {
+			OC_Log::write('mail', $exception->getMessage(), OC_Log::DEBUG);
 		}
-
-		if($ccaddress<>'') $mailo->AddCC($ccaddress,$ccname);
-		if($bcc<>'') $mailo->AddBCC($bcc);
-
-		$mailo->AddReplyTo($fromaddress, $fromname);
-
-		$mailo->WordWrap = 50;
-		if($html==1) $mailo->IsHTML(true); else $mailo->IsHTML(false);
-
-		$mailo->Subject = $subject;
-		if($altbody=='') {
-			$mailo->Body    = $mailtext.OC_MAIL::getfooter();
-			$mailo->AltBody = '';
-		}else{
-			$mailo->Body    = $mailtext;
-			$mailo->AltBody = $altbody;
-		}
-		$mailo->CharSet = 'UTF-8';
-
-		$mailo->Send();
-		unset($mailo);
-
-		OC_Log::write('Mail from '.$fromname.' ('.$fromaddress.')'.' to: '.$toname.'('.$toaddress.')'.' subject: '.$subject,'mail',OC_Log::DEBUG);
-
 	}
 
 
diff --git a/lib/migrate.php b/lib/migrate.php
index f788a637d3ce1ae4753c62d34425d1b8adc4bca4..1b6367ed6ec6448bc6cc135b6dec3da7f91ab017 100644
--- a/lib/migrate.php
+++ b/lib/migrate.php
@@ -91,7 +91,7 @@ class OC_Migrate{
 	 	if( self::$exporttype == 'user' ){
 	 		// Check user exists
 	 		if( !is_null($uid) ){
-                                $db = new OC_User_Database;
+				$db = new OC_User_Database;
 		 		if( !$db->userExists( $uid ) ){
 					OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR);
 					return json_encode( array( 'success' => false ) );
diff --git a/lib/mimetypes.fixlist.php b/lib/mimetypes.fixlist.php
deleted file mode 100644
index 13e3f16b3696a08e4b4289e39369b5157b68a6f5..0000000000000000000000000000000000000000
--- a/lib/mimetypes.fixlist.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-return array(
-	'ics'=>'text/calendar',
-	'ical'=>'text/calendar',
-	'js'=>'application/javascript',
-	'odt'=>'application/vnd.oasis.opendocument.text',
-	'ods'=>'application/vnd.oasis.opendocument.spreadsheet',
-	'odg'=>'application/vnd.oasis.opendocument.graphics',
-	'odp'=>'application/vnd.oasis.opendocument.presentation',
-	'pl'=>'text/x-script.perl',
-	'py'=>'text/x-script.phyton',
-	'vcf' => 'text/vcard',
-	'vcard' => 'text/vcard',
-	'doc'=>'application/msword',
-	'docx'=>'application/msword',
-	'xls'=>'application/msexcel',
-	'xlsx'=>'application/msexcel',
-	'ppt'=>'application/mspowerpoint',
-	'pptx'=>'application/mspowerpoint',
-	'sgf' => 'application/sgf',
-	'cdr' => 'application/coreldraw'
-);
diff --git a/lib/mimetypes.list.php b/lib/mimetypes.list.php
index ccf47999b1cfa1dbbe26c68c814b97405259c6b3..f7207493f7f2633f8d47f066418a6aea03742087 100644
--- a/lib/mimetypes.list.php
+++ b/lib/mimetypes.list.php
@@ -78,5 +78,16 @@ return array(
 	'mpeg'=>'video/mpeg',
 	'mov'=>'video/quicktime',
 	'webm'=>'video/webm',
-	'wmv'=>'video/x-ms-asf'
+	'wmv'=>'video/x-ms-asf',
+	'py'=>'text/x-script.phyton',
+	'vcf' => 'text/vcard',
+	'vcard' => 'text/vcard',
+	'doc'=>'application/msword',
+	'docx'=>'application/msword',
+	'xls'=>'application/msexcel',
+	'xlsx'=>'application/msexcel',
+	'ppt'=>'application/mspowerpoint',
+	'pptx'=>'application/mspowerpoint',
+	'sgf' => 'application/sgf',
+	'cdr' => 'application/coreldraw',
 );
diff --git a/lib/minimizer.php b/lib/minimizer.php
index 3bf5ff9980b8457dc72a8cebc5f5b5742e4f001a..3dc89e331a648f0498ab33b187052f0fc2fc1b59 100644
--- a/lib/minimizer.php
+++ b/lib/minimizer.php
@@ -46,3 +46,13 @@ abstract class OC_Minimizer {
 		echo $out;
 	}
 }
+
+if (!function_exists('gzdecode')) {
+	function gzdecode($data,$maxlength=null,&$filename='',&$error='')
+	{
+		if (strcmp(substr($data,0,9),"\x1f\x8b\x8\0\0\0\0\0\0")) {
+			return null;  // Not the GZIP format we expect (See RFC 1952)
+		}
+		return gzinflate(substr($data,10,-8));
+	}
+}
diff --git a/lib/ocs.php b/lib/ocs.php
index 5e697b4830496588d3ec1b1100d25be5bae45020..d7a7951fab53d3f453a87569ef6387e1a0a65c5c 100644
--- a/lib/ocs.php
+++ b/lib/ocs.php
@@ -23,8 +23,6 @@
 *
 */
 
-
-
 /**
  * Class to handle open collaboration services API requests
  *
@@ -92,77 +90,160 @@ class OC_OCS {
 			exit();
 		}
 
-		// preprocess url
-		$url = strtolower($_SERVER['REQUEST_URI']);
-		if(substr($url,(strlen($url)-1))<>'/') $url.='/';
-		$ex=explode('/',$url);
-		$paracount=count($ex);
 		$format = self::readData($method, 'format', 'text', '');
-		// eventhandler
+
+		$router = new OC_Router();
+		$router->useCollection('ocs');
 		// CONFIG
-		// apiconfig - GET - CONFIG
-		if(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'config')){
-			OC_OCS::apiconfig($format);
+		$router->create('config', '/config.{format}')
+			->defaults(array('format' => $format))
+			->action('OC_OCS', 'apiConfig')
+			->requirements(array('format'=>'xml|json'));
 
 		// PERSON
-		// personcheck - POST - PERSON/CHECK
-		}elseif(($method=='post') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-3]=='person') and ($ex[$paracount-2] == 'check')){
-			$login = self::readData($method, 'login', 'text');
-			$passwd = self::readData($method, 'password', 'text');
-			OC_OCS::personcheck($format,$login,$passwd);
-		} else if ($method == 'post' && $ex[$paracount - 4] == 'v1.php' && $ex[$paracount - 3] == 'person' && $ex[$paracount - 2] == 'add') {
-			if (OC_Group::inGroup(self::checkPassword(), 'admin')) {
-				$login = self::readData($method, 'login', 'text');
-				$password = self::readData($method, 'password', 'text');
-				try {
-					OC_User::createUser($login, $password);
-					echo self::generateXml($format, 'ok', 201, '');
-				} catch (Exception $exception) {
-					echo self::generateXml($format, 'fail', 400, $exception->getMessage());
-				}
-			} else {
-				echo self::generateXml($format, 'fail', 403, 'Permission denied');
-			}
+		$router->create('person_check', '/person/check.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$login = OC_OCS::readData('post', 'login', 'text');
+					$passwd = OC_OCS::readData('post', 'password', 'text');
+					OC_OCS::personCheck($format,$login,$passwd);
+					})
+			->requirements(array('format'=>'xml|json'));
+
 		// ACTIVITY
 		// activityget - GET ACTIVITY   page,pagesize als urlparameter
-		}elseif(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')){
-			$page = self::readData($method, 'page', 'int', 0);
-			$pagesize = self::readData($method, 'pagesize','int', 10);
-			if($pagesize<1 or $pagesize>100) $pagesize=10;
-			OC_OCS::activityget($format,$page,$pagesize);
-
+		$router->create('activity_get', '/activity.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$page = OC_OCS::readData('get', 'page', 'int', 0);
+					$pagesize = OC_OCS::readData('get', 'pagesize', 'int', 10);
+					if($pagesize<1 or $pagesize>100) $pagesize=10;
+					OC_OCS::activityGet($format, $page, $pagesize);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// activityput - POST ACTIVITY
-		}elseif(($method=='post') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')){
-			$message = self::readData($method, 'message', 'text');
-			OC_OCS::activityput($format,$message);
+		$router->create('activity_put', '/activity.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$message = OC_OCS::readData('post', 'message', 'text');
+					OC_OCS::activityPut($format,$message);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// PRIVATEDATA
 		// get - GET DATA
-		}elseif(($method=='get') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-2] == 'getattribute')){
-			OC_OCS::privateDataGet($format);
-
-		}elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-3] == 'getattribute')){
-			$app=$ex[$paracount-2];
-			OC_OCS::privateDataGet($format, $app);
-				}elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'getattribute')){
-			
-			$key=$ex[$paracount-2];
-			$app=$ex[$paracount-3];
-			OC_OCS::privateDataGet($format, $app,$key);
-
+		$router->create('privatedata_get',
+				  '/privatedata/getattribute/{app}/{key}.{format}')
+			->defaults(array('app' => '', 'key' => '', 'format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$app = addslashes(strip_tags($parameters['app']));
+					$key = addslashes(strip_tags($parameters['key']));
+					OC_OCS::privateDataGet($format, $app, $key);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// set - POST DATA
-		}elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'setattribute')){
-			$key=$ex[$paracount-2];
-			$app=$ex[$paracount-3];
-			$value = self::readData($method, 'value', 'text');
-			OC_OCS::privatedataset($format, $app, $key, $value);
+		$router->create('privatedata_set',
+				  '/privatedata/setattribute/{app}/{key}.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$app = addslashes(strip_tags($parameters['app']));
+					$key = addslashes(strip_tags($parameters['key']));
+					$value=OC_OCS::readData('post', 'value', 'text');
+					OC_OCS::privateDataSet($format, $app, $key, $value);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// delete - POST DATA
-		}elseif(($method=='post') and ($ex[$paracount-6] =='v1.php') and ($ex[$paracount-4] == 'deleteattribute')){
-			$key=$ex[$paracount-2];
-			$app=$ex[$paracount-3];
-			OC_OCS::privatedatadelete($format, $app, $key);
-
-		}else{
+		$router->create('privatedata_delete',
+				  '/privatedata/deleteattribute/{app}/{key}.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$app = addslashes(strip_tags($parameters['app']));
+					$key = addslashes(strip_tags($parameters['key']));
+					OC_OCS::privateDataDelete($format, $app, $key);
+					})
+			->requirements(array('format'=>'xml|json'));
+
+		// CLOUD
+		// systemWebApps 
+		$router->create('system_webapps',
+				  '/cloud/system/webapps.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					OC_OCS::systemwebapps($format);
+					})
+			->requirements(array('format'=>'xml|json'));
+
+		// quotaget 
+		$router->create('quota_get',
+				  '/cloud/user/{user}.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					OC_OCS::quotaGet($format, $user);
+					})
+			->requirements(array('format'=>'xml|json'));
+		// quotaset 
+		$router->create('quota_set',
+				  '/cloud/user/{user}.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					$quota = self::readData('post', 'quota', 'int');
+					OC_OCS::quotaSet($format, $user, $quota);
+					})
+			->requirements(array('format'=>'xml|json'));
+
+		// keygetpublic 
+		$router->create('keygetpublic',
+				  '/cloud/user/{user}/publickey.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					OC_OCS::publicKeyGet($format,$user);
+					})
+			->requirements(array('format'=>'xml|json'));
+
+		// keygetprivate 
+		$router->create('keygetpublic',
+				  '/cloud/user/{user}/privatekey.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					OC_OCS::privateKeyGet($format,$user);
+					})
+			->requirements(array('format'=>'xml|json'));
+
+
+// add more calls here
+// please document all the call in the draft spec
+// http://www.freedesktop.org/wiki/Specifications/open-collaboration-services-1.7#CLOUD
+
+// TODO:
+// users
+// groups
+// bookmarks
+// sharing
+// versioning
+// news (rss)
+		try {
+			$router->match($_SERVER['PATH_INFO']);
+		} catch (ResourceNotFoundException $e) {
 			$txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
 			$txt.=OC_OCS::getdebugoutput();
 			echo(OC_OCS::generatexml($format,'failed',999,$txt));
@@ -198,25 +279,25 @@ class OC_OCS {
 		if(isset($_SERVER['PHP_AUTH_PW']))   $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw='';
 
 		if(empty($authuser)) {
-		if($forceuser){
-			header('WWW-Authenticate: Basic realm="your valid user account or api key"');
-			header('HTTP/1.0 401 Unauthorized');
-			exit;
-		}else{
-			$identifieduser='';
-		}
-		}else{
-		if(!OC_User::login($authuser,$authpw)){
 			if($forceuser){
-			header('WWW-Authenticate: Basic realm="your valid user account or api key"');
-			header('HTTP/1.0 401 Unauthorized');
-			exit;
+				header('WWW-Authenticate: Basic realm="your valid user account or api key"');
+				header('HTTP/1.0 401 Unauthorized');
+				exit;
 			}else{
-			$identifieduser='';
+				$identifieduser='';
 			}
 		}else{
-			$identifieduser=$authuser;
-		}
+			if(!OC_User::login($authuser,$authpw)){
+				if($forceuser){
+					header('WWW-Authenticate: Basic realm="your valid user account or api key"');
+					header('HTTP/1.0 401 Unauthorized');
+					exit;
+				}else{
+					$identifieduser='';
+				}
+			}else{
+				$identifieduser=$authuser;
+			}
 		}
 
 		return($identifieduser);
@@ -350,11 +431,12 @@ class OC_OCS {
 	* @param string $format
 	* @return string xml/json
 	*/
-	private static function apiConfig($format) {
+	public static function apiConfig($parameters) {
+		$format = $parameters['format'];
 		$user=OC_OCS::checkpassword(false);
 		$url=substr(OCP\Util::getServerHost().$_SERVER['SCRIPT_NAME'],0,-11).'';
 
-		$xml['version']='1.5';
+		$xml['version']='1.7';
 		$xml['website']='ownCloud';
 		$xml['host']=OCP\Util::getServerHost();
 		$xml['contact']='';
@@ -524,4 +606,134 @@ class OC_OCS {
 	public static function deleteData($user, $app, $key) {
 		return OC_Preferences::deleteKey($user,$app,$key);
 	}
+
+
+        // CLOUD API #############################################
+
+        /**
+        * get a list of installed web apps
+        * @param string $format
+        * @return string xml/json
+        */
+        private static function systemWebApps($format) {
+                $login=OC_OCS::checkpassword();
+		$apps=OC_App::getEnabledApps();
+		$values=array();
+		foreach($apps as $app) {
+			$info=OC_App::getAppInfo($app);
+			if(isset($info['standalone'])) {
+				$newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>'');
+				$values[]=$newvalue;
+			}
+
+		}
+		$txt=OC_OCS::generatexml($format, 'ok', 100, '', $values, 'cloud', '', 2, 0, 0);
+		echo($txt);
+
+        }
+
+
+        /**
+        * get the quota of a user
+        * @param string $format
+        * @param string $user
+        * @return string xml/json
+        */
+        private static function quotaGet($format,$user) {
+                $login=OC_OCS::checkpassword();
+		if(OC_Group::inGroup($login, 'admin') or ($login==$user)) {
+
+			if(OC_User::userExists($user)){
+				// calculate the disc space
+				$user_dir = '/'.$user.'/files';
+				OC_Filesystem::init($user_dir);
+				$rootInfo=OC_FileCache::get('');
+				$sharedInfo=OC_FileCache::get('/Shared');
+				$used=$rootInfo['size']-$sharedInfo['size'];
+				$free=OC_Filesystem::free_space();
+				$total=$free+$used;
+				if($total==0) $total=1;  // prevent division by zero
+				$relative=round(($used/$total)*10000)/100;
+
+				$xml=array();
+				$xml['quota']=$total;
+				$xml['free']=$free;
+				$xml['used']=$used;
+				$xml['relative']=$relative;
+
+				$txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', '', 1, 0, 0);
+				echo($txt);
+			}else{
+				echo self::generateXml('', 'fail', 300, 'User does not exist');
+			}
+		}else{
+			echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.');
+		}
+        }
+
+        /**
+        * set the quota of a user
+        * @param string $format
+        * @param string $user
+        * @param string $quota
+        * @return string xml/json
+        */
+        private static function quotaSet($format,$user,$quota) {
+                $login=OC_OCS::checkpassword();
+                if(OC_Group::inGroup($login, 'admin')) {
+
+			// todo
+			// not yet implemented
+			// add logic here
+			error_log('OCS call: user:'.$user.' quota:'.$quota);
+
+                        $xml=array();
+                        $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', '', 1, 0, 0);
+                        echo($txt);
+                }else{
+                        echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.');
+                }
+        }
+
+        /**
+        * get the public key of a user
+        * @param string $format
+        * @param string $user
+        * @return string xml/json
+        */
+        private static function publicKeyGet($format,$user) {
+                $login=OC_OCS::checkpassword();
+
+		if(OC_User::userExists($user)){
+			// calculate the disc space
+			$txt='this is the public key of '.$user;
+			echo($txt);
+		}else{
+			echo self::generateXml('', 'fail', 300, 'User does not exist');
+		}
+	}
+
+        /**
+        * get the private key of a user
+        * @param string $format
+        * @param string $user
+        * @return string xml/json
+        */
+        private static function privateKeyGet($format,$user) {
+                $login=OC_OCS::checkpassword();
+                if(OC_Group::inGroup($login, 'admin') or ($login==$user)) {
+
+                        if(OC_User::userExists($user)){
+                                // calculate the disc space
+                                $txt='this is the private key of '.$user;
+                                echo($txt);
+                        }else{
+                                echo self::generateXml('', 'fail', 300, 'User does not exist');
+                        }
+                }else{
+                        echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.');
+                }
+        }
+
+
 }
diff --git a/lib/ocsclient.php b/lib/ocsclient.php
index 951d761d7e67547dea434db5fcd5a6a638bfcf62..ae35470cff6f24d1870a4b17994c24f1f49b1ea8 100644
--- a/lib/ocsclient.php
+++ b/lib/ocsclient.php
@@ -71,7 +71,7 @@ class OC_OCSClient{
 		$tmp=$data->data;
 		$cats=array();
 
-		foreach($tmp->category as $key=>$value) {
+		foreach($tmp->category as $value) {
 
 			$id= (int) $value->id;
 			$name= (string) $value->name;
diff --git a/lib/preferences.php b/lib/preferences.php
index f72378ce94f6c95280c81fe36851fb2f54b8d438..c91423e69bcd767abeaf0a20ecf730ad7264c89e 100644
--- a/lib/preferences.php
+++ b/lib/preferences.php
@@ -165,7 +165,7 @@ class OC_Preferences{
 	public static function deleteKey( $user, $app, $key ){
 		// No need for more comments
 		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE userid = ? AND appid = ? AND configkey = ?' );
-		$result = $query->execute( array( $user, $app, $key ));
+		$query->execute( array( $user, $app, $key ));
 
 		return true;
 	}
@@ -181,7 +181,7 @@ class OC_Preferences{
 	public static function deleteApp( $user, $app ){
 		// No need for more comments
 		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE userid = ? AND appid = ?' );
-		$result = $query->execute( array( $user, $app ));
+		$query->execute( array( $user, $app ));
 
 		return true;
 	}
@@ -196,7 +196,7 @@ class OC_Preferences{
 	public static function deleteUser( $user ){
 		// No need for more comments
 		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE userid = ?' );
-		$result = $query->execute( array( $user ));
+		$query->execute( array( $user ));
 
 		return true;
 	}
@@ -211,7 +211,7 @@ class OC_Preferences{
 	public static function deleteAppFromAllUsers( $app ){
 		// No need for more comments
 		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE appid = ?' );
-		$result = $query->execute( array( $app ));
+		$query->execute( array( $app ));
 
 		return true;
 	}
diff --git a/lib/public/app.php b/lib/public/app.php
index 9e2108818bfdfcf3a664537f0ec77aff660398fc..28411933beb5e6b682814eb00726be6cc5b1ebfe 100644
--- a/lib/public/app.php
+++ b/lib/public/app.php
@@ -34,28 +34,6 @@ namespace OCP;
  * This class provides functions to manage apps in ownCloud
  */
 class App {
-
-        /**
-         * @brief Makes owncloud aware of this app
-         * @brief This call is deprecated and not necessary to use.
-         * @param $data array with all information
-         * @returns true/false
-         *
-         * This function registers the application. $data is an associative array.
-         * The following keys are required:
-         *   - id: id of the application, has to be unique ('addressbook')
-         *   - name: Human readable name ('Addressbook')
-         *   - version: array with Version (major, minor, bugfix) ( array(1, 0, 2))
-         *
-         * The following keys are optional:
-         *   - order: integer, that influences the position of your application in
-         *     a list of applications. Lower values come first.
-         *
-         */
-        public static function register( $data ){
-        }
-
-
         /**
          * @brief adds an entry to the navigation
          * @param $data array containing the data
@@ -158,6 +136,3 @@ class App {
 
 
 }
-
-
-?>
diff --git a/lib/public/config.php b/lib/public/config.php
index 9f5abe672cb64fc0ccc284b908574bbab4100572..ab01902ffe60e13d673ebfa8476711ad88a7e460 100644
--- a/lib/public/config.php
+++ b/lib/public/config.php
@@ -134,5 +134,3 @@ class Config {
 
 
 }
-
-?>
diff --git a/lib/public/db.php b/lib/public/db.php
index f7564c0bb6a7613bfd13e72571f481b6be72865f..3a33f7674d80f5d76fc616d168c97304c667fb57 100644
--- a/lib/public/db.php
+++ b/lib/public/db.php
@@ -91,5 +91,3 @@ class DB {
 
 
 }
-
-?>
diff --git a/lib/public/files.php b/lib/public/files.php
index fc3004434bae43c512bbe7ce47e92ccd02682dc2..32b3f03674484193044e45081d6ef0c10f8b946b 100644
--- a/lib/public/files.php
+++ b/lib/public/files.php
@@ -115,5 +115,3 @@ class Files {
 
 
 }
-
-?>
diff --git a/lib/public/groupinterface.php b/lib/public/groupinterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..9783302811805920a760a4c7dd45e27fd99c3414
--- /dev/null
+++ b/lib/public/groupinterface.php
@@ -0,0 +1,31 @@
+<?php
+/**
+* ownCloud
+*
+* @author Arthur Schiwon
+* @copyright 2012 Arthur Schiwon blizzz@owncloud.org
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+/**
+ * Public interface of ownCloud for apps to use.
+ * Group Class.
+ *
+ */
+
+namespace OCP;
+
+interface GroupInterface extends \OC_Group_Interface {}
\ No newline at end of file
diff --git a/lib/public/json.php b/lib/public/json.php
index b6edbd65bd5a07aa8f78ffa526f9f951f3ba3331..99df79173eb85dbff1d75660fd2bd7c510465d7c 100644
--- a/lib/public/json.php
+++ b/lib/public/json.php
@@ -35,75 +35,140 @@ namespace OCP;
  */
 class JSON {
 
-
 	/**
 	* @brief Encode and print $data in JSON format
 	* @param array $data The data to use
 	* @param string $setContentType the optional content type
+	* @return string json formatted string.
 	*/
 	public static function encodedPrint( $data, $setContentType=true ){
 		return(\OC_JSON::encodedPrint( $data, $setContentType ));
 	}
 
-
 	/**
-	* @brief Check if the user is logged in, send json error msg if not
+	* Check if the user is logged in, send json error msg if not.
+	* 
+	* This method checks if a user is logged in. If not, a json error
+	* response will be return and the method will exit from execution
+	* of the script.
+	* The returned json will be in the format:
+	* 
+	*     {"status":"error","data":{"message":"Authentication error."}}
+	* 
+	* Add this call to the start of all ajax method files that requires
+	* an authenticated user.
+	* 
+	* @return string json formatted error string if not authenticated.
 	*/
 	public static function checkLoggedIn(){
 		return(\OC_JSON::checkLoggedIn());
 	}
 
 	/**
-	 * @brief Check an ajax get/post call if the request token is valid.
-	 * @return json Error msg if not valid.
-	 */
+	* Check an ajax get/post call if the request token is valid.
+	* 
+	* This method checks for a valid variable 'requesttoken' in $_GET,
+	* $_POST and $_SERVER. If a valid token is not found, a json error
+	* response will be return and the method will exit from execution
+	* of the script.
+	* The returned json will be in the format:
+	* 
+	*     {"status":"error","data":{"message":"Token expired. Please reload page."}}
+	* 
+	* Add this call to the start of all ajax method files that creates, 
+	* updates or deletes anything.
+	* In cases where you e.g. use an ajax call to load a dialog containing
+	* a submittable form, you will need to add the requesttoken first as a
+	* parameter to the ajax call, then assign it to the template and finally
+	* add a hidden input field also named 'requesttoken' containing the value.
+	* 
+	* @return string json formatted error string if not valid.
+	*/
 	public static function callCheck(){
 		return(\OC_JSON::callCheck());
 	}
 
 	/**
-	* @brief Send json success msg
+	* Send json success msg
+	* 
+	* Return a json success message with optional extra data.
+	* @see OCP\JSON::error() 		for the format to use.
+	* 
 	* @param array $data The data to use
+	* @return string json formatted string.
 	*/
 	public static function success( $data = array() ){
 		return(\OC_JSON::success( $data ));
 	}
 
-
 	/**
-	* @brief Send json error msg
+	* Send json error msg
+	* 
+	* Return a json error message with optional extra data for 
+	* error message or app specific data.
+	* 
+	* Example use:
+	* 
+	*     $id = [some value]
+	*     OCP\JSON::error(array('data':array('message':'An error happened', 'id': $id)));
+	* 
+	* Will return the json formatted string:
+	* 
+	*     {"status":"error","data":{"message":"An error happened", "id":[some value]}}
+	* 
 	* @param array $data The data to use
+	* @return string json formatted error string.
 	*/
 	public static function error( $data = array() ){
 		return(\OC_JSON::error( $data ));
 	}
 
-
 	/**
-	 * @brief set Content-Type header to jsonrequest
-	 * @param array $type The contwnt type header
-	 */
+	* @brief set Content-Type header to jsonrequest
+	* @param array $type The contwnt type header
+	* @return string json formatted string.
+	*/
 	public static function setContentTypeHeader( $type='application/json' ){
 		return(\OC_JSON::setContentTypeHeader( $type ));
 	}
 
-
 	/**
-	 * @brief Check if the App is enabled and send JSON error message instead
-	 * @param string $app The app to check
-	 */
+	* Check if the App is enabled and send JSON error message instead
+	* 
+	* This method checks if a specific app is enabled. If not, a json error
+	* response will be return and the method will exit from execution
+	* of the script.
+	* The returned json will be in the format:
+	* 
+	*     {"status":"error","data":{"message":"Application is not enabled."}}
+	* 
+	* Add this call to the start of all ajax method files that requires
+	* a specific app to be enabled.
+	* 
+	* @param string $app The app to check
+	* @return string json formatted string if not enabled.
+	*/
 	public static function checkAppEnabled( $app ){
 		return(\OC_JSON::checkAppEnabled( $app ));
 	}
 
-
 	/**
-	* @brief Check if the user is a admin, send json error msg if not
+	* Check if the user is a admin, send json error msg if not
+	* 
+	* This method checks if the current user has admin rights. If not, a json error
+	* response will be return and the method will exit from execution
+	* of the script.
+	* The returned json will be in the format:
+	* 
+	*     {"status":"error","data":{"message":"Authentication error."}}
+	* 
+	* Add this call to the start of all ajax method files that requires
+	* administrative rights.
+	* 
+	* @return string json formatted string if not admin user.
 	*/
 	public static function checkAdminUser(){
 		return(\OC_JSON::checkAdminUser());
 	}
 
 }
-
-?>
diff --git a/lib/public/response.php b/lib/public/response.php
index cc2137c5caefd5b067c699533a30a5d33bdf1656..8dff3bcd3541eae97dada5703315eddc24511ad9 100644
--- a/lib/public/response.php
+++ b/lib/public/response.php
@@ -105,5 +105,3 @@ class Response {
 
 
 }
-
-?>
diff --git a/lib/public/template.php b/lib/public/template.php
index b89088bdd0645316c236716f27a67a2f8ab6522f..a0ed618cb2c5f16f4a87e872170c36f7ae556d69 100644
--- a/lib/public/template.php
+++ b/lib/public/template.php
@@ -104,6 +104,3 @@ function html_select_options($options, $selected, $params=array()) {
 class Template extends \OC_Template {
 
 }
-
-
-?>
diff --git a/lib/public/user.php b/lib/public/user.php
index a0c069f73795c7ee15ca4e467b0aa2e68b7a3738..713e366b9680d86cd8087f361c71e148470cf499 100644
--- a/lib/public/user.php
+++ b/lib/public/user.php
@@ -120,6 +120,3 @@ class User {
 
 
 }
-
-
-?>
diff --git a/lib/public/userinterface.php b/lib/public/userinterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..b73a8f8d8b07474d2a33ec196ac33d1ccf3ba6cd
--- /dev/null
+++ b/lib/public/userinterface.php
@@ -0,0 +1,31 @@
+<?php
+/**
+* ownCloud
+*
+* @author Arthur Schiwon
+* @copyright 2012 Arthur Schiwon blizzz@owncloud.org
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+/**
+ * Public interface of ownCloud for apps to use.
+ * User Class.
+ *
+ */
+
+namespace OCP;
+
+interface UserInterface extends \OC_User_Interface {}
\ No newline at end of file
diff --git a/lib/public/util.php b/lib/public/util.php
index 41121091544abafecacbedfd9b058de98e00e7db..75ca29f7129d70817209b73ca318ae937cce62f2 100644
--- a/lib/public/util.php
+++ b/lib/public/util.php
@@ -320,6 +320,15 @@ class Util {
 	public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
 		return(\OC_Helper::mb_str_replace($search, $replace, $subject, $encoding, $count));
 	}
-}
 
-?>
+	/**
+	* @brief performs a search in a nested array
+	* @param haystack the array to be searched
+	* @param needle the search string
+	* @param $index optional, only search this key name
+	* @return the key of the matching field, otherwise false
+	*/
+	public static function recursiveArraySearch($haystack, $needle, $index = null) {
+		return(\OC_Helper::recursiveArraySearch($haystack, $needle, $index));
+	}
+}
diff --git a/lib/route.php b/lib/route.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d3339add6cfd3500bfe05de5d8cc15613778aee
--- /dev/null
+++ b/lib/route.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use Symfony\Component\Routing\Route;
+
+class OC_Route extends Route {
+	public function method($method) {
+		$this->setRequirement('_method', $method);
+		return $this;
+	}
+
+	public function post() {
+		$this->method('post');
+		return $this;
+	}
+
+	public function get() {
+		$this->method('get');
+		return $this;
+	}
+
+	public function put() {
+		$this->method('put');
+		return $this;
+	}
+
+	public function delete() {
+		$this->method('delete');
+		return $this;
+	}
+
+	public function defaults($defaults) {
+		$action = $this->getDefault('action');
+		$this->setDefaults($defaults);
+		if (isset($defaults['action'])) {
+			$action = $defaults['action'];
+		}
+		$this->action($action);
+		return $this;
+	}
+
+	public function requirements($requirements) {
+		$method = $this->getRequirement('_method');
+		$this->setRequirements($requirements);
+		if (isset($requirements['_method'])) {
+			$method = $requirements['_method'];
+		}
+		$this->method($method);
+		return $this;
+	}
+
+	public function action($class, $function = null) {
+		$action = array($class, $function);
+		if (is_null($function)) {
+			$action = $class;
+		}
+		$this->setDefault('action', $action);
+		return $this;
+	}
+}
diff --git a/lib/router.php b/lib/router.php
new file mode 100644
index 0000000000000000000000000000000000000000..f037ecdfef455b1977af37dddc20bfa3361b7784
--- /dev/null
+++ b/lib/router.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+//use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+class OC_Router {
+	protected $collections = array();
+	protected $collection = null;
+
+	public function useCollection($name) {
+		if (!isset($this->collections[$name])) {
+			$this->collections[$name] = new RouteCollection();
+		}
+		$this->collection = $this->collections[$name];
+	}
+
+	public function create($name, $pattern, array $defaults = array(), array $requirements = array()) {
+		$route = new OC_Route($pattern, $defaults, $requirements);
+		$this->collection->add($name, $route);
+		return $route;
+	}
+
+    	public function match($url) {
+		$context = new RequestContext($_SERVER['REQUEST_URI'], $_SERVER['REQUEST_METHOD']);
+		$matcher = new UrlMatcher($this->collection, $context);
+		$parameters = $matcher->match($url);
+		if (isset($parameters['action'])) {
+			$action = $parameters['action'];
+			if (!is_callable($action)) {
+			var_dump($action);
+				throw new Exception('not a callable action');
+			}
+			unset($parameters['action']);
+			call_user_func($action, $parameters);
+		} elseif (isset($parameters['file'])) {
+			include ($parameters['file']);
+		} else {
+			throw new Exception('no action available');
+		}
+	}
+}
diff --git a/lib/search/provider.php b/lib/search/provider.php
index 838ab696d0420e6d44d777e8e4ed85cb8ac1b9c2..b3ee79b477091ea14a8720f23a59f10f082c83ae 100644
--- a/lib/search/provider.php
+++ b/lib/search/provider.php
@@ -2,13 +2,17 @@
 /**
  * provides search functionalty
  */
-class OC_Search_Provider {
-	public function __construct($options){}
+abstract class OC_Search_Provider {
+	private $options;
+	
+	public function __construct($options){
+		$this->options=$options;
+	}
 	
 	/**
 	 * search for $query
 	 * @param string $query
 	 * @return array An array of OC_Search_Result's
 	 */
-	public function search($query){}
+	abstract public function search($query);
 }
diff --git a/lib/setup.php b/lib/setup.php
index bad0f5301c79dad11e7ddcf71ff0eb4a8fc344e1..4d71bed86e2d799389b096c3dae99317d4113d2e 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -102,7 +102,6 @@ class OC_Setup {
 				}
 				else {
 					$oldUser=OC_Config::getValue('dbuser', false);
-					$oldPassword=OC_Config::getValue('dbpassword', false);
 					
 					$query="SELECT user FROM mysql.user WHERE user='$dbuser'"; //this should be enough to check for admin rights in mysql
 					if(mysql_query($query, $connection)) {
@@ -257,7 +256,7 @@ class OC_Setup {
 				OC_Installer::installShippedApps();
 
 				//create htaccess files for apache hosts
-				if (strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) {
+				if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) {
 					self::createHtaccess();
 				}
 
@@ -380,5 +379,3 @@ class OC_Setup {
 		file_put_contents(OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data').'/index.html', '');
 	}
 }
-
-?>
diff --git a/lib/streamwrappers.php b/lib/streamwrappers.php
index f1e0fa0e1d9cadffdca404212b1630ae87a80905..f502c6170bddaaf48d7f1f07a923103b88087a31 100644
--- a/lib/streamwrappers.php
+++ b/lib/streamwrappers.php
@@ -1,6 +1,4 @@
 <?php
-global $FAKEDIRS;
-$FAKEDIRS=array();
 
 class OC_FakeDirStream{
 	public static $dirs=array();
@@ -8,8 +6,6 @@ class OC_FakeDirStream{
 	private $index;
 
 	public function dir_opendir($path,$options){
-		global $FAKEDIRS;
-		$url=parse_url($path);
 		$this->name=substr($path,strlen('fakedir://'));
 		$this->index=0;
 		if(!isset(self::$dirs[$this->name])){
@@ -161,7 +157,6 @@ class OC_StaticStreamWrapper {
 	public function stream_write($data) {
 		if (!$this->writable) return 0;
 		$size = strlen($data);
-		$len = strlen(self::$data[$this->path]);
 		if ($this->stream_eof()) {
 			self::$data[$this->path] .= $data;
 		} else {
diff --git a/lib/template.php b/lib/template.php
index 3b48c27b9b4c8a406bcedeed75e6ae959fd13ac3..5b6999af5330ac7a19492390f7bb417f6eae8a28 100644
--- a/lib/template.php
+++ b/lib/template.php
@@ -82,7 +82,6 @@ function relative_modified_date($timestamp) {
 	$diffhours = round($diffminutes/60);
 	$diffdays = round($diffhours/24);
 	$diffmonths = round($diffdays/31);
-	$diffyears = round($diffdays/365);
 
 	if($timediff < 60) { return $l->t('seconds ago'); }
 	else if($timediff < 120) { return $l->t('1 minute ago'); }
diff --git a/lib/templatelayout.php b/lib/templatelayout.php
index d33a87e9e4c645270978135cf0375c9c3667e46b..588a784599724e61aab2ad0235819c2002305a2c 100644
--- a/lib/templatelayout.php
+++ b/lib/templatelayout.php
@@ -123,7 +123,7 @@ class OC_TemplateLayout extends OC_Template {
 					elseif(self::appendIfExist($files, $apps_dir['path'], $apps_dir['url'], "$style.css")) { $append =true; break; }
 				}
 				if(! $append) {
-					echo('css file not found: style:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);
+					echo('css file not found: style:'.$style.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);
 					die();
 				}
 			}
diff --git a/lib/updater.php b/lib/updater.php
index 5d97178c30e73e9e5be28b57e73b0463fa7944d8..332cea03bfc013809b6a1161b865e506e9fcdd82 100644
--- a/lib/updater.php
+++ b/lib/updater.php
@@ -84,4 +84,3 @@ class OC_Updater{
 
 	}
 }
-?>
diff --git a/lib/user.php b/lib/user.php
index d02c1208a8de967841118a52167f2009db1c3e5d..e3c9c23effa1d566972e8deeaf4e41e1a7199b68 100644
--- a/lib/user.php
+++ b/lib/user.php
@@ -21,7 +21,7 @@
  */
 
 /**
- * This class provides wrapper methods for user management. Multiple backends are 
+ * This class provides wrapper methods for user management. Multiple backends are
  * supported. User management operations are delegated to the configured backend for
  * execution.
  *
@@ -50,8 +50,8 @@ class OC_User {
 	 *
 	 * Makes a list of backends that can be used by other modules
 	 */
-	public static function registerBackend( $name ){
-		self::$_backends[] = $name;
+	public static function registerBackend( $backend ){
+		self::$_backends[] = $backend;
 		return true;
 	}
 
@@ -83,27 +83,37 @@ class OC_User {
 	 * Set the User Authentication Module
 	 */
 	public static function useBackend( $backend = 'database' ){
-		// You'll never know what happens
-		if( null === $backend OR !is_string( $backend )){
-			$backend = 'database';
-		}
+		if($backend instanceof OC_User_Interface){
+			self::$_usedBackends[get_class($backend)]=$backend;
+		}else{
+			// You'll never know what happens
+			if( null === $backend OR !is_string( $backend )){
+				$backend = 'database';
+			}
 
-		// Load backend
-		switch( $backend ){
-			case 'database':
-			case 'mysql':
-			case 'sqlite':
-				self::$_usedBackends[$backend] = new OC_User_Database();
-				break;
-			default:
-				$className = 'OC_USER_' . strToUpper($backend);
-				self::$_usedBackends[$backend] = new $className();
-				break;
+			// Load backend
+			switch( $backend ){
+				case 'database':
+				case 'mysql':
+				case 'sqlite':
+					self::$_usedBackends[$backend] = new OC_User_Database();
+					break;
+				default:
+					$className = 'OC_USER_' . strToUpper($backend);
+					self::$_usedBackends[$backend] = new $className();
+					break;
+			}
 		}
-
 		true;
 	}
 
+	/**
+	 * remove all used backends
+	 */
+	public static function clearBackends(){
+		self::$_usedBackends=array();
+	}
+
 	/**
 	 * @brief Create a new user
 	 * @param $uid The username of the user to create
@@ -200,7 +210,7 @@ class OC_User {
 		if( $run ){
 			$uid=self::checkPassword( $uid, $password );
 			if($uid){
-				session_regenerate_id();
+				session_regenerate_id(true);
 				self::setUserId($uid);
 				OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password ));
 				return true;
@@ -345,17 +355,13 @@ class OC_User {
 	 * @return boolean
 	 */
 	public static function userExists($uid){
-		static $user_exists_checked = null;
-		if (!is_null($user_exists_checked)) {
-			return $user_exists_checked;
-		}
 		foreach(self::$_usedBackends as $backend){
 			$result=$backend->userExists($uid);
 			if($result===true){
-				return $user_exists_checked = true;
+				return true;
 			}
 		}
-		return $user_exists_checked = false;
+		return false;
 	}
 
 	/**
diff --git a/lib/user/backend.php b/lib/user/backend.php
index be068a63ce093caa39afd2fd25c4d568cce6f3c9..daa942d261cfee852eddb24b1f7e6f661e7f4ec7 100644
--- a/lib/user/backend.php
+++ b/lib/user/backend.php
@@ -42,7 +42,7 @@ define('OC_USER_BACKEND_CHECK_PASSWORD',    0x000100);
  *
  * Subclass this for your own backends, and see OC_User_Example for descriptions
  */
-abstract class OC_User_Backend {
+abstract class OC_User_Backend implements OC_User_Interface {
 
 	protected $possibleActions = array(
 		OC_USER_BACKEND_CREATE_USER => 'createUser',
diff --git a/lib/user/database.php b/lib/user/database.php
index a48b8357d6427b3b89a03442471fd26c68ed54ae..cc27b3ddbfd311e94b798b54bdd8795ac5fc6740 100644
--- a/lib/user/database.php
+++ b/lib/user/database.php
@@ -39,7 +39,6 @@ require_once 'phpass/PasswordHash.php';
  * Class for user management in a SQL Database (e.g. MySQL, SQLite)
  */
 class OC_User_Database extends OC_User_Backend {
-	static private $userGroupCache=array();
 	/**
 	 * @var PasswordHash
 	 */
@@ -87,7 +86,7 @@ class OC_User_Database extends OC_User_Backend {
 	public function deleteUser( $uid ){
 		// Delete user-group-relation
 		$query = OC_DB::prepare( "DELETE FROM `*PREFIX*users` WHERE uid = ?" );
-		$result = $query->execute( array( $uid ));
+		$query->execute( array( $uid ));
 		return true;
 	}
 
@@ -104,11 +103,10 @@ class OC_User_Database extends OC_User_Backend {
 			$hasher=$this->getHasher();
 			$hash = $hasher->HashPassword($password.OC_Config::getValue('passwordsalt', ''));
 			$query = OC_DB::prepare( "UPDATE *PREFIX*users SET password = ? WHERE uid = ?" );
-			$result = $query->execute( array( $hash, $uid ));
+			$query->execute( array( $hash, $uid ));
 
 			return true;
-		}
-		else{
+		}else{
 			return false;
 		}
 	}
diff --git a/lib/user/example.php b/lib/user/example.php
index 7f3fd1b857832539545c600cae74758803944df8..77246d8136c4bb071e9f5d8298127e5588c7a35f 100644
--- a/lib/user/example.php
+++ b/lib/user/example.php
@@ -35,9 +35,7 @@ abstract class OC_User_Example extends OC_User_Backend {
 		* Creates a new user. Basic checking of username is done in OC_User
 		* itself, not in its subclasses.
 		*/
-	public function createUser($uid, $password){
-		return OC_USER_BACKEND_NOT_IMPLEMENTED;
-	}
+	abstract public function createUser($uid, $password);
 
 	/**
 		* @brief Set password
@@ -47,9 +45,7 @@ abstract class OC_User_Example extends OC_User_Backend {
 		*
 		* Change the password of a user
 		*/
-	public function setPassword($uid, $password){
-		return OC_USER_BACKEND_NOT_IMPLEMENTED;
-	}
+	abstract public function setPassword($uid, $password);
 
 	/**
 		* @brief Check if the password is correct
@@ -60,7 +56,5 @@ abstract class OC_User_Example extends OC_User_Backend {
 		* Check if the password is correct without logging in the user
 		* returns the user id or false
 		*/
-	public function checkPassword($uid, $password){
-		return OC_USER_BACKEND_NOT_IMPLEMENTED;
-	}
+	abstract public function checkPassword($uid, $password);
 }
diff --git a/lib/user/interface.php b/lib/user/interface.php
new file mode 100644
index 0000000000000000000000000000000000000000..dc3685dc20dcf23cf868b6f2b8190e3efc656cd3
--- /dev/null
+++ b/lib/user/interface.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * ownCloud - user interface
+ *
+ * @author Arthur Schiwon
+ * @copyright 2012 Arthur Schiwon blizzz@owncloud.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+interface OC_User_Interface {
+
+	/**
+	* @brief Check if backend implements actions
+	* @param $actions bitwise-or'ed actions
+	* @returns boolean
+	*
+	* Returns the supported actions as int to be
+	* compared with OC_USER_BACKEND_CREATE_USER etc.
+	*/
+	public function implementsActions($actions);
+
+	/**
+	* @brief delete a user
+	* @param $uid The username of the user to delete
+	* @returns true/false
+	*
+	* Deletes a user
+	*/
+	public function deleteUser($uid);
+
+	/**
+	* @brief Get a list of all users
+	* @returns array with all uids
+	*
+	* Get a list of all users.
+	*/
+	public function getUsers();
+
+	/**
+	* @brief check if a user exists
+	* @param string $uid the username
+	* @return boolean
+	*/
+	public function userExists($uid);
+
+}
\ No newline at end of file
diff --git a/lib/util.php b/lib/util.php
index 2a7b8a922f9dad7a23ade46c4e2e97b4bcd6fdd8..0c563278cc5e1cf84782e7aec21f47962bbddeb1 100755
--- a/lib/util.php
+++ b/lib/util.php
@@ -77,13 +77,13 @@ class OC_Util {
 		return '5 pre alpha';
 	}
 
-        /**
-         * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise".
-         * @return string
-         */
-        public static function getEditionString(){
-                return '';
-        }
+	/**
+	 * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise".
+	 * @return string
+	 */
+	public static function getEditionString(){
+			return '';
+	}
 
 	/**
 	 * add a javascript file
@@ -131,12 +131,12 @@ class OC_Util {
 		self::$headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text);
 	}
 
-   /**
-     * formats a timestamp in the "right" way
-     *
-     * @param int timestamp $timestamp
-     * @param bool dateOnly option to ommit time from the result
-     */
+	/**
+	 * formats a timestamp in the "right" way
+	 *
+	 * @param int timestamp $timestamp
+	 * @param bool dateOnly option to ommit time from the result
+	 */
     public static function formatDate( $timestamp,$dateOnly=false){
 		if(isset($_SESSION['timezone'])){//adjust to clients timezone if we know it
 			$systemTimeZone = intval(date('O'));
@@ -189,8 +189,6 @@ class OC_Util {
 		if(!(is_callable('sqlite_open') or class_exists('SQLite3')) and !is_callable('mysql_connect') and !is_callable('pg_connect')){
 			$errors[]=array('error'=>'No database drivers (sqlite, mysql, or postgresql) installed.<br/>','hint'=>'');//TODO: sane hint
 		}
-		$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
-		$CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" );
 
 		//common hint for all file permissons error messages
 		$permissionsHint="Permissions can usually be fixed by giving the webserver write access to the ownCloud directory";
@@ -440,26 +438,25 @@ class OC_Util {
 	}
 
 
-        /**
-         * Check if the htaccess file is working by creating a test file in the data directory and trying to access via http
-         */
-        public static function ishtaccessworking() {
-
+	/**
+	 * Check if the htaccess file is working by creating a test file in the data directory and trying to access via http
+	 */
+	public static function ishtaccessworking() {
 		// testdata
 		$filename='/htaccesstest.txt';
 		$testcontent='testcontent';
 
 		// creating a test file
-                $testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename;
-                $fp = @fopen($testfile, 'w');
-                @fwrite($fp, $testcontent);
-                @fclose($fp);
+		$testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename;
+		$fp = @fopen($testfile, 'w');
+		@fwrite($fp, $testcontent);
+		@fclose($fp);
 
 		// accessing the file via http
-                $url = OC_Helper::serverProtocol(). '://'  . OC_Helper::serverHost() . OC::$WEBROOT.'/data'.$filename;
-                $fp = @fopen($url, 'r');
-                $content=@fread($fp, 2048);
-                @fclose($fp);
+		$url = OC_Helper::serverProtocol(). '://'  . OC_Helper::serverHost() . OC::$WEBROOT.'/data'.$filename;
+		$fp = @fopen($url, 'r');
+		$content=@fread($fp, 2048);
+		@fclose($fp);
 
 		// cleanup
 		@unlink($testfile);
@@ -469,13 +466,7 @@ class OC_Util {
 			return(false);
 		}else{
 			return(true);
-
 		}
-
-        }
-
-
-
-
+	}
 
 }
diff --git a/lib/vcategories.php b/lib/vcategories.php
index 8157c3438680e1032f4c63fa2c0d7a6180727f73..d15b7b166eaee6e971c4027358e377f41a8c04da 100644
--- a/lib/vcategories.php
+++ b/lib/vcategories.php
@@ -131,8 +131,10 @@ class OC_VCategories {
 	*	}
 	* 	$categories->rescan($objects);
 	*/
-	public function rescan($objects, $sync=true) {
-		$this->categories = array();
+	public function rescan($objects, $sync=true, $reset=true) {
+		if($reset === true) {
+			$this->categories = array();
+		}
 		foreach($objects as $object) {
 			//OC_Log::write('core','OC_VCategories::rescan: '.substr($object, 0, 100).'(...)', OC_Log::DEBUG);
 			$vobject = OC_VObject::parse($object);
@@ -221,4 +223,3 @@ class OC_VCategories {
 	}
 
 }
-?>
diff --git a/ocs/providers.php b/ocs/providers.php
index adaa28db6cd7feab015713005fb21fe4d7a22031..cc6de32266bd6940bfb740ebe165b56685073175 100644
--- a/ocs/providers.php
+++ b/ocs/providers.php
@@ -35,11 +35,10 @@ echo('
  <termsofuse></termsofuse>
  <register></register>
  <services>
-   <activity ocsversion="1.5" />
+   <config ocsversion="1.7" />
+   <activity ocsversion="1.7" />
+   <cloud ocsversion="1.7" />
  </services>
 </provider>
 </providers>
 ');
-
-
-?>
diff --git a/ocs/v1.php b/ocs/v1.php
index 77dd75b9fc2ace2f08ee76280a8e7f53d70e318e..ab0dc80f4ba9faa38c772a723e67c136dd7a7a50 100644
--- a/ocs/v1.php
+++ b/ocs/v1.php
@@ -24,5 +24,3 @@
 require_once('../lib/base.php');
 @ob_clean();
 OC_OCS::handle();
-
-?>
diff --git a/public.php b/public.php
index 8846769f467bae9fedf231672add8c213d72f5d6..8383f36a246bf80198d5da8a3f3a1471465cbf59 100644
--- a/public.php
+++ b/public.php
@@ -10,6 +10,7 @@ if(is_null($file)){
 
 $parts=explode('/',$file,2);
 $app=$parts[0];
+
 OC_Util::checkAppEnabled($app);
 OC_App::loadApp($app);
 
diff --git a/remote.php b/remote.php
index bdce867aabaf0755396302c8d5ad345e75b30cbe..8461eea19ada063631ff64b7994aae570ce3d918 100644
--- a/remote.php
+++ b/remote.php
@@ -15,27 +15,27 @@ if (!$pos = strpos($path_info, '/', 1)) {
 	$pos = strlen($path_info);
 }
 $service=substr($path_info, 1, $pos-1);
+
 $file = OC_AppConfig::getValue('core', 'remote_' . $service);
-$file = preg_replace('/apps\//','', $file); //Todo Remove after Multiappdir migration
 
 if(is_null($file)){
 	OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
 	exit;
 }
 
-$file = ltrim ($file, '/');
+$file=ltrim($file,'/');
 
 $parts=explode('/', $file, 2);
 $app=$parts[0];
 switch ($app) {
+	case 'core':
+		$file =  OC::$SERVERROOT .'/'. $file;
+		break;
 	default:
 		OC_Util::checkAppEnabled($app);
 		OC_App::loadApp($app);
 		$file = OC_App::getAppPath($app) .'/'. $parts[1];
 		break;
-	case 'core':
-		$file =  OC::$SERVERROOT .'/'. $file;
-		break;
 }
 $baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
 require_once($file);
diff --git a/search/ajax/search.php b/search/ajax/search.php
index 326724d60c4f4787d55a49425c033c50bb9d9ca5..95ddfedf8ba7b044495177c5ff69c237a0e716e6 100644
--- a/search/ajax/search.php
+++ b/search/ajax/search.php
@@ -35,5 +35,3 @@ if($query){
 }else{
 	echo 'false';
 }
-
-?>
diff --git a/search/index.php b/search/index.php
index 518695c56d202d4dab7bc27f6ac1496d9578a63b..de55aec3999391239a3649531d9402f3143168ac 100644
--- a/search/index.php
+++ b/search/index.php
@@ -49,5 +49,3 @@ foreach($results as $result){
 $tmpl = new OC_Template( 'search', 'index', 'user' );
 $tmpl->assign('resultTypes',$resultTypes);
 $tmpl->printPage();
-
-?>
diff --git a/settings/ajax/changepassword.php b/settings/ajax/changepassword.php
index 860ea987871af323373ebff7a73e245e58655db9..4ba6813517bf6ca986ef2da0fdc4707a862e6a59 100644
--- a/settings/ajax/changepassword.php
+++ b/settings/ajax/changepassword.php
@@ -9,6 +9,8 @@ $oldPassword=isset($_POST["oldpassword"])?$_POST["oldpassword"]:'';
 
 // Check if we are a user
 OC_JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
 if( (!OC_Group::inGroup( OC_User::getUser(), 'admin' ) && ($username!=OC_User::getUser() || !OC_User::checkPassword($username,$oldPassword)))) {
 	OC_JSON::error( array( "data" => array( "message" => "Authentication error" )));
 	exit();
@@ -21,5 +23,3 @@ if( OC_User::setPassword( $username, $password )){
 else{
 	OC_JSON::error(array("data" => array( "message" => "Unable to change password" )));
 }
-
-?>
diff --git a/settings/ajax/creategroup.php b/settings/ajax/creategroup.php
index 57d82e7bd94599f16002385cf1de2f8bcc5f82b0..af8ad3dd8c08319fc9924d03796d766cbe0c0919 100644
--- a/settings/ajax/creategroup.php
+++ b/settings/ajax/creategroup.php
@@ -9,6 +9,8 @@ if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' ))
 	exit();
 }
 
+OCP\JSON::callCheck();
+
 $groupname = $_POST["groupname"];
 
 // Does the group exist?
@@ -24,5 +26,3 @@ if( OC_Group::createGroup( $groupname )){
 else{
 	OC_JSON::error(array("data" => array( "message" => "Unable to add group" )));
 }
-
-?>
diff --git a/settings/ajax/createuser.php b/settings/ajax/createuser.php
index 6714711bc876fbf5161343c6265859941a8b2d32..c56df4bc15ab8a29e56bf94dc687794b9d8af1f5 100644
--- a/settings/ajax/createuser.php
+++ b/settings/ajax/createuser.php
@@ -8,6 +8,7 @@ if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' ))
 	OC_JSON::error(array("data" => array( "message" => "Authentication error" )));
 	exit();
 }
+OCP\JSON::callCheck();
 
 $groups = array();
 if( isset( $_POST["groups"] )){
@@ -35,5 +36,3 @@ try {
 } catch (Exception $exception) {
 	OC_JSON::error(array("data" => array( "message" => $exception->getMessage())));
 }
-
-?>
diff --git a/settings/ajax/disableapp.php b/settings/ajax/disableapp.php
index 53e9be379e19eae172eff3b15215a60425060c66..cc00698870702faf8482361dbc52c83fd0f8f4c5 100644
--- a/settings/ajax/disableapp.php
+++ b/settings/ajax/disableapp.php
@@ -2,6 +2,7 @@
 // Init owncloud
 require_once('../../lib/base.php');
 OC_JSON::checkAdminUser();
+OCP\JSON::callCheck();
 OC_JSON::setContentTypeHeader();
 
 OC_App::disable($_POST['appid']);
diff --git a/settings/ajax/enableapp.php b/settings/ajax/enableapp.php
index cb116ebe4e84bf1a3e88431d684f12ec03ad8d55..bd53a50210cdffe5fe41173cbcfaf038d3290dab 100644
--- a/settings/ajax/enableapp.php
+++ b/settings/ajax/enableapp.php
@@ -3,6 +3,7 @@
 // Init owncloud
 require_once('../../lib/base.php');
 OC_JSON::checkAdminUser();
+OCP\JSON::callCheck();
 OC_JSON::setContentTypeHeader();
 
 if(OC_App::enable($_POST['appid'])){
diff --git a/settings/ajax/lostpassword.php b/settings/ajax/lostpassword.php
index c6df8551f5265ddac8e38e49e169d29f3cb78308..803a424854c27eeace7e4eaa852f293cc3b0543e 100644
--- a/settings/ajax/lostpassword.php
+++ b/settings/ajax/lostpassword.php
@@ -2,8 +2,8 @@
 
 // Init owncloud
 require_once('../../lib/base.php');
-
 OC_JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 $l=OC_L10N::get('core');
 
@@ -15,5 +15,3 @@ if( isset( $_POST['email'] ) && filter_var( $_POST['email'], FILTER_VALIDATE_EMA
 }else{
 	OC_JSON::error(array("data" => array( "message" => $l->t("Invalid email") )));
 }
-
-?>
diff --git a/settings/ajax/openid.php b/settings/ajax/openid.php
index 58d071255c205c0c10224e23b836399460bf654b..bf4ead06020b6c6eefdaf7c6152347821ab838ea 100644
--- a/settings/ajax/openid.php
+++ b/settings/ajax/openid.php
@@ -6,6 +6,7 @@ require_once('../../lib/base.php');
 $l=OC_L10N::get('settings');
 
 OC_JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 OC_JSON::checkAppEnabled('user_openid');
 
 // Get data
@@ -16,5 +17,3 @@ if( isset( $_POST['identity'] ) ){
 }else{
 	OC_JSON::error(array("data" => array( "message" => $l->t("Invalid request") )));
 }
-
-?>
diff --git a/settings/ajax/removegroup.php b/settings/ajax/removegroup.php
index 4d3647818949f28d323aeef433082a8918465b89..f8c2065956c8f677c4256badc34cbc73fce54f70 100644
--- a/settings/ajax/removegroup.php
+++ b/settings/ajax/removegroup.php
@@ -4,6 +4,7 @@
 require_once('../../lib/base.php');
 
 OC_JSON::checkAdminUser();
+OCP\JSON::callCheck();
 
 $name = $_POST["groupname"];
 
@@ -14,5 +15,3 @@ if( OC_Group::deleteGroup( $name )){
 else{
 	OC_JSON::error(array("data" => array( "message" => "Unable to delete group" )));
 }
-
-?>
diff --git a/settings/ajax/removeuser.php b/settings/ajax/removeuser.php
index 2c288997a1f2467e8db132d6d1855fe2da264e3f..230815217c34294ca03af2be9398f34755cc4b0d 100644
--- a/settings/ajax/removeuser.php
+++ b/settings/ajax/removeuser.php
@@ -4,6 +4,7 @@
 require_once('../../lib/base.php');
 
 OC_JSON::checkAdminUser();
+OCP\JSON::callCheck();
 
 $username = $_POST["username"];
 
@@ -14,5 +15,3 @@ if( OC_User::deleteUser( $username )){
 else{
 	OC_JSON::error(array("data" => array( "message" => "Unable to delete user" )));
 }
-
-?>
diff --git a/settings/ajax/setlanguage.php b/settings/ajax/setlanguage.php
index e3b00c3bc8073b980949fc8dafb925755bd54d34..54b103cd4fe4a7ce7cf5afb34ed2f0d413b4de23 100644
--- a/settings/ajax/setlanguage.php
+++ b/settings/ajax/setlanguage.php
@@ -6,6 +6,7 @@ require_once('../../lib/base.php');
 $l=OC_L10N::get('settings');
 
 OC_JSON::checkLoggedIn();
+OCP\JSON::callCheck();
 
 
 // Get data
@@ -21,5 +22,3 @@ if( isset( $_POST['lang'] ) ){
 }else{
 	OC_JSON::error(array("data" => array( "message" => $l->t("Invalid request") )));
 }
-
-?>
diff --git a/settings/ajax/setloglevel.php b/settings/ajax/setloglevel.php
index 298cbd64738fb1f0629c44af08eb20eed855c614..4b97ba2aa32fe08aa8508f33f9b58066abdd80a1 100644
--- a/settings/ajax/setloglevel.php
+++ b/settings/ajax/setloglevel.php
@@ -7,6 +7,7 @@
 
 require_once('../../lib/base.php');
 OC_Util::checkAdminUser();
+OCP\JSON::callCheck();
 
 OC_Config::setValue( 'loglevel', $_POST['level'] );
 
diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php
index f59017600ac5b991069a2b057ed63fcea14cb7e0..2b412c0f2fdc923b02fc01382b9bd60f3b839ed6 100644
--- a/settings/ajax/setquota.php
+++ b/settings/ajax/setquota.php
@@ -9,6 +9,7 @@
 require_once('../../lib/base.php');
 
 OC_JSON::checkAdminUser();
+OCP\JSON::callCheck();
 
 $username = isset($_POST["username"])?$_POST["username"]:'';
 
@@ -34,4 +35,3 @@ if($username){
 }
 OC_JSON::success(array("data" => array( "username" => $username ,'quota'=>$quota)));
 
-?>
diff --git a/settings/ajax/togglegroups.php b/settings/ajax/togglegroups.php
index 7773c1049c3eb3f24553492a59e53fa839c9141f..95338ed0267b06801b53d97c8f9b318f02b0427b 100644
--- a/settings/ajax/togglegroups.php
+++ b/settings/ajax/togglegroups.php
@@ -4,6 +4,7 @@
 require_once('../../lib/base.php');
 
 OC_JSON::checkAdminUser();
+OCP\JSON::callCheck();
 
 $success = true;
 $error = "add user to";
@@ -37,5 +38,3 @@ if( $success ){
 else{
 	OC_JSON::error(array("data" => array( "message" => "Unable to $error group $group" )));
 }
-
-?>
diff --git a/settings/apps.php b/settings/apps.php
index f4ae8b8d977ca17dec9bde96834ed182238c1f70..762395c031b04ffab9b7629e3bac98ec4a99dff9 100644
--- a/settings/apps.php
+++ b/settings/apps.php
@@ -97,5 +97,3 @@ $appid = (isset($_GET['appid'])?strip_tags($_GET['appid']):'');
 $tmpl->assign('appid',$appid);
 
 $tmpl->printPage();
-
-?>
diff --git a/settings/help.php b/settings/help.php
index f0abed8f558208e1100360c3545bc2cee54da080..b1dc1c5be771fa0c7e4e44a272a83754a8dcc0e6 100644
--- a/settings/help.php
+++ b/settings/help.php
@@ -25,5 +25,3 @@ $tmpl->assign( "kbe", $kbe );
 $tmpl->assign( "pagecount", $pagecount );
 $tmpl->assign( "page", $page );
 $tmpl->printPage();
-
-?>
diff --git a/settings/js/users.js b/settings/js/users.js
index dfa28e4c4cb39a9f40c2c4e002471f0ea5f1978c..63ad426ecf4a578accf725da8a115cba59de1ef5 100644
--- a/settings/js/users.js
+++ b/settings/js/users.js
@@ -47,25 +47,24 @@ UserList={
 		if( !UserList.deleteCanceled && UserList.deleteUid ){
 			
 			// Delete user via ajax
-			$.post(
-				OC.filePath('settings','ajax','removeuser.php'),
-				{username:UserList.deleteUid},
-				function(result){
-					
-					// Remove undo option, & remove user from table
-					boolOperationFinished( 
-						data, function(){
-							$('#notification').fadeOut();
-							$('tr').filterAttr( 'data-uid', username ).remove();
-							UserList.deleteCanceled=true;
-							UserList.deleteFiles=null;
-							if( ready ){
-								ready();
-							}
+			$.ajax({
+				type: 'POST',
+				url: OC.filePath('settings', 'ajax', 'removeuser.php'),
+				async: false,
+				data: { username: UserList.deleteUid },
+				success: function(result) {
+					if (result.status == 'success') {
+						// Remove undo option, & remove user from table
+						$('#notification').fadeOut();
+						$('tr').filterAttr('data-uid', UserList.deleteUid).remove();
+						UserList.deleteCanceled = true;
+						UserList.deleteFiles = null;
+						if (ready) {
+							ready();
 						}
-					);
+					}
 				}
-			);
+			});
  		}
 	}
 }
@@ -186,7 +185,7 @@ $(document).ready(function(){
 	})
 	
 	$('input.quota-other').live('change',function(){
-		var uid=$(this).parent().parent().data('uid');
+		var uid=$(this).parent().parent().parent().data('uid');
 		var quota=$(this).val();
 		var select=$(this).prev();
 		var other=$(this);
diff --git a/settings/personal.php b/settings/personal.php
index 26a9f601d9aa1dd45b9b3379d68999cdf0d0bbcb..d82db0d0e7e1adb40f8833ca870a14ac91fa0a52 100644
--- a/settings/personal.php
+++ b/settings/personal.php
@@ -17,7 +17,7 @@ OC_App::setActiveNavigationEntry( 'personal' );
 
 // calculate the disc space
 $rootInfo=OC_FileCache::get('');
-$sharedInfo=OC_FileCache::get('/Shared');
+$sharedInfo=OC_FileCache::get('/Shared');
 $used=$rootInfo['size']-$sharedInfo['size'];
 $free=OC_Filesystem::free_space();
 $total=$free+$used;
@@ -61,5 +61,3 @@ foreach($forms as $form){
 	$tmpl->append('forms',$form);
 }
 $tmpl->printPage();
-
-?>
diff --git a/settings/users.php b/settings/users.php
index 026a30a750c96eb605823e02106af832d4519484..c3259d2a3f1eb40577c25a6529169aa0070db389 100644
--- a/settings/users.php
+++ b/settings/users.php
@@ -48,6 +48,3 @@ $tmpl->assign( 'quota_preset', $quotaPreset);
 $tmpl->assign( 'default_quota', $defaultQuota);
 $tmpl->assign( 'share_notice', $shareNotice);
 $tmpl->printPage();
-
-?>
-
diff --git a/status.php b/status.php
index 2d31702ecb3f9725f361280c7088dfaa8c134dcb..f314a3a1c6f43c4ae387f1e265fc5a6a63f9864b 100644
--- a/status.php
+++ b/status.php
@@ -29,6 +29,3 @@ if(OC_Config::getValue('installed')==1) $installed='true'; else $installed='fals
 $values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString(),'edition'=>OC_Util::getEditionString());
 
 echo(json_encode($values));
-
-
-?>
diff --git a/tests/index.php b/tests/index.php
index 691bf2a5d45a1dfa195b314c0f9b255ffdfcac41..0be27ee3fd710d8fac3eb00df0b9200402273a65 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -26,39 +26,64 @@ require_once 'simpletest/mock_objects.php';
 require_once 'simpletest/collector.php';
 require_once 'simpletest/default_reporter.php';
 
+$testSuiteName="ownCloud Unit Test Suite";
+
+// prepare the reporter
+if(OC::$CLI){
+	$reporter=new TextReporter;
+	$test=isset($_SERVER['argv'][1])?$_SERVER['argv'][1]:false;
+	if($test=='xml'){
+		$reporter= new XmlReporter;
+		$test=false;
+
+		if(isset($_SERVER['argv'][2])){
+			$testSuiteName=$testSuiteName." (".$_SERVER['argv'][2].")";
+		}
+	}
+}else{
+	$reporter=new HtmlReporter;
+	$test=isset($_GET['test'])?$_GET['test']:false;
+}
+
+// test suite instance
+$testSuite=new TestSuite($testSuiteName);
+
 //load core test cases
-loadTests(dirname(__FILE__));
+loadTests(dirname(__FILE__), $testSuite, $test);
 
 //load app test cases
+
+//
+// TODO: define a list of apps to be enabled + enable them
+//
+
 $apps=OC_App::getEnabledApps();
 foreach($apps as $app){
 	if(is_dir(OC::$SERVERROOT.'/apps/'.$app.'/tests')){
-		loadTests(OC::$SERVERROOT.'/apps/'.$app.'/tests');
+		loadTests(OC::$SERVERROOT.'/apps/'.$app.'/tests', $testSuite, $test);
 	}
 }
 
-function loadTests($dir=''){
-	if(OC::$CLI){
-		$reporter='TextReporter';
-		$test=isset($_SERVER['argv'][1])?$_SERVER['argv'][1]:false;
-	}else{
-		$reporter='HtmlReporter';
-		$test=isset($_GET['test'])?$_GET['test']:false;
-	}
+// run the suite
+if($testSuite->getSize()>0){
+	$testSuite->run($reporter);
+}
+
+// helper below
+function loadTests($dir,$testSuite, $test){
 	if($dh=opendir($dir)){
 		while($name=readdir($dh)){
 			if($name[0]!='.'){//no hidden files, '.' or '..'
 				$file=$dir.'/'.$name;
 				if(is_dir($file)){
-					loadTests($file);
+					loadTests($file, $testSuite, $test);
 				}elseif(substr($file,-4)=='.php' and $file!=__FILE__){
 					$name=getTestName($file);
 					if($test===false or $test==$name or substr($name,0,strlen($test))==$test){
-						$testCase=new TestSuite($name);
-						$testCase->addFile($file);
-						if($testCase->getSize()>0){
-							$testCase->run(new $reporter());
-						}
+						$extractor = new SimpleFileLoader();
+						$loadedSuite=$extractor->load($file);
+						if ($loadedSuite->getSize() > 0)
+							$testSuite->add($loadedSuite);
 					}
 				}
 			}
diff --git a/tests/lib/cache.php b/tests/lib/cache.php
index bb5cfc6ee19897a703212d545fecff0b4ebd2582..511999be956835a3dd0af684138982715bcab4f5 100644
--- a/tests/lib/cache.php
+++ b/tests/lib/cache.php
@@ -42,6 +42,27 @@ abstract class Test_Cache extends UnitTestCase {
 		$this->assertNull($this->instance->get('not_set'),'Unset value not equal to null');
 
 		$this->assertTrue($this->instance->remove('value1'));
+		$this->assertFalse($this->instance->hasKey('value1'));
+	}
+
+	function testClear(){
+		$value='ipsum lorum';
+		$this->instance->set('1_value1',$value);
+		$this->instance->set('1_value2',$value);
+		$this->instance->set('2_value1',$value);
+		$this->instance->set('3_value1',$value);
+
+		$this->assertTrue($this->instance->clear('1_'));
+		$this->assertFalse($this->instance->hasKey('1_value1'));
+		$this->assertFalse($this->instance->hasKey('1_value2'));
+		$this->assertTrue($this->instance->hasKey('2_value1'));
+		$this->assertTrue($this->instance->hasKey('3_value1'));
+
+		$this->assertTrue($this->instance->clear());
+		$this->assertFalse($this->instance->hasKey('1_value1'));
+		$this->assertFalse($this->instance->hasKey('1_value2'));
+		$this->assertFalse($this->instance->hasKey('2_value1'));
+		$this->assertFalse($this->instance->hasKey('3_value1'));
 	}
 
 	function testTTL(){
diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php
index 54e60e6569ddf623500d748d697bd2d2ac0a3fbe..28778efb68cbf94608cd8e8424011885630cfc57 100644
--- a/tests/lib/cache/file.php
+++ b/tests/lib/cache/file.php
@@ -21,8 +21,10 @@
 */
 
 class Test_Cache_File extends Test_Cache {
+	private $user;
+	
 	function skip() {
-		$this->skipUnless(OC_User::isLoggedIn());
+		//$this->skipUnless(OC_User::isLoggedIn());
 	}
 	
 	public function setUp(){
@@ -30,17 +32,32 @@ class Test_Cache_File extends Test_Cache {
 		OC_FileProxy::clearProxies();
 		OC_Hook::clear('OC_Filesystem');
 		
-		//enable only the encryption hook
-		OC_FileProxy::register(new OC_FileProxy_Encryption());
+		//enable only the encryption hook if needed
+		if(OC_App::isEnabled('files_encryption')){
+			OC_FileProxy::register(new OC_FileProxy_Encryption());
+		}
 		
 		//set up temporary storage
 		OC_Filesystem::clearMounts();
 		OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/');
 
+		OC_User::clearBackends();
+		OC_User::useBackend(new OC_User_Dummy());
+		
+		//login
+		OC_User::createUser('test', 'test');
+		
+		$this->user=OC_User::getUser();
+		OC_User::setUserId('test');
+
 		//set up the users dir
 		$rootView=new OC_FilesystemView('');
-		$rootView->mkdir('/'.OC_User::getUser());
+		$rootView->mkdir('/test');
 		
 		$this->instance=new OC_Cache_File();
 	}
+
+	public function tearDown(){
+		OC_User::setUserId($this->user);
+	}
 }
diff --git a/tests/lib/user/database.php b/tests/lib/user/database.php
index b2fcce93c5bb89f43d4aab53396616233c74716f..f484ffa78f71cf58cf75f65c9dd688a11f0caefb 100644
--- a/tests/lib/user/database.php
+++ b/tests/lib/user/database.php
@@ -21,7 +21,6 @@
 */
 
 class Test_User_Database extends Test_User_Backend {
-	private $user=array();
 	/**
 	 * get a new unique user name
 	 * test cases can override this in order to clean up created user
diff --git a/webapps.php b/webapps.php
deleted file mode 100644
index b5fee9bf85ffd05428322522f5dc79bceb02f96f..0000000000000000000000000000000000000000
--- a/webapps.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
-* ownCloud status page. usefull if you want to check from the outside if an owncloud installation exists
-*
-* @author Frank Karlitschek
-* @copyright 2012 Frank Karlitschek frank@owncloud.org
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*
-*/
-
-$RUNTIME_NOAPPS = TRUE; //no apps, yet
-
-require_once('lib/base.php');
-
-
-//valid user account 
-if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser='';
-if(isset($_SERVER['PHP_AUTH_PW']))   $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw='';
-
-if(!OC_User::login($authuser,$authpw)){
-	header('WWW-Authenticate: Basic realm="your valid user account"');
-	header('HTTP/1.0 401 Unauthorized');
-	exit;
-}else{
-
-	$apps=OC_App::getEnabledApps();
-	$values=array();
-	foreach($apps as $app) {
-		$info=OC_App::getAppInfo($app);
-		if(isset($info['standalone'])) {
-			$newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>'');
-			$values[]=$newvalue;
-		}
-
-	}
-
-	echo(json_encode($values));
-	exit;
-
-
-}
-
-?>