diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php
index cc86e9bf27096bd6307c8e0ee5018f3709cdf516..850c335c27d061f93fcdf9c66c2ff9266e8247db 100644
--- a/apps/files/appinfo/app.php
+++ b/apps/files/appinfo/app.php
@@ -28,6 +28,7 @@
  */
 \OCP\App::registerAdmin('files', 'admin');
 
+$l = \OC::$server->getL10N('files');
 
 \OC::$server->getNavigationManager()->add(function () {
 	$urlGenerator = \OC::$server->getURLGenerator();
@@ -49,8 +50,7 @@ $templateManager->registerTemplate('application/vnd.oasis.opendocument.presentat
 $templateManager->registerTemplate('application/vnd.oasis.opendocument.text', 'core/templates/filetemplates/template.odt');
 $templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadsheet', 'core/templates/filetemplates/template.ods');
 
-\OCA\Files\App::getNavigationManager()->add(function () {
-	$l = \OC::$server->getL10N('files');
+\OCA\Files\App::getNavigationManager()->add(function () use ($l) {
 	return [
 		'id' => 'files',
 		'appname' => 'files',
@@ -60,6 +60,16 @@ $templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadshe
 	];
 });
 
+\OCA\Files\App::getNavigationManager()->add(function () use ($l) {
+	return [
+		'id' => 'recent',
+		'appname' => 'files',
+		'script' => 'recentlist.php',
+		'order' => 2,
+		'name' => $l->t('Recent'),
+	];
+});
+
 \OC::$server->getActivityManager()->registerExtension(function() {
 	return new \OCA\Files\Activity(
 		\OC::$server->query('L10NFactory'),
diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php
index 34bb10117733d5656ca10b55200cdfcaa09aa770..7b5ac63a1603f8c04643a5f56292886907d095ce 100644
--- a/apps/files/appinfo/routes.php
+++ b/apps/files/appinfo/routes.php
@@ -50,6 +50,11 @@ $application->registerRoutes(
 				'verb' => 'GET',
 				'requirements' => array('tagName' => '.+'),
 			),
+			array(
+				'name' => 'API#getRecentFiles',
+				'url' => '/api/v1/recent/',
+				'verb' => 'GET'
+			),
 			array(
 				'name' => 'API#updateFileSorting',
 				'url' => '/api/v1/sorting',
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index f0b16a5788680d60a1371ab9a1c79a69b760804a..24cccb3a5c8f34b04aef2def4510d0410776528d 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -174,6 +174,11 @@
 		 */
 		_clientSideSort: true,
 
+		/**
+		 * Whether or not users can change the sort attribute or direction
+		 */
+		_allowSorting: true,
+
 		/**
 		 * Current directory
 		 * @type String
@@ -718,7 +723,7 @@
 				$target = $target.closest('a');
 			}
 			sort = $target.attr('data-sort');
-			if (sort) {
+			if (sort && this._allowSorting) {
 				if (this._sort === sort) {
 					this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc', true, true);
 				}
diff --git a/apps/files/js/recentfilelist.js b/apps/files/js/recentfilelist.js
new file mode 100644
index 0000000000000000000000000000000000000000..e8c61cbfe2df740a4d62e7501e7e6e77000f408f
--- /dev/null
+++ b/apps/files/js/recentfilelist.js
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+// HACK: this piece needs to be loaded AFTER the files app (for unit tests)
+$(document).ready(function () {
+	(function (OCA) {
+		/**
+		 * @class OCA.Files.RecentFileList
+		 * @augments OCA.Files.RecentFileList
+		 *
+		 * @classdesc Recent file list.
+		 * Displays the list of recently modified files
+		 *
+		 * @param $el container element with existing markup for the #controls
+		 * and a table
+		 * @param [options] map of options, see other parameters
+		 */
+		var RecentFileList = function ($el, options) {
+			options.sorting = {
+				mode: 'mtime',
+				direction: 'desc'
+			};
+			this.initialize($el, options);
+			this._allowSorting = false;
+		};
+		RecentFileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
+			/** @lends OCA.Files.RecentFileList.prototype */ {
+				id: 'recent',
+				appName: t('files', 'Recent'),
+
+				_clientSideSort: true,
+				_allowSelection: false,
+
+				/**
+				 * @private
+				 */
+				initialize: function () {
+					OCA.Files.FileList.prototype.initialize.apply(this, arguments);
+					if (this.initialized) {
+						return;
+					}
+					OC.Plugins.attach('OCA.Files.RecentFileList', this);
+				},
+
+				updateEmptyContent: function () {
+					var dir = this.getCurrentDirectory();
+					if (dir === '/') {
+						// root has special permissions
+						this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty);
+						this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty);
+					}
+					else {
+						OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
+					}
+				},
+
+				getDirectoryPermissions: function () {
+					return OC.PERMISSION_READ | OC.PERMISSION_DELETE;
+				},
+
+				updateStorageStatistics: function () {
+					// no op because it doesn't have
+					// storage info like free space / used space
+				},
+
+				reload: function () {
+					this.showMask();
+					if (this._reloadCall) {
+						this._reloadCall.abort();
+					}
+
+					// there is only root
+					this._setCurrentDir('/', false);
+
+					this._reloadCall = $.ajax({
+						url: OC.generateUrl('/apps/files/api/v1/recent'),
+						type: 'GET',
+						dataType: 'json'
+					});
+					var callBack = this.reloadCallback.bind(this);
+					return this._reloadCall.then(callBack, callBack);
+				},
+
+				reloadCallback: function (result) {
+					delete this._reloadCall;
+					this.hideMask();
+
+					if (result.files) {
+						this.setFiles(result.files.sort(this._sortComparator));
+						return true;
+					}
+					return false;
+				}
+			});
+
+		OCA.Files.RecentFileList = RecentFileList;
+	})(OCA);
+});
+
diff --git a/apps/files/js/recentplugin.js b/apps/files/js/recentplugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..fcd427b18a2c278254ec287b65ff868fc91bba83
--- /dev/null
+++ b/apps/files/js/recentplugin.js
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function (OCA) {
+	/**
+	 * @namespace OCA.Files.RecentPlugin
+	 *
+	 * Registers the recent file list from the files app sidebar.
+	 */
+	OCA.Files.RecentPlugin = {
+		name: 'Recent',
+
+		/**
+		 * @type OCA.Files.RecentFileList
+		 */
+		recentFileList: null,
+
+		attach: function () {
+			var self = this;
+			$('#app-content-recent').on('show.plugin-recent', function (e) {
+				self.showFileList($(e.target));
+			});
+			$('#app-content-recent').on('hide.plugin-recent', function () {
+				self.hideFileList();
+			});
+		},
+
+		detach: function () {
+			if (this.recentFileList) {
+				this.recentFileList.destroy();
+				OCA.Files.fileActions.off('setDefault.plugin-recent', this._onActionsUpdated);
+				OCA.Files.fileActions.off('registerAction.plugin-recent', this._onActionsUpdated);
+				$('#app-content-recent').off('.plugin-recent');
+				this.recentFileList = null;
+			}
+		},
+
+		showFileList: function ($el) {
+			if (!this.recentFileList) {
+				this.recentFileList = this._createRecentFileList($el);
+			}
+			return this.recentFileList;
+		},
+
+		hideFileList: function () {
+			if (this.recentFileList) {
+				this.recentFileList.$fileList.empty();
+			}
+		},
+
+		/**
+		 * Creates the recent file list.
+		 *
+		 * @param $el container for the file list
+		 * @return {OCA.Files.RecentFileList} file list
+		 */
+		_createRecentFileList: function ($el) {
+			var fileActions = this._createFileActions();
+			// register recent list for sidebar section
+			return new OCA.Files.RecentFileList(
+				$el, {
+					fileActions: fileActions,
+					scrollContainer: $('#app-content')
+				}
+			);
+		},
+
+		_createFileActions: function () {
+			// inherit file actions from the files app
+			var fileActions = new OCA.Files.FileActions();
+			// note: not merging the legacy actions because legacy apps are not
+			// compatible with the sharing overview and need to be adapted first
+			fileActions.registerDefaultActions();
+			fileActions.merge(OCA.Files.fileActions);
+
+			if (!this._globalActionsInitialized) {
+				// in case actions are registered later
+				this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
+				OCA.Files.fileActions.on('setDefault.plugin-recent', this._onActionsUpdated);
+				OCA.Files.fileActions.on('registerAction.plugin-recent', this._onActionsUpdated);
+				this._globalActionsInitialized = true;
+			}
+
+			// when the user clicks on a folder, redirect to the corresponding
+			// folder in the files app instead of opening it directly
+			fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
+				OCA.Files.App.setActiveView('files', {silent: true});
+				var path = OC.joinPaths(context.$file.attr('data-path'), filename);
+				OCA.Files.App.fileList.changeDirectory(path, true, true);
+			});
+			fileActions.setDefault('dir', 'Open');
+			return fileActions;
+		},
+
+		_onActionsUpdated: function (ev) {
+			if (ev.action) {
+				this.recentFileList.fileActions.registerAction(ev.action);
+			} else if (ev.defaultAction) {
+				this.recentFileList.fileActions.setDefault(
+					ev.defaultAction.mime,
+					ev.defaultAction.name
+				);
+			}
+		}
+	};
+
+})(OCA);
+
+OC.Plugins.register('OCA.Files.App', OCA.Files.RecentPlugin);
+
diff --git a/apps/files/lib/AppInfo/Application.php b/apps/files/lib/AppInfo/Application.php
index fac8e3a3a4bea4de97e5fd9147e9afb5c06f9bf4..fc91e05ba7e66ebfc5fefc2a41f1c4b6ee67f7fa 100644
--- a/apps/files/lib/AppInfo/Application.php
+++ b/apps/files/lib/AppInfo/Application.php
@@ -47,7 +47,8 @@ class Application extends App {
 				$c->query('TagService'),
 				$server->getPreviewManager(),
 				$server->getShareManager(),
-				$server->getConfig()
+				$server->getConfig(),
+				$server->getUserFolder()
 			);
 		});
 
