diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index 6f2f61dc8a353d8b77598349b9f307082343f956..aa108f9840e15e3238b022ae73a1aaf7056db521 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -26,6 +26,9 @@ namespace OCA\user_ldap\lib;
 abstract class Access {
 	protected $connection;
 
+	//never ever check this var directly, always use getPagedSearchResultState
+	protected $pagedSearchedSuccessful;
+
 	public function setConnector(Connection &$connection) {
 		$this->connection = $connection;
 	}
@@ -441,12 +444,12 @@ abstract class Access {
 		return true;
 	}
 
-	public function fetchListOfUsers($filter, $attr) {
-		return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1));
+	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
+		return $this->fetchList($this->searchUsers($filter, $attr, $limit, $offset), (count($attr) > 1));
 	}
 
-	public function fetchListOfGroups($filter, $attr) {
-		return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1));
+	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
+		return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
 	}
 
 	private function fetchList($list, $manyAttributes) {
@@ -470,8 +473,8 @@ abstract class Access {
 	 *
 	 * Executes an LDAP search
 	 */
-	public function searchUsers($filter, $attr = null) {
-		return $this->search($filter, $this->connection->ldapBaseUsers, $attr);
+	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
+		return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
 	}
 
 	/**
@@ -482,8 +485,8 @@ abstract class Access {
 	 *
 	 * Executes an LDAP search
 	 */
-	public function searchGroups($filter, $attr = null) {
-		return $this->search($filter, $this->connection->ldapBaseGroups, $attr);
+	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
+		return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
 	}
 
 	/**
@@ -495,29 +498,73 @@ abstract class Access {
 	 *
 	 * Executes an LDAP search
 	 */
-	private function search($filter, $base, $attr = null) {
+	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
 		if(!is_null($attr) && !is_array($attr)) {
 			$attr = array(mb_strtolower($attr, 'UTF-8'));
 		}
 
-		// See if we have a resource
+		// See if we have a resource, in case not cancel with message
 		$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 {
+		if(!is_resource($link_resource)) {
 			// 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();
 		}
 
+		//TODO: lines 516:540 into a function of its own. $pagedSearchOK as return
+		//check wether paged query should be attempted
+		$pagedSearchOK = false;
+		if($this->connection->hasPagedResultSupport && !is_null($limit)) {
+			$offset = intval($offset); //can be null
+			//get the cookie from the search for the previous search, required by LDAP
+			$cookie = $this->getPagedResultCookie($filter, $limit, $offset);
+			if(empty($cookie) && ($offset > 0)) {
+				//no cookie known, although the offset is not 0. Maybe cache run out. We need to start all over *sigh* (btw, Dear Reader, did you need LDAP paged searching was designed by MSFT?)
+				$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
+				//a bit recursive, $offset of 0 is the exit
+				$this->search($filter, $base, $attr, $limit, $reOffset, true);
+				$cookie = $this->getPagedResultCookie($filter, $limit, $offset);
+				//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
+				//TODO: remember this, probably does not change in the next request...
+				if(empty($cookie)) {
+					$cookie = null;
+				}
+			}
+			if(!is_null($cookie)) {
+				$pagedSearchOK = ldap_control_paged_result($link_resource, $limit, false, $cookie);
+				\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
+			} else {
+				\OCP\Util::writeLog('user_ldap', 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset, \OCP\Util::DEBUG);
+			}
+		}
+
+		$sr = ldap_search($link_resource, $base, $filter, $attr);
+		$findings = ldap_get_entries($link_resource, $sr );
+		if($pagedSearchOK) {
+			\OCP\Util::writeLog('user_ldap', 'Paged search successful', \OCP\Util::INFO);
+			ldap_control_paged_result_response($link_resource, $sr, $cookie);
+			\OCP\Util::writeLog('user_ldap', 'Set paged search cookie '.$cookie, \OCP\Util::INFO);
+			$this->setPagedResultCookie($filter, $limit, $offset, $cookie);
+			//browsing through prior pages to get the cookie for the new one
+			if($skipHandling) {
+				return;
+			}
+			//if count is bigger, then the server does not support paged search. Instead, he did a normal search. We set a flag here, so the callee knows how to deal with it.
+			//TODO: Not used, just make a count on the returned values in the callee
+			if($findings['count'] <= $limit) {
+				$this->pagedSearchedSuccessful = true;
+			}
+		} else {
+			\OCP\Util::writeLog('user_ldap', 'Paged search failed :(', \OCP\Util::INFO);
+		}
+
+		// 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();
+		}
+
 		if(!is_null($attr)) {
 			$selection = array();
 			$multiarray = false;
@@ -557,6 +604,7 @@ abstract class Access {
 					}
 				}
 			}
+// 			die(var_dump($selection));
 			return $selection;
 		}
 		return $findings;
@@ -680,4 +728,51 @@ abstract class Access {
 		}
 		return $uuid;
 	}
+
+	/**
+	 * @brief get a cookie for the next LDAP paged search
+	 * @param $filter the search filter to identify the correct search
+	 * @param $limit the limit (or 'pageSize'), to identify the correct search well
+	 * @param $offset the offset for the new search to identify the correct search really good
+	 * @returns string containing the key or empty if none is cached
+	 */
+	private function getPagedResultCookie($filter, $limit, $offset) {
+		if($offset == 0) {
+			return '';
+		}
+		$offset -= $limit;
+		//we work with cache here
+		$cachekey = 'lc' . dechex(crc32($filter)) . '-' . $limit . '-' . $offset;
+		$cookie = $this->connection->getFromCache($cachekey);
+		if(is_null($cookie)) {
+			$cookie = '';
+		}
+		return $cookie;
+	}
+
+	/**
+	 * @brief set a cookie for LDAP paged search run
+	 * @param $filter the search filter to identify the correct search
+	 * @param $limit the limit (or 'pageSize'), to identify the correct search well
+	 * @param $offset the offset for the run search to identify the correct search really good
+	 * @param $cookie string containing the cookie returned by ldap_control_paged_result_response
+	 * @return void
+	 */
+	private function setPagedResultCookie($filter, $limit, $offset) {
+		if(!empty($cookie)) {
+			$cachekey = 'lc' . dechex(crc32($filter)) . '-' . $limit . '-' . $offset;
+			$cookie = $this->connection->writeToCache($cachekey, $cookie);
+		}
+	}
+
+	/**
+	 * @brief check wether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
+	 * @return true on success, null or false otherwise
+	 */
+	public function getPagedSearchResultState() {
+		$result = $this->pagedSearchedSuccessful;
+		$this->pagedSearchedSuccessful = null;
+		return $result;
+	}
+
 }