diff --git a/apps/files/lib/Controller/ApiController.php b/apps/files/lib/Controller/ApiController.php
index 57eb43bbe9cffca3a2c707b45ce20127bcab47b2..7ce83bfca15e86b027920259a5f7f64c6805dce1 100644
--- a/apps/files/lib/Controller/ApiController.php
+++ b/apps/files/lib/Controller/ApiController.php
@@ -31,6 +31,7 @@ namespace OCA\Files\Controller;
 
 use OCP\AppFramework\Http;
 use OCP\AppFramework\Controller;
+use OCP\Files\Folder;
 use OCP\IConfig;
 use OCP\IRequest;
 use OCP\AppFramework\Http\DataResponse;
@@ -39,7 +40,7 @@ use OCP\AppFramework\Http\Response;
 use OCA\Files\Service\TagService;
 use OCP\IPreview;
 use OCP\Share\IManager;
-use OCP\Files\Node;
+use OC\Files\Node\Node;
 use OCP\IUserSession;
 
 /**
@@ -58,12 +59,18 @@ class ApiController extends Controller {
 	private $userSession;
 	/** IConfig */
 	private $config;
+	/** @var Folder */
+	private $userFolder;
 
 	/**
 	 * @param string $appName
 	 * @param IRequest $request
+	 * @param IUserSession $userSession
 	 * @param TagService $tagService
 	 * @param IPreview $previewManager
+	 * @param IManager $shareManager
+	 * @param IConfig $config
+	 * @param Folder $userFolder
 	 */
 	public function __construct($appName,
 								IRequest $request,
@@ -71,13 +78,15 @@ class ApiController extends Controller {
 								TagService $tagService,
 								IPreview $previewManager,
 								IManager $shareManager,
-								IConfig $config) {
+								IConfig $config,
+								Folder $userFolder) {
 		parent::__construct($appName, $request);
 		$this->userSession = $userSession;
 		$this->tagService = $tagService;
 		$this->previewManager = $previewManager;
 		$this->shareManager = $shareManager;
 		$this->config = $config;
+		$this->userFolder = $userFolder;
 	}
 
 	/**
@@ -142,6 +151,28 @@ class ApiController extends Controller {
 		return new DataResponse($result);
 	}
 
+	/**
+	 * @param \OCP\Files\Node[] $nodes
+	 * @return array
+	 */
+	private function formatNodes(array $nodes) {
+		return array_values(array_map(function (Node $node) {
+			/** @var \OC\Files\Node\Node $shareTypes */
+			$shareTypes = $this->getShareTypes($node);
+			$file = \OCA\Files\Helper::formatFileInfo($node->getFileInfo());
+			$parts = explode('/', dirname($node->getPath()), 4);
+			if (isset($parts[3])) {
+				$file['path'] = '/' . $parts[3];
+			} else {
+				$file['path'] = '/';
+			}
+			if (!empty($shareTypes)) {
+				$file['shareTypes'] = $shareTypes;
+			}
+			return $file;
+		}, $nodes));
+	}
+
 	/**
 	 * Returns a list of all files tagged with the given tag.
 	 *
@@ -151,27 +182,27 @@ class ApiController extends Controller {
 	 * @return DataResponse
 	 */
 	public function getFilesByTag($tagName) {
-		$files = array();
 		$nodes = $this->tagService->getFilesByTag($tagName);
-		foreach ($nodes as &$node) {
-			$shareTypes = $this->getShareTypes($node);
-			$fileInfo = $node->getFileInfo();
-			$file = \OCA\Files\Helper::formatFileInfo($fileInfo);
-			$parts = explode('/', dirname($fileInfo->getPath()), 4);
-			if(isset($parts[3])) {
-				$file['path'] = '/' . $parts[3];
-			} else {
-				$file['path'] = '/';
-			}
+		$files = $this->formatNodes($nodes);
+		foreach ($files as &$file) {
 			$file['tags'] = [$tagName];
-			if (!empty($shareTypes)) {
-				$file['shareTypes'] = $shareTypes;
-			}
-			$files[] = $file;
 		}
 		return new DataResponse(['files' => $files]);
 	}
 
+	/**
+	 * Returns a list of recently modifed files.
+	 *
+	 * @NoAdminRequired
+	 *
+	 * @return DataResponse
+	 */
+	public function getRecentFiles() {
+		$nodes = $this->userFolder->getRecent(100);
+		$files = $this->formatNodes($nodes);
+		return new DataResponse(['files' => $files]);
+	}
+
 	/**
 	 * Return a list of share types for outgoing shares
 	 *
diff --git a/apps/files/lib/Controller/ViewController.php b/apps/files/lib/Controller/ViewController.php
index 5e342e6589bc455b4c80e94439939bf073142fa8..9dbe06ff7898f3676bac1f93dc6478339fe76339 100644
--- a/apps/files/lib/Controller/ViewController.php
+++ b/apps/files/lib/Controller/ViewController.php
@@ -173,9 +173,11 @@ class ViewController extends Controller {
 		\OCP\Util::addscript('files', 'search');
 
 		\OCP\Util::addScript('files', 'favoritesfilelist');
+		\OCP\Util::addScript('files', 'recentfilelist');
 		\OCP\Util::addScript('files', 'tagsplugin');
 		\OCP\Util::addScript('files', 'gotoplugin');
 		\OCP\Util::addScript('files', 'favoritesplugin');
+		\OCP\Util::addScript('files', 'recentplugin');
 
 		\OCP\Util::addScript('files', 'detailfileinfoview');
 		\OCP\Util::addScript('files', 'sidebarpreviewmanager');
diff --git a/apps/files/recentlist.php b/apps/files/recentlist.php
new file mode 100644
index 0000000000000000000000000000000000000000..1976be4894ad5906d426df076b258e6dd0afcb85
--- /dev/null
+++ b/apps/files/recentlist.php
@@ -0,0 +1,7 @@
+<?php
+// Check if we are a user
+OCP\User::checkLoggedIn();
+
+$tmpl = new OCP\Template('files', 'recentlist', '');
+
+$tmpl->printPage();
diff --git a/apps/files/templates/recentlist.php b/apps/files/templates/recentlist.php
new file mode 100644
index 0000000000000000000000000000000000000000..1667eb4cc8d9a81552daaeafc289b61fd104bf14
--- /dev/null
+++ b/apps/files/templates/recentlist.php
@@ -0,0 +1,42 @@
+<?php /** @var $l OC_L10N */ ?>
+<div id='notification'></div>
+
+<div id="emptycontent" class="hidden"></div>
+
+<input type="hidden" name="dir" value="" id="dir">
+
+<div class="nofilterresults hidden">
+	<div class="icon-search"></div>
+	<h2><?php p($l->t('No entries found in this folder')); ?></h2>
+	<p></p>
+</div>
+
+<table id="filestable">
+	<thead>
+	<tr>
+		<th id='headerName' class="hidden column-name">
+			<div id="headerName-container">
+				<a class="name sort columntitle"
+				   data-sort="name"><span><?php p($l->t('Name')); ?></span></a>
+			</div>
+		</th>
+		<th id="headerSize" class="hidden column-size">
+			<a class="size sort columntitle"
+			   data-sort="size"><span><?php p($l->t('Size')); ?></span></a>
+		</th>
+		<th id="headerDate" class="hidden column-mtime">
+			<a id="modified" class="columntitle"
+			   data-sort="mtime"><span><?php p($l->t('Modified')); ?></span><span
+					class="sort-indicator"></span></a>
+			<span class="selectedActions"><a href="" class="delete-selected">
+						<span><?php p($l->t('Delete')) ?></span>
+						<span class="icon icon-delete"></span>
+					</a></span>
+		</th>
+	</tr>
+	</thead>
+	<tbody id="fileList">
+	</tbody>
+	<tfoot>
+	</tfoot>
+</table>
diff --git a/apps/files/tests/Controller/ApiControllerTest.php b/apps/files/tests/Controller/ApiControllerTest.php
index 1d39c88021bea5eff9473ca0933dfef7dbc1b14b..348150e0e0835caff0c734a6a2670753ad8ff023 100644
--- a/apps/files/tests/Controller/ApiControllerTest.php
+++ b/apps/files/tests/Controller/ApiControllerTest.php
@@ -59,6 +59,8 @@ class ApiControllerTest extends TestCase {
 	private $shareManager;
 	/** @var \OCP\IConfig */
 	private $config;
+	/** @var  \OC\Files\Node\Folder */
+	private $userFolder;
 
 	public function setUp() {
 		$this->request = $this->getMockBuilder('\OCP\IRequest')
@@ -82,6 +84,9 @@ class ApiControllerTest extends TestCase {
 			->disableOriginalConstructor()
 			->getMock();
 		$this->config = $this->getMock('\OCP\IConfig');
+		$this->userFolder = $this->getMockBuilder('\OC\Files\Node\Folder')
+			->disableOriginalConstructor()
+			->getMock();
 
 		$this->apiController = new ApiController(
 			$this->appName,
@@ -90,7 +95,8 @@ class ApiControllerTest extends TestCase {
 			$this->tagService,
 			$this->preview,
 			$this->shareManager,
-			$this->config
+			$this->config,
+			$this->userFolder
 		);
 	}
 
diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php
index ceb48a2241f3f1ccfb922e1663dcfa2f987a0520..373f8c25152223f92ac9107cb5c549c44ac60044 100644
--- a/apps/files/tests/Controller/ViewControllerTest.php
+++ b/apps/files/tests/Controller/ViewControllerTest.php
@@ -191,7 +191,16 @@ class ViewControllerTest extends TestCase {
 				'appname' => 'files',
 				'script' => 'list.php',
 				'order' => 0,
-				'name' => new \OC_L10N_String(new \OC_L10N('files'), 'All files', []),
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('files'), 'All files', []),
+				'active' => false,
+				'icon' => '',
+			],
+			[
+				'id' => 'recent',
+				'appname' => 'files',
+				'script' => 'recentlist.php',
+				'order' => 2,
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('files'), 'Recent', []),
 				'active' => false,
 				'icon' => '',
 			],
@@ -209,7 +218,7 @@ class ViewControllerTest extends TestCase {
 				'appname' => 'files_sharing',
 				'script' => 'list.php',
 				'order' => 10,
-				'name' => new \OC_L10N_String(new \OC_L10N('files_sharing'), 'Shared with you', []),
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('files_sharing'), 'Shared with you', []),
 				'active' => false,
 				'icon' => '',
 			],
@@ -218,7 +227,7 @@ class ViewControllerTest extends TestCase {
 				'appname' => 'files_sharing',
 				'script' => 'list.php',
 				'order' => 15,
-				'name' => new \OC_L10N_String(new \OC_L10N('files_sharing'), 'Shared with others', []),
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('files_sharing'), 'Shared with others', []),
 				'active' => false,
 				'icon' => '',
 			],
@@ -227,7 +236,7 @@ class ViewControllerTest extends TestCase {
 				'appname' => 'files_sharing',
 				'script' => 'list.php',
 				'order' => 20,
-				'name' => new \OC_L10N_String(new \OC_L10N('files_sharing'), 'Shared by link', []),
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('files_sharing'), 'Shared by link', []),
 				'active' => false,
 				'icon' => '',
 			],