\ No newline at end of file
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
index bf65d9ad91c8870bc41cf7d0eafe24ce11de1e7d..a570b29b79312a3a5f40d2a7a4ae95787aacbf12 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/connection.php
@@ -56,15 +56,20 @@ class Connection {
 		'ldapUuidAttribute' => null,
 		'ldapOverrideUuidAttribute' => null,
 		'homeFolderNamingRule' => null,
+		'hasPagedResultSupport' => false,
 	);
 
 	public function __construct($configID = 'user_ldap') {
 		$this->configID = $configID;
 		$this->cache = \OC_Cache::getGlobalCache();
+		$this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result') && function_exists('ldap_control_paged_result_response'));
+		\OCP\Util::writeLog('user_ldap', 'PHP supports paged results? '.print_r($this->config['hasPagedResultSupport'], true), \OCP\Util::INFO);
 	}
 
 	public function __destruct() {
-		@ldap_unbind($this->ldapConnectionRes);
+		if(is_resource($this->ldapConnectionRes)) {
+			@ldap_unbind($this->ldapConnectionRes);
+		};
 	}
 
 	public function __get($name) {
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index e104c8d1764925cde9d016d4d024ecbeb02ded9d..e95bd24fbdd7e84a3e524cd48a23ecfdc7bd37d7 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -104,24 +104,38 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 	 * Get a list of all users.
 	 */
 	public function getUsers($search = '', $limit = 10, $offset = 0) {
-		$ldap_users = $this->connection->getFromCache('getUsers');
-		if(is_null($ldap_users)) {
-			$ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn'));
-			$ldap_users = $this->ownCloudUserNames($ldap_users);
-			$this->connection->writeToCache('getUsers', $ldap_users);
-		}
-		$this->userSearch = $search;
-		if(!empty($this->userSearch)) {
-			$ldap_users = array_filter($ldap_users, array($this, 'userMatchesFilter'));
+		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
+
+		//check if users are cached, if so return
+		$ldap_users = $this->connection->getFromCache($cachekey);
+		if(!is_null($ldap_users)) {
+			return $ldap_users;
 		}
-		if($limit == -1) {
-			$limit = null;
+
+		//prepare search filter
+		$search = empty($search) ? '*' : '*'.$search.'*';
+		$filter = $this->combineFilterWithAnd(array(
+			$this->connection->ldapUserFilter,
+			$this->connection->ldapGroupDisplayName.'='.$search
+		));
+
+		\OCP\Util::writeLog('user_ldap', 'getUsers: Get users filter '.$filter, \OCP\Util::DEBUG);
+		//do the search and translate results to owncloud names
+		$ldap_users = $this->fetchListOfUsers($filter, array($this->connection->ldapUserDisplayName, 'dn'), $limit, $offset);
+		$ldap_users = $this->ownCloudUserNames($ldap_users);
+
+		if(!$this->getPagedSearchResultState()) {
+			\OCP\Util::writeLog('user_ldap', 'getUsers: We got old-style results', \OCP\Util::DEBUG);
+			//if not supported, a 'normal' search has run automatically, we just need to get our slice of the cake. And we cache the general search, too
+			$this->connection->writeToCache('getUsers-'.$search, $ldap_users);
+			$ldap_users = array_slice($ldap_users, $offset, $limit);
+		} else {
+			//debug message only
+			\OCP\Util::writeLog('user_ldap', 'getUsers: We got paged results', \OCP\Util::DEBUG);
 		}
-		return array_slice($ldap_users, $offset, $limit);
-	}
 
-	public function userMatchesFilter($user) {
-		return (strripos($user, $this->userSearch) !== false);
+		$this->connection->writeToCache($cachekey, $ldap_users);
+		return $ldap_users;
 	}
 
 	/**
diff --git a/core/ajax/navigationdetect.php b/core/ajax/navigationdetect.php
new file mode 100644
index 0000000000000000000000000000000000000000..c7d0bd38dbc8597f53da0d5b0cfc1ff5a4c4d02f
--- /dev/null
+++ b/core/ajax/navigationdetect.php
@@ -0,0 +1,22 @@
+<?php
+
+$RUNTIME_NOAPPS = true;
+
+require_once '../../lib/base.php';
+
+OC_Util::checkAdminUser();
+OCP\JSON::callCheck();
+
+$app = $_GET['app'];
+
+//load the one app and see what it adds to the navigation
+OC_App::loadApp($app);
+
+$navigation = OC_App::getNavigation();
+
+$navIds = array();
+foreach ($navigation as $nav) {
+	$navIds[] = $nav['id'];
+}
+
+OCP\JSON::success(array('nav_ids' => array_values($navIds), 'nav_entries' => $navigation));
diff --git a/core/js/jquery.infieldlabel.js b/core/js/jquery.infieldlabel.js
deleted file mode 100644
index f6a67b66ce163faa59283e64c5e07ebe4ac52787..0000000000000000000000000000000000000000
--- a/core/js/jquery.infieldlabel.js
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * In-Field Label jQuery Plugin
- * http://fuelyourcoding.com/scripts/infield.html
- *
- * Copyright (c) 2009 Doug Neiner
- * Dual licensed under the MIT and GPL licenses.
- * Uses the same license as jQuery, see:
- * http://docs.jquery.com/License
- *
- * @version 0.1
- */
-(function($){
-	
-    $.InFieldLabels = function(label,field, options){
-        // To avoid scope issues, use 'base' instead of 'this'
-        // to reference this class from internal events and functions.
-        var base = this;
-        
-        // Access to jQuery and DOM versions of each element
-        base.$label = $(label);
-        base.label = label;
-
- 		base.$field = $(field);
-		base.field = field;
-        
-		base.$label.data("InFieldLabels", base);
-		base.showing = true;
-        
-        base.init = function(){
-			// Merge supplied options with default options
-            base.options = $.extend({},$.InFieldLabels.defaultOptions, options);
-
-			// Check if the field is already filled in
-			if(base.$field.val() != ""){
-				base.$label.hide();
-				base.showing = false;
-			};
-			
-			base.$field.focus(function(){
-				base.fadeOnFocus();
-			}).blur(function(){
-				base.checkForEmpty(true);
-			}).bind('keydown.infieldlabel',function(e){
-				// Use of a namespace (.infieldlabel) allows us to
-				// unbind just this method later
-				base.hideOnChange(e);
-			}).change(function(e){
-				base.checkForEmpty();
-			}).bind('onPropertyChange', function(){
-				base.checkForEmpty();
-			});
-        };
-
-		// If the label is currently showing
-		// then fade it down to the amount
-		// specified in the settings
-		base.fadeOnFocus = function(){
-			if(base.showing){
-				base.setOpacity(base.options.fadeOpacity);
-			};
-		};
-		
-		base.setOpacity = function(opacity){
-			base.$label.stop().animate({ opacity: opacity }, base.options.fadeDuration);
-			base.showing = (opacity > 0.0);
-		};
-		
-		// Checks for empty as a fail safe
-		// set blur to true when passing from
-		// the blur event
-		base.checkForEmpty = function(blur){
-			if(base.$field.val() == ""){
-				base.prepForShow();
-				base.setOpacity( blur ? 1.0 : base.options.fadeOpacity );
-			} else {
-				base.setOpacity(0.0);
-			};
-		};
-		
-		base.prepForShow = function(e){
-			if(!base.showing) {
-				// Prepare for a animate in...
-				base.$label.css({opacity: 0.0}).show();
-				
-				// Reattach the keydown event
-				base.$field.bind('keydown.infieldlabel',function(e){
-					base.hideOnChange(e);
-				});
-			};
-		};
-
-		base.hideOnChange = function(e){
-			if(
-				(e.keyCode == 16) || // Skip Shift
-				(e.keyCode == 9) // Skip Tab
-			  ) return; 
-			
-			if(base.showing){
-				base.$label.hide();
-				base.showing = false;
-			};
-			
-			// Remove keydown event to save on CPU processing
-			base.$field.unbind('keydown.infieldlabel');
-		};
-      
-		// Run the initialization method
-        base.init();
-    };
-	
-    $.InFieldLabels.defaultOptions = {
-        fadeOpacity: 0.5, // Once a field has focus, how transparent should the label be
-		fadeDuration: 300 // How long should it take to animate from 1.0 opacity to the fadeOpacity
-    };
-	
-
-    $.fn.inFieldLabels = function(options){
-        return this.each(function(){
-			// Find input or textarea based on for= attribute
-			// The for attribute on the label must contain the ID
-			// of the input or textarea element
-			var for_attr = $(this).attr('for');
-			if( !for_attr ) return; // Nothing to attach, since the for field wasn't used
-			
-			
-			// Find the referenced input or textarea element
-			var $field = $(
-				"input#" + for_attr + "[type='text']," + 
-				"input#" + for_attr + "[type='password']," + 
-				"textarea#" + for_attr
-				);
-				
-			if( $field.length == 0) return; // Again, nothing to attach
-			
-			// Only create object for input[text], input[password], or textarea
-            (new $.InFieldLabels(this, $field[0], options));
-        });
-    };
-	
-})(jQuery);
\ No newline at end of file
diff --git a/core/js/jquery.infieldlabel.min.js b/core/js/jquery.infieldlabel.min.js
index 8f0ab9f7c5ea845477d14fc7b84cf31020354b45..36f6b8f1271faa49155a3ea12e0f7e7c36b7b640 100644
--- a/core/js/jquery.infieldlabel.min.js
+++ b/core/js/jquery.infieldlabel.min.js
@@ -1,12 +1,12 @@
 /*
- * In-Field Label jQuery Plugin
- * http://fuelyourcoding.com/scripts/infield.html
- *
- * Copyright (c) 2009 Doug Neiner
- * Dual licensed under the MIT and GPL licenses.
- * Uses the same license as jQuery, see:
- * http://docs.jquery.com/License
- *
- * @version 0.1
- */
-(function($){$.InFieldLabels=function(b,c,d){var f=this;f.$label=$(b);f.label=b;f.$field=$(c);f.field=c;f.$label.data("InFieldLabels",f);f.showing=true;f.init=function(){f.options=$.extend({},$.InFieldLabels.defaultOptions,d);if(f.$field.val()!=""){f.$label.hide();f.showing=false};f.$field.focus(function(){f.fadeOnFocus()}).blur(function(){f.checkForEmpty(true)}).bind('keydown.infieldlabel',function(e){f.hideOnChange(e)}).change(function(e){f.checkForEmpty()}).bind('onPropertyChange',function(){f.checkForEmpty()})};f.fadeOnFocus=function(){if(f.showing){f.setOpacity(f.options.fadeOpacity)}};f.setOpacity=function(a){f.$label.stop().animate({opacity:a},f.options.fadeDuration);f.showing=(a>0.0)};f.checkForEmpty=function(a){if(f.$field.val()==""){f.prepForShow();f.setOpacity(a?1.0:f.options.fadeOpacity)}else{f.setOpacity(0.0)}};f.prepForShow=function(e){if(!f.showing){f.$label.css({opacity:0.0}).show();f.$field.bind('keydown.infieldlabel',function(e){f.hideOnChange(e)})}};f.hideOnChange=function(e){if((e.keyCode==16)||(e.keyCode==9))return;if(f.showing){f.$label.hide();f.showing=false};f.$field.unbind('keydown.infieldlabel')};f.init()};$.InFieldLabels.defaultOptions={fadeOpacity:0.5,fadeDuration:300};$.fn.inFieldLabels=function(c){return this.each(function(){var a=$(this).attr('for');if(!a)return;var b=$("input#"+a+"[type='text'],"+"input#"+a+"[type='password'],"+"textarea#"+a);if(b.length==0)return;(new $.InFieldLabels(this,b[0],c))})}})(jQuery);
\ No newline at end of file
+ In-Field Label jQuery Plugin
+ http://fuelyourcoding.com/scripts/infield.html
+
+ Copyright (c) 2009-2010 Doug Neiner
+ Dual licensed under the MIT and GPL licenses.
+ Uses the same license as jQuery, see:
+ http://docs.jquery.com/License
+
+ @version 0.1.5
+*/
+(function($){$.InFieldLabels=function(label,field,options){var base=this;base.$label=$(label);base.label=label;base.$field=$(field);base.field=field;base.$label.data("InFieldLabels",base);base.showing=true;base.init=function(){base.options=$.extend({},$.InFieldLabels.defaultOptions,options);setTimeout(function(){if(base.$field.val()!==""){base.$label.hide();base.showing=false}},200);base.$field.focus(function(){base.fadeOnFocus()}).blur(function(){base.checkForEmpty(true)}).bind('keydown.infieldlabel',function(e){base.hideOnChange(e)}).bind('paste',function(e){base.setOpacity(0.0)}).change(function(e){base.checkForEmpty()}).bind('onPropertyChange',function(){base.checkForEmpty()}).bind('keyup.infieldlabel',function(){base.checkForEmpty()})};base.fadeOnFocus=function(){if(base.showing){base.setOpacity(base.options.fadeOpacity)}};base.setOpacity=function(opacity){base.$label.stop().animate({opacity:opacity},base.options.fadeDuration);base.showing=(opacity>0.0)};base.checkForEmpty=function(blur){if(base.$field.val()===""){base.prepForShow();base.setOpacity(blur?1.0:base.options.fadeOpacity)}else{base.setOpacity(0.0)}};base.prepForShow=function(e){if(!base.showing){base.$label.css({opacity:0.0}).show();base.$field.bind('keydown.infieldlabel',function(e){base.hideOnChange(e)})}};base.hideOnChange=function(e){if((e.keyCode===16)||(e.keyCode===9)){return}if(base.showing){base.$label.hide();base.showing=false}base.$field.unbind('keydown.infieldlabel')};base.init()};$.InFieldLabels.defaultOptions={fadeOpacity:0.5,fadeDuration:300};$.fn.inFieldLabels=function(options){return this.each(function(){var for_attr=$(this).attr('for'),$field;if(!for_attr){return}$field=$("input#"+for_attr+"[type='text'],"+"input#"+for_attr+"[type='search'],"+"input#"+for_attr+"[type='tel'],"+"input#"+for_attr+"[type='url'],"+"input#"+for_attr+"[type='email'],"+"input#"+for_attr+"[type='password'],"+"textarea#"+for_attr);if($field.length===0){return}(new $.InFieldLabels(this,$field[0],options))})}}(jQuery));
diff --git a/lib/helper.php b/lib/helper.php
index 00b974543ad44530927543def626de6d55bcb10c..aa453ad750496a4ba8b4a430696da6cdb15b47bb 100644
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -373,20 +373,15 @@ class OC_Helper {
 		}
 		if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) {
 			// it looks like we have a 'file' command,
-			// lets see it it does have mime support
+			// lets see if it does have mime support
 			$path=escapeshellarg($path);
 			$fp = popen("file -i -b $path 2>/dev/null", "r");
 			$reply = fgets($fp);
 			pclose($fp);
 
-			//trim the character set from the end of the response
-			$mimeType=substr($reply,0, strrpos($reply,' '));
-			$mimeType=substr($mimeType,0, strrpos($mimeType,"\n"));
-
-			//trim ;
-			if (strpos($mimeType, ';') !== false) {
-				$mimeType = strstr($mimeType, ';', true);
-			}
+			// we have smth like 'text/x-c++; charset=us-ascii\n'
+			// and need to eliminate everything starting with semicolon including trailing LF
+			$mimeType = preg_replace('/;.*/ms', '', trim($reply));
 
 		}
 		return $mimeType;
diff --git a/lib/public/share.php b/lib/public/share.php
index d27802b52f7c9bb2544783eeafae08b2f8c52983..7a9a087d1bd75fcc7479a0b1e7e8d6b80f2d8118 100644
--- a/lib/public/share.php
+++ b/lib/public/share.php
@@ -28,6 +28,9 @@ namespace OCP;
 /**
 * This class provides the ability for apps to share their content between users.
 * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
+* 
+* It provides the following hooks:
+*  - post_shared
 */
 class Share {
 
@@ -937,7 +940,20 @@ class Share {
 				// Insert an extra row for the group share if the item or file target is unique for this user
 				if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) {
 					$query->execute(array($itemType, $itemSource, $itemTarget, $parent, self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), $fileSource, $fileTarget));
-					\OC_DB::insertid('*PREFIX*share');
+					$id = \OC_DB::insertid('*PREFIX*share');
+					\OC_Hook::emit('OCP\Share', 'post_shared', array(
+						'itemType' => $itemType,
+						'itemSource' => $itemSource,
+						'itemTarget' => $itemTarget,
+						'parent' => $parent,
+						'shareType' => self::$shareTypeGroupUserUnique,
+						'shareWith' => $uid,
+						'uidOwner' => $uidOwner,
+						'permissions' => $permissions,
+						'fileSource' => $fileSource,
+						'fileTarget' => $fileTarget,
+						'id' => $id
+					));
 				}
 			}
 			if ($parentFolder === true) {
@@ -963,6 +979,19 @@ class Share {
 			}
 			$query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, $permissions, time(), $fileSource, $fileTarget));
 			$id = \OC_DB::insertid('*PREFIX*share');
+			\OC_Hook::emit('OCP\Share', 'post_shared', array(
+				'itemType' => $itemType,
+				'itemSource' => $itemSource,
+				'itemTarget' => $itemTarget,
+				'parent' => $parent,
+				'shareType' => $shareType,
+				'shareWith' => $shareWith,
+				'uidOwner' => $uidOwner,
+				'permissions' => $permissions,
+				'fileSource' => $fileSource,
+				'fileTarget' => $fileTarget,
+				'id' => $id
+			));
 			if ($parentFolder === true) {
 				$parentFolders['id'] = $id;
 				// Return parent folder to preserve file target paths for potential children
diff --git a/settings/js/apps.js b/settings/js/apps.js
index 8de95100c4c59e9f912c43f1b5814ac1c56a474e..e45abf9b3dde4f6ee895dbabe24dfd367fc49ba1 100644
--- a/settings/js/apps.js
+++ b/settings/js/apps.js
@@ -51,6 +51,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
 				}
 				else {
 					element.data('active',false);
+					OC.Settings.Apps.removeNavigation(appid);
 					element.val(t('settings','Enable'));
 				}
 			},'json');
@@ -61,6 +62,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
 					OC.dialogs.alert('Error while enabling app','Error');
 				}
 				else {
+					OC.Settings.Apps.addNavigation(appid);
 					element.data('active',true);
 					element.val(t('settings','Disable'));
 				}