@@ -236,7 +245,7 @@ class ViewControllerTest extends TestCase {
 				'appname' => 'systemtags',
 				'script' => 'list.php',
 				'order' => 25,
-				'name' => new \OC_L10N_String(new \OC_L10N('systemtags'), 'Tags', []),
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('systemtags'), 'Tags', []),
 				'active' => false,
 				'icon' => '',
 			],
@@ -245,7 +254,7 @@ class ViewControllerTest extends TestCase {
 				'appname' => 'files_trashbin',
 				'script' => 'list.php',
 				'order' => 50,
-				'name' => new \OC_L10N_String(new \OC_L10N('files_trashbin'), 'Deleted files', []),
+				'name' => (string)new \OC_L10N_String(new \OC_L10N('files_trashbin'), 'Deleted files', []),
 				'active' => false,
 				'icon' => '',
 				],
@@ -272,6 +281,10 @@ class ViewControllerTest extends TestCase {
 						'id' => 'files',
 						'content' => null,
 					],
+					[
+						'id' => 'recent',
+						'content' => null,
+					],
 					[
 						'id' => 'favorites',
 						'content' => null,
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 8813b6c0775cfa800b7edcb2d51250b998349ef7..e67e4817e2aaf3b5cb136cd36fc73f21eb069167 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -26,7 +26,10 @@
 
 namespace OC\Files\Node;
 
+use OC\DB\QueryBuilder\Literal;
+use OCP\DB\QueryBuilder\IQueryBuilder;
 use OCP\Files\FileInfo;
+use OCP\Files\Mount\IMountPoint;
 use OCP\Files\NotFoundException;
 use OCP\Files\NotPermittedException;
 
@@ -358,4 +361,79 @@ class Folder extends Node implements \OCP\Files\Folder {
 		$uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
 		return trim($this->getRelativePath($uniqueName), '/');
 	}
+
+	/**
+	 * @param int $limit
+	 * @param int $offset
+	 * @return \OCP\Files\Node[]
+	 */
+	public function getRecent($limit, $offset = 0) {
+		$mimetypeLoader = \OC::$server->getMimeTypeLoader();
+		$mounts = $this->root->getMountsIn($this->path);
+		$mounts[] = $this->getMountPoint();
+
+		$mounts = array_filter($mounts, function (IMountPoint $mount) {
+			return $mount->getStorage();
+		});
+		$storageIds = array_map(function (IMountPoint $mount) {
+			return $mount->getStorage()->getCache()->getNumericStorageId();
+		}, $mounts);
+		/** @var IMountPoint[] $mountMap */
+		$mountMap = array_combine($storageIds, $mounts);
+		$folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
+
+		//todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc)
+
+		$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
+		$query = $builder
+			->select('f.*')
+			->from('filecache', 'f')
+			->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
+			->andWhere($builder->expr()->orX(
+			// handle non empty folders separate
+				$builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
+				$builder->expr()->eq('f.size', new Literal(0))
+			))
+			->orderBy('f.mtime', 'DESC')
+			->setMaxResults($limit)
+			->setFirstResult($offset);
+
+		$result = $query->execute()->fetchAll();
+
+		$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
+			$mount = $mountMap[$entry['storage']];
+			$entry['internalPath'] = $entry['path'];
+			$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
+			$entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
+			$path = $this->getAbsolutePath($mount, $entry['path']);
+			if (is_null($path)) {
+				return null;
+			}
+			$fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
+			return $this->root->createNode($fileInfo->getPath(), $fileInfo);
+		}, $result));
+
+		return array_values(array_filter($files, function (Node $node) {
+			$relative = $this->getRelativePath($node->getPath());
+			return $relative !== null && $relative !== '/';
+		}));
+	}
+
+	private function getAbsolutePath(IMountPoint $mount, $path) {
+		$storage = $mount->getStorage();
+		if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
+			/** @var \OC\Files\Storage\Wrapper\Jail $storage */
+			$jailRoot = $storage->getSourcePath('');
+			$rootLength = strlen($jailRoot) + 1;
+			if ($path === $jailRoot) {
+				return $mount->getMountPoint();
+			} else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
+				return $mount->getMountPoint() . substr($path, $rootLength);
+			} else {
+				return null;
+			}
+		} else {
+			return $mount->getMountPoint() . $path;
+		}
+	}
 }
diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php
index 1203fc4d1627635b4a87507a02c3399203668e52..317b8144653a88530d549db96c2539a9e4786a53 100644
--- a/lib/private/Files/Node/LazyRoot.php
+++ b/lib/private/Files/Node/LazyRoot.php
@@ -471,5 +471,10 @@ class LazyRoot implements IRootFolder {
 		return $this->__call(__FUNCTION__, func_get_args());
 	}
 
-
+	/**
+	 * @inheritDoc
+	 */
+	public function getRecent($limit, $offset = 0) {
+		return $this->__call(__FUNCTION__, func_get_args());
+	}
 }
diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php
index 80c75523748f0e258a9db118b15f52bef3409ebc..5b9085799b932d5c153e880d9b2ea622b45156df 100644
--- a/lib/private/Files/Storage/Wrapper/Jail.php
+++ b/lib/private/Files/Storage/Wrapper/Jail.php
@@ -377,7 +377,7 @@ class Jail extends Wrapper {
 	 */
 	public function getCache($path = '', $storage = null) {
 		if (!$storage) {
-			$storage = $this;
+			$storage = $this->storage;
 		}
 		$sourceCache = $this->storage->getCache($this->getSourcePath($path), $storage);
 		return new CacheJail($sourceCache, $this->rootPath);
diff --git a/lib/public/Files/FileInfo.php b/lib/public/Files/FileInfo.php
index d61726c4217aa11d1896e733786fa7c1f0000713..04790d41556692cbba0c6524ce035fa55a7d0dba 100644
--- a/lib/public/Files/FileInfo.php
+++ b/lib/public/Files/FileInfo.php
@@ -58,6 +58,11 @@ interface FileInfo {
 	 */
 	const SPACE_UNLIMITED = -3;
 
+	/**
+	 * @since 9.1.0
+	 */
+	const MIMETYPE_FOLDER = 'httpd/unix-directory';
+
 	/**
 	 * Get the Etag of the file or folder
 	 *
diff --git a/lib/public/Files/Folder.php b/lib/public/Files/Folder.php
index 9a2a338b559fde493dcc10e2500d3cfbec14710b..8f8576d8503f290bc9eded60d7ddd64a819302d0 100644
--- a/lib/public/Files/Folder.php
+++ b/lib/public/Files/Folder.php
@@ -175,4 +175,12 @@ interface Folder extends Node {
 	 * @since 8.1.0
 	 */
 	public function getNonExistingName($name);
+
+	/**
+	 * @param int $limit
+	 * @param int $offset
+	 * @return \OCP\Files\Node[]
+	 * @since 9.1.0
+	 */
+	public function getRecent($limit, $offset = 0);
 }
diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php
index 7ce9fff1419486e7de4dd80b2433602721244895..18acfcae1fabba34e25a7c4b2c64feae78925542 100644
--- a/tests/lib/Files/Node/FolderTest.php
+++ b/tests/lib/Files/Node/FolderTest.php
@@ -12,6 +12,8 @@ use OC\Files\Cache\Cache;
 use OC\Files\FileInfo;
 use OC\Files\Mount\MountPoint;
 use OC\Files\Node\Node;
+use OC\Files\Storage\Temporary;
+use OC\Files\Storage\Wrapper\Jail;
 use OCP\Files\NotFoundException;
 use OCP\Files\NotPermittedException;
 use OC\Files\View;
@@ -760,9 +762,9 @@ class FolderTest extends \Test\TestCase {
 	public function uniqueNameProvider() {
 		return [
 			// input, existing, expected
-			['foo', []					, 'foo'],
-			['foo', ['foo']				, 'foo (2)'],
-			['foo', ['foo', 'foo (2)']	, 'foo (3)']
+			['foo', [], 'foo'],
+			['foo', ['foo'], 'foo (2)'],
+			['foo', ['foo', 'foo (2)'], 'foo (3)']
 		];
 	}
 
@@ -782,7 +784,7 @@ class FolderTest extends \Test\TestCase {
 			->method('file_exists')
 			->will($this->returnCallback(function ($path) use ($existingFiles, $folderPath) {
 				foreach ($existingFiles as $existing) {
-					if ($folderPath . '/' . $existing === $path){
+					if ($folderPath . '/' . $existing === $path) {
 						return true;
 					}
 				}
@@ -792,4 +794,167 @@ class FolderTest extends \Test\TestCase {
 		$node = new \OC\Files\Node\Folder($root, $view, $folderPath);
 		$this->assertEquals($expected, $node->getNonExistingName($name));
 	}
+
+	public function testRecent() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		$folderPath = '/bar/foo';
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */
+		$root = $this->getMock('\OC\Files\Node\Root', array('getUser', 'getMountsIn', 'getMount'), array($manager, $view, $this->user));
+		/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */
+		$folderInfo = $this->getMockBuilder('\OC\Files\FileInfo')
+			->disableOriginalConstructor()->getMock();
+
+		$baseTime = 1000;
+		$storage = new Temporary();
+		$mount = new MountPoint($storage, '');
+
+		$folderInfo->expects($this->any())
+			->method('getMountPoint')
+			->will($this->returnValue($mount));
+
+		$cache = $storage->getCache();
+
+		$id1 = $cache->put('bar/foo/inside.txt', [
+			'storage_mtime' => $baseTime,
+			'mtime' => $baseTime,
+			'mimetype' => 'text/plain',
+			'size' => 3
+		]);
+		$id2 = $cache->put('bar/foo/old.txt', [
+			'storage_mtime' => $baseTime - 100,
+			'mtime' => $baseTime - 100,
+			'mimetype' => 'text/plain',
+			'size' => 3
+		]);
+		$cache->put('bar/asd/outside.txt', [
+			'storage_mtime' => $baseTime,
+			'mtime' => $baseTime,
+			'mimetype' => 'text/plain',
+			'size' => 3
+		]);
+		$id3 = $cache->put('bar/foo/older.txt', [
+			'storage_mtime' => $baseTime - 600,
+			'mtime' => $baseTime - 600,
+			'mimetype' => 'text/plain',
+			'size' => 3
+		]);
+
+		$node = new \OC\Files\Node\Folder($root, $view, $folderPath, $folderInfo);
+
+
+		$nodes = $node->getRecent(5);
+		$ids = array_map(function (Node $node) {
+			return (int)$node->getId();
+		}, $nodes);
+		$this->assertEquals([$id1, $id2, $id3], $ids);
+	}
+
+	public function testRecentFolder() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		$folderPath = '/bar/foo';
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */
+		$root = $this->getMock('\OC\Files\Node\Root', array('getUser', 'getMountsIn', 'getMount'), array($manager, $view, $this->user));
+		/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */
+		$folderInfo = $this->getMockBuilder('\OC\Files\FileInfo')
+			->disableOriginalConstructor()->getMock();
+
+		$baseTime = 1000;
+		$storage = new Temporary();
+		$mount = new MountPoint($storage, '');
+
+		$folderInfo->expects($this->any())
+			->method('getMountPoint')
+			->will($this->returnValue($mount));
+
+		$cache = $storage->getCache();
+
+		$id1 = $cache->put('bar/foo/folder', [
+			'storage_mtime' => $baseTime,
+			'mtime' => $baseTime,
+			'mimetype' => \OCP\Files\FileInfo::MIMETYPE_FOLDER,
+			'size' => 3
+		]);
+		$id2 = $cache->put('bar/foo/folder/bar.txt', [
+			'storage_mtime' => $baseTime,
+			'mtime' => $baseTime,
+			'mimetype' => 'text/plain',
+			'size' => 3,
+			'parent' => $id1
+		]);
+		$id3 = $cache->put('bar/foo/folder/asd.txt', [
+			'storage_mtime' => $baseTime - 100,
+			'mtime' => $baseTime - 100,
+			'mimetype' => 'text/plain',
+			'size' => 3,
+			'parent' => $id1
+		]);
+
+		$node = new \OC\Files\Node\Folder($root, $view, $folderPath, $folderInfo);
+
+
+		$nodes = $node->getRecent(5);
+		$ids = array_map(function (Node $node) {
+			return (int)$node->getId();
+		}, $nodes);
+		$this->assertEquals([$id2, $id3], $ids);
+		$this->assertEquals($baseTime, $nodes[0]->getMTime());
+		$this->assertEquals($baseTime - 100, $nodes[1]->getMTime());
+	}
+
+	public function testRecentJail() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		$folderPath = '/bar/foo';
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */
+		$root = $this->getMock('\OC\Files\Node\Root', array('getUser', 'getMountsIn', 'getMount'), array($manager, $view, $this->user));
+		/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */
+		$folderInfo = $this->getMockBuilder('\OC\Files\FileInfo')
+			->disableOriginalConstructor()->getMock();
+
+		$baseTime = 1000;
+		$storage = new Temporary();
+		$jail = new Jail([
+			'storage' => $storage,
+			'root' => 'folder'
+		]);
+		$mount = new MountPoint($jail, '/bar/foo');
+
+		$folderInfo->expects($this->any())
+			->method('getMountPoint')
+			->will($this->returnValue($mount));
+
+		$cache = $storage->getCache();
+
+		$id1 = $cache->put('folder/inside.txt', [
+			'storage_mtime' => $baseTime,
+			'mtime' => $baseTime,
+			'mimetype' => 'text/plain',
+			'size' => 3
+		]);
+		$cache->put('outside.txt', [
+			'storage_mtime' => $baseTime - 100,
+			'mtime' => $baseTime - 100,
+			'mimetype' => 'text/plain',
+			'size' => 3
+		]);
+
+		$node = new \OC\Files\Node\Folder($root, $view, $folderPath, $folderInfo);
+
+		$nodes = $node->getRecent(5);
+		$ids = array_map(function (Node $node) {
+			return (int)$node->getId();
+		}, $nodes);
+		$this->assertEquals([$id1], $ids);
+	}
 }