@@ -87,6 +89,38 @@ OC.Settings.Apps = OC.Settings.Apps || {
 			applist.last().after(app);
 		}
 		return app;
+	},
+	removeNavigation: function(appid){
+		$.getJSON(OC.filePath('core','ajax','navigationdetect.php'), {app: appid}).done(function(response){
+			if(response.status === 'success'){
+				var navIds=response.nav_ids;
+				for(var i=0; i< navIds.length; i++){
+					$('#apps').children('li[data-id="'+navIds[i]+'"]').remove();
+				}
+			}
+		});
+	},
+	addNavigation: function(appid){
+		$.getJSON(OC.filePath('core','ajax','navigationdetect.php'), {app: appid}).done(function(response){
+			if(response.status === 'success'){
+				var navEntries=response.nav_entries;
+				for(var i=0; i< navEntries.length; i++){
+					var entry = navEntries[i];
+					var container = $('#apps');
+
+					if(container.children('li[data-id="'+entry.id+'"]').length === 0){
+						var li=$('<li></li>');
+						li.attr('data-id', entry.id);
+						var a=$('<a></a>');
+						a.attr('style', 'background-image: url('+entry.icon+')');
+						a.text(entry.name);
+						a.attr('href', entry.href);
+						li.append(a);
+						container.append(li);
+					}
+				}
+			}
+		});
 	}
 };