diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 731dd7a23e7e1d985f762bfde6038302dfc6017e..0bcea2eceaf3c18c7c3a45229103ec84e4cb0b84 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -151,7 +151,13 @@ tr:hover span.extension {
 }
 
 table tr.mouseOver td { background-color:#eee; }
-table th { height:24px; padding:0 8px; color:#999; }
+table th { height:24px; padding:0 8px; }
+table th, table th a {
+	color: #999;
+}
+table.multiselect th a {
+	color: #000;
+}
 table th .columntitle {
 	display: inline-block;
 	padding: 15px;
diff --git a/apps/files/index.php b/apps/files/index.php
index e24c535cb208cab1488194ddadc29bb5e444b970..95ae7977eccf1ff125a5b56b430d468c6134a3b6 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -74,7 +74,12 @@ if (OC_App::isEnabled('files_encryption')) {
 
 $nav = new OCP\Template('files', 'appnavigation', '');
 
+function sortNavigationItems($item1, $item2) {
+	return $item1['order'] - $item2['order'];
+}
+
 $navItems = \OCA\Files\App::getNavigationManager()->getAll();
+usort($navItems, 'sortNavigationItems');
 $nav->assign('navigationItems', $navItems);
 
 $contentItems = array();
diff --git a/apps/files/js/app.js b/apps/files/js/app.js
index 9155fb38cdbeee986ac6d21194e92651c98d47e1..71802948a5ca70d9eaf9fb1aca8bcd07acc15830 100644
--- a/apps/files/js/app.js
+++ b/apps/files/js/app.js
@@ -24,20 +24,27 @@
 		initialize: function() {
 			this.navigation = new OCA.Files.Navigation($('#app-navigation'));
 
-			// TODO: ideally these should be in a separate class / app (the embedded "all files" app)
-			this.fileActions = OCA.Files.FileActions;
+			var fileActions = new OCA.Files.FileActions();
+			// default actions
+			fileActions.registerDefaultActions();
+			// legacy actions
+			fileActions.merge(window.FileActions);
+			// regular actions
+			fileActions.merge(OCA.Files.fileActions);
+
 			this.files = OCA.Files.Files;
 
+			// TODO: ideally these should be in a separate class / app (the embedded "all files" app)
 			this.fileList = new OCA.Files.FileList(
 				$('#app-content-files'), {
 					scrollContainer: $('#app-content'),
 					dragOptions: dragOptions,
-					folderDropOptions: folderDropOptions
+					folderDropOptions: folderDropOptions,
+					fileActions: fileActions,
+					allowLegacyActions: true
 				}
 			);
 			this.files.initialize();
-			this.fileActions.registerDefaultActions(this.fileList);
-			this.fileList.setFileActions(this.fileActions);
 
 			// for backward compatibility, the global FileList will
 			// refer to the one of the "files" view
@@ -57,6 +64,22 @@
 			return this.navigation.getActiveContainer();
 		},
 
+		/**
+		 * Sets the currently active view
+		 * @param viewId view id
+		 */
+		setActiveView: function(viewId, options) {
+			this.navigation.setActiveItem(viewId, options);
+		},
+
+		/**
+		 * Returns the view id of the currently active view
+		 * @return view id
+		 */
+		getActiveView: function() {
+			return this.navigation.getActiveItem();
+		},
+
 		/**
 		 * Setup events based on URL changes
 		 */
@@ -138,7 +161,7 @@
 })();
 
 $(document).ready(function() {
-	// wait for other apps/extensions to register their event handlers
+	// wait for other apps/extensions to register their event handlers and file actions
 	// in the "ready" clause
 	_.defer(function() {
 		OCA.Files.App.initialize();
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index 085195e961d6012b676c613f4e6d5f902dbc283d..3df62f37518c68dd1f9d73202e7299d4f8ebc7e8 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -11,11 +11,40 @@
 /* global trashBinApp */
 (function() {
 
-	var FileActions = {
+	/**
+	 * Construct a new FileActions instance
+	 */
+	var FileActions = function() {
+		this.initialize();
+	}
+	FileActions.prototype = {
 		actions: {},
 		defaults: {},
 		icons: {},
 		currentFile: null,
+		initialize: function() {
+			this.clear();
+		},
+		/**
+		 * Merges the actions from the given fileActions into
+		 * this instance.
+		 *
+		 * @param fileActions instance of OCA.Files.FileActions
+		 */
+		merge: function(fileActions) {
+			var self = this;
+			// merge first level to avoid unintended overwriting
+			_.each(fileActions.actions, function(sourceMimeData, mime) {
+				var targetMimeData = self.actions[mime];
+				if (!targetMimeData) {
+					targetMimeData = {};
+				}
+				self.actions[mime] = _.extend(targetMimeData, sourceMimeData);
+			});
+
+			this.defaults = _.extend(this.defaults, fileActions.defaults);
+			this.icons = _.extend(this.icons, fileActions.icons);
+		},
 		register: function (mime, name, permissions, icon, action, displayName) {
 			if (!this.actions[mime]) {
 				this.actions[mime] = {};
@@ -98,8 +127,13 @@
 		 * @param parent "td" element of the file for which to display actions
 		 * @param triggerEvent if true, triggers the fileActionsReady on the file
 		 * list afterwards (false by default)
+		 * @param fileList OCA.Files.FileList instance on which the action is
+		 * done, defaults to OCA.Files.App.fileList
 		 */
-		display: function (parent, triggerEvent) {
+		display: function (parent, triggerEvent, fileList) {
+			if (!fileList) {
+				console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance');
+			}
 			this.currentFile = parent;
 			var self = this;
 			var actions = this.getActions(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions());
@@ -120,9 +154,18 @@
 				event.preventDefault();
 
 				self.currentFile = event.data.elem;
+				// also set on global object for legacy apps
+				window.FileActions.currentFile = self.currentFile;
+
 				var file = self.getCurrentFile();
+				var $tr = $(this).closest('tr');
 
-				event.data.actionFunc(file);
+				event.data.actionFunc(file, {
+					$file: $tr,
+					fileList: fileList || OCA.Files.App.fileList,
+					fileActions: self,
+					dir: $tr.attr('data-path') || fileList.getCurrentDirectory()
+				});
 			};
 
 			var addAction = function (name, action, displayName) {
@@ -189,7 +232,7 @@
 			}
 
 			if (triggerEvent){
-				$('#fileList').trigger(jQuery.Event("fileActionsReady"));
+				fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList}));
 			}
 		},
 		getCurrentFile: function () {
@@ -208,29 +251,27 @@
 		/**
 		 * Register the actions that are used by default for the files app.
 		 */
-		registerDefaultActions: function(fileList) {
-			// TODO: try to find a way to not make it depend on fileList,
-			// maybe get a handler or listener to trigger events on
+		registerDefaultActions: function() {
 			this.register('all', 'Delete', OC.PERMISSION_DELETE, function () {
 				return OC.imagePath('core', 'actions/delete');
-			}, function (filename) {
-				fileList.do_delete(filename);
+			}, function (filename, context) {
+				context.fileList.do_delete(filename);
 				$('.tipsy').remove();
 			});
 
 			// t('files', 'Rename')
 			this.register('all', 'Rename', OC.PERMISSION_UPDATE, function () {
 				return OC.imagePath('core', 'actions/rename');
-			}, function (filename) {
-				fileList.rename(filename);
+			}, function (filename, context) {
+				context.fileList.rename(filename);
 			});
 
-			this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
-				var dir = fileList.getCurrentDirectory();
+			this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
+				var dir = context.fileList.getCurrentDirectory();
 				if (dir !== '/') {
 					dir = dir + '/';
 				}
-				fileList.changeDirectory(dir + filename);
+				context.fileList.changeDirectory(dir + filename);
 			});
 
 			this.setDefault('dir', 'Open');
@@ -243,20 +284,38 @@
 
 			this.register(downloadScope, 'Download', OC.PERMISSION_READ, function () {
 				return OC.imagePath('core', 'actions/download');
-			}, function (filename) {
-				var url = fileList.getDownloadUrl(filename, fileList.getCurrentDirectory());
+			}, function (filename, context) {
+				var dir = context.dir || context.fileList.getCurrentDirectory();
+				var url = context.fileList.getDownloadUrl(filename, dir);
 				if (url) {
 					OC.redirect(url);
 				}
 			});
-
-			fileList.$fileList.trigger(jQuery.Event("fileActionsReady"));
 		}
 	};
 
 	OCA.Files.FileActions = FileActions;
-})();
 
-// for backward compatibility
-window.FileActions = OCA.Files.FileActions;
+	// global file actions to be used by all lists
+	OCA.Files.fileActions = new OCA.Files.FileActions();
+	OCA.Files.legacyFileActions = new OCA.Files.FileActions();
+
+	// for backward compatibility
+	// 
+	// legacy apps are expecting a stateful global FileActions object to register
+	// their actions on. Since legacy apps are very likely to break with other
+	// FileList views than the main one ("All files"), actions registered
+	// through window.FileActions will be limited to the main file list.
+	window.FileActions = OCA.Files.legacyFileActions;
+	window.FileActions.register = function (mime, name, permissions, icon, action, displayName) {
+		console.warn('FileActions.register() is deprecated, please use OCA.Files.fileActions.register() instead', arguments);
+		OCA.Files.FileActions.prototype.register.call(
+				window.FileActions, mime, name, permissions, icon, action, displayName
+		);
+	};
+	window.FileActions.setDefault = function (mime, name) {
+		console.warn('FileActions.setDefault() is deprecated, please use OCA.Files.fileActions.setDefault() instead', mime, name);
+		OCA.Files.FileActions.prototype.setDefault.call(window.FileActions, mime, name);
+	};
+})();
 
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 72e1a688041b0cd29619c7e348295cd1fa438567..68b222071441c849a2bfd9aabe9f782a0d81f214 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -125,7 +125,7 @@
 			this.$container = options.scrollContainer || $(window);
 			this.$table = $el.find('table:first');
 			this.$fileList = $el.find('#fileList');
-			this.fileActions = OCA.Files.FileActions;
+			this._initFileActions(options.fileActions);
 			this.files = [];
 			this._selectedFiles = {};
 			this._selectionSummary = new OCA.Files.FileSummary();
@@ -168,6 +168,14 @@
 			this.$container.on('scroll', _.bind(this._onScroll, this));
 		},
 
+		_initFileActions: function(fileActions) {
+			this.fileActions = fileActions;
+			if (!this.fileActions) {
+				this.fileActions = new OCA.Files.FileActions();
+				this.fileActions.registerDefaultActions();
+			}
+		},
+
 		/**
 		 * Event handler for when the URL changed
 		 */
@@ -248,7 +256,14 @@
 					var action = this.fileActions.getDefault(mime,type, permissions);
 					if (action) {
 						event.preventDefault();
-						action(filename);
+						// also set on global object for legacy apps
+						window.FileActions.currentFile = this.fileActions.currentFile;
+						action(filename, {
+							$file: $tr,
+							fileList: this,
+							fileActions: this.fileActions,
+							dir: $tr.attr('data-path') || this.getCurrentDirectory()
+						});
 					}
 				}
 			}
@@ -448,7 +463,7 @@
 
 			while (count > 0 && index < this.files.length) {
 				fileData = this.files[index];
-				tr = this._renderRow(fileData, {updateSummary: false});
+				tr = this._renderRow(fileData, {updateSummary: false, silent: true});
 				this.$fileList.append(tr);
 				if (isAllSelected || this._selectedFiles[fileData.id]) {
 					tr.addClass('selected');
@@ -493,7 +508,7 @@
 			this.$el.find('thead').after(this.$fileList);
 
 			this.updateEmptyContent();
-			this.$fileList.trigger(jQuery.Event("fileActionsReady"));
+			this.$fileList.trigger($.Event('fileActionsReady', {fileList: this}));
 
 			this.fileSummary.calculate(filesArray);
 
@@ -515,6 +530,7 @@
 				type = fileData.type || 'file',
 				mtime = parseInt(fileData.mtime, 10) || new Date().getTime(),
 				mime = fileData.mimetype,
+				path = fileData.path,
 				linkUrl;
 			options = options || {};
 
@@ -534,6 +550,13 @@
 				"data-permissions": fileData.permissions || this.getDirectoryPermissions()
 			});
 
+			if (!_.isUndefined(path)) {
+				tr.attr('data-path', path);
+			}
+			else {
+				path = this.getCurrentDirectory();
+			}
+
 			if (type === 'dir') {
 				// use default folder icon
 				icon = icon || OC.imagePath('core', 'filetypes/folder');
@@ -550,10 +573,10 @@
 
 			// linkUrl
 			if (type === 'dir') {
-				linkUrl = this.linkTo(this.getCurrentDirectory() + '/' + name);
+				linkUrl = this.linkTo(path + '/' + name);
 			}
 			else {
-				linkUrl = this.getDownloadUrl(name, this.getCurrentDirectory());
+				linkUrl = this.getDownloadUrl(name, path);
 			}
 			td.append('<input id="select-' + this.id + '-' + fileData.id +
 				'" type="checkbox" /><label for="select-' + this.id + '-' + fileData.id + '"></label>');
@@ -621,7 +644,8 @@
 		 *
 		 * @param fileData map of file attributes
 		 * @param options map of attributes:
-		 * - "updateSummary" true to update the summary after adding (default), false otherwise
+		 * - "updateSummary": true to update the summary after adding (default), false otherwise
+		 * - "silent": true to prevent firing events like "fileActionsReady"
 		 * @return new tr element (not appended to the table)
 		 */
 		add: function(fileData, options) {
@@ -693,6 +717,7 @@
 			options = options || {};
 			var type = fileData.type || 'file',
 				mime = fileData.mimetype,
+				path = fileData.path || this.getCurrentDirectory(),
 				permissions = parseInt(fileData.permissions, 10) || 0;
 
 			if (fileData.isShareMountPoint) {
@@ -723,13 +748,13 @@
 			}
 
 			// display actions
-			this.fileActions.display(filenameTd, false);
+			this.fileActions.display(filenameTd, !options.silent, this);
 
 			if (fileData.isPreviewAvailable) {
 				// lazy load / newly inserted td ?
 				if (!fileData.icon) {
 					this.lazyLoadPreview({
-						path: this.getCurrentDirectory() + '/' + fileData.name,
+						path: path + '/' + fileData.name,
 						mime: mime,
 						etag: fileData.etag,
 						callback: function(url) {
@@ -740,7 +765,7 @@
 				else {
 					// set the preview URL directly
 					var urlSpec = {
-							file: this.getCurrentDirectory() + '/' + fileData.name,
+							file: path + '/' + fileData.name,
 							c: fileData.etag
 						};
 					var previewUrl = this.generatePreviewUrl(urlSpec);
@@ -783,13 +808,6 @@
 			return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
 		},
 
-		/**
-		 * Sets the file actions handler
-		 */
-		setFileActions: function(fileActions) {
-			this.fileActions = fileActions;
-		},
-
 		/**
 		 * Sets the current directory name and updates the breadcrumb.
 		 * @param targetDir directory to display
@@ -1213,16 +1231,16 @@
 								// reinsert row
 								self.files.splice(tr.index(), 1);
 								tr.remove();
-								self.add(fileInfo, {updateSummary: false});
-								self.$fileList.trigger($.Event('fileActionsReady'));
+								self.add(fileInfo, {updateSummary: false, silent: true});
+								self.$fileList.trigger($.Event('fileActionsReady', {fileList: self}));
 							}
 						});
 					} else {
 						// add back the old file info when cancelled
 						self.files.splice(tr.index(), 1);
 						tr.remove();
-						self.add(oldFileInfo, {updateSummary: false});
-						self.$fileList.trigger($.Event('fileActionsReady'));
+						self.add(oldFileInfo, {updateSummary: false, silent: true});
+						self.$fileList.trigger($.Event('fileActionsReady', {fileList: self}));
 					}
 				} catch (error) {
 					input.attr('title', error);
diff --git a/apps/files/tests/js/appSpec.js b/apps/files/tests/js/appSpec.js
index 0e9abad6989c9c84b04e82a532aaeee309bcd103..a9bbab03ecb4914be8abeb3fa31cf530d0eac7a8 100644
--- a/apps/files/tests/js/appSpec.js
+++ b/apps/files/tests/js/appSpec.js
@@ -41,6 +41,10 @@ describe('OCA.Files.App tests', function() {
 			'</div>'
 		);
 
+		window.FileActions = new OCA.Files.FileActions();
+		OCA.Files.legacyFileActions = window.FileActions;
+		OCA.Files.fileActions = new OCA.Files.FileActions();
+
 		pushStateStub = sinon.stub(OC.Util.History, 'pushState');
 		parseUrlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery');
 		parseUrlQueryStub.returns({});
@@ -51,8 +55,6 @@ describe('OCA.Files.App tests', function() {
 		App.navigation = null;
 		App.fileList = null;
 		App.files = null;
-		App.fileActions.clear();
-		App.fileActions = null;
 
 		pushStateStub.restore();
 		parseUrlQueryStub.restore();
@@ -64,6 +66,53 @@ describe('OCA.Files.App tests', function() {
 			expect(App.fileList.fileActions.actions.all).toBeDefined();
 			expect(App.fileList.$el.is('#app-content-files')).toEqual(true);
 		});
+		it('merges the legacy file actions with the default ones', function() {
+			var legacyActionStub = sinon.stub();
+			var actionStub = sinon.stub();
+			// legacy action
+			window.FileActions.register(
+					'all',
+					'LegacyTest',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					legacyActionStub
+			);
+			// legacy action to be overwritten
+			window.FileActions.register(
+					'all',
+					'OverwriteThis',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					legacyActionStub
+			);
+
+			// regular file actions
+			OCA.Files.fileActions.register(
+					'all',
+					'RegularTest',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					actionStub
+			);
+
+			// overwrite
+			OCA.Files.fileActions.register(
+					'all',
+					'OverwriteThis',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					actionStub
+			);
+
+			App.initialize();
+
+			var actions = App.fileList.fileActions.actions;
+			expect(actions.all.OverwriteThis.action).toBe(actionStub);
+			expect(actions.all.LegacyTest.action).toBe(legacyActionStub);
+			expect(actions.all.RegularTest.action).toBe(actionStub);
+			// default one still there
+			expect(actions.dir.Open.action).toBeDefined();
+		});
 	});
 
 	describe('URL handling', function() {
diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js
index 9152dbb58c30a6961f3265698f95d7d70b53827e..490594a17739f274c7fa6bfa3c1c485a3f618d42 100644
--- a/apps/files/tests/js/fileactionsSpec.js
+++ b/apps/files/tests/js/fileactionsSpec.js
@@ -21,7 +21,7 @@
 
 describe('OCA.Files.FileActions tests', function() {
 	var $filesTable, fileList;
-	var FileActions = OCA.Files.FileActions;
+	var FileActions; 
 
 	beforeEach(function() {
 		// init horrible parameters
@@ -31,10 +31,11 @@ describe('OCA.Files.FileActions tests', function() {
 		// dummy files table
 		$filesTable = $body.append('<table id="filestable"></table>');
 		fileList = new OCA.Files.FileList($('#testArea'));
-		FileActions.registerDefaultActions(fileList);
+		FileActions = new OCA.Files.FileActions();
+		FileActions.registerDefaultActions();
 	});
 	afterEach(function() {
-		FileActions.clear();
+		FileActions = null;
 		fileList = undefined;
 		$('#dir, #permissions, #filestable').remove();
 	});
@@ -78,8 +79,8 @@ describe('OCA.Files.FileActions tests', function() {
 		};
 		var $tr = fileList.add(fileData);
 
-		FileActions.display($tr.find('td.filename'), true);
-		FileActions.display($tr.find('td.filename'), true);
+		FileActions.display($tr.find('td.filename'), true, fileList);
+		FileActions.display($tr.find('td.filename'), true, fileList);
 
 		// actions defined after cal
 		expect($tr.find('.action.action-download').length).toEqual(1);
@@ -98,12 +99,39 @@ describe('OCA.Files.FileActions tests', function() {
 			mtime: '123456'
 		};
 		var $tr = fileList.add(fileData);
-		FileActions.display($tr.find('td.filename'), true);
+		FileActions.display($tr.find('td.filename'), true, fileList);
+
+		$tr.find('.action-download').click();
+
+		expect(redirectStub.calledOnce).toEqual(true);
+		expect(redirectStub.getCall(0).args[0]).toEqual(
+			OC.webroot +
+			'/index.php/apps/files/ajax/download.php' +
+			'?dir=%2Fsubdir&files=testName.txt');
+		redirectStub.restore();
+	});
+	it('takes the file\'s path into account when clicking download', function() {
+		var redirectStub = sinon.stub(OC, 'redirect');
+		var fileData = {
+			id: 18,
+			type: 'file',
+			name: 'testName.txt',
+			path: '/anotherpath/there',
+			mimetype: 'text/plain',
+			size: '1234',
+			etag: 'a01234c',
+			mtime: '123456'
+		};
+		var $tr = fileList.add(fileData);
+		FileActions.display($tr.find('td.filename'), true, fileList);
 
 		$tr.find('.action-download').click();
 
 		expect(redirectStub.calledOnce).toEqual(true);
-		expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=testName.txt');
+		expect(redirectStub.getCall(0).args[0]).toEqual(
+			OC.webroot + '/index.php/apps/files/ajax/download.php' +
+			'?dir=%2Fanotherpath%2Fthere&files=testName.txt'
+		);
 		redirectStub.restore();
 	});
 	it('deletes file when clicking delete', function() {
@@ -118,11 +146,47 @@ describe('OCA.Files.FileActions tests', function() {
 			mtime: '123456'
 		};
 		var $tr = fileList.add(fileData);
-		FileActions.display($tr.find('td.filename'), true);
+		FileActions.display($tr.find('td.filename'), true, fileList);
 
 		$tr.find('.action.delete').click();
 
 		expect(deleteStub.calledOnce).toEqual(true);
 		deleteStub.restore();
 	});
+	it('passes context to action handler', function() {
+		var actionStub = sinon.stub();
+		var fileData = {
+			id: 18,
+			type: 'file',
+			name: 'testName.txt',
+			mimetype: 'text/plain',
+			size: '1234',
+			etag: 'a01234c',
+			mtime: '123456'
+		};
+		var $tr = fileList.add(fileData);
+		FileActions.register(
+				'all',
+				'Test',
+				OC.PERMISSION_READ,
+				OC.imagePath('core', 'actions/test'),
+				actionStub
+		);
+		FileActions.display($tr.find('td.filename'), true, fileList);
+		$tr.find('.action-test').click();
+		expect(actionStub.calledOnce).toEqual(true);
+		expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
+		var context = actionStub.getCall(0).args[1];
+		expect(context.$file.is($tr)).toEqual(true);
+		expect(context.fileList).toBeDefined();
+		expect(context.fileActions).toBeDefined();
+		expect(context.dir).toEqual('/subdir');
+
+		// when data-path is defined
+		actionStub.reset();
+		$tr.attr('data-path', '/somepath');
+		$tr.find('.action-test').click();
+		context = actionStub.getCall(0).args[1];
+		expect(context.dir).toEqual('/somepath');
+	});
 });
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index a3dc5b255a15d142abd62416b337108a3f796bf8..3e9950dfe19fc60c57c4f2a8492156bb113bf02e 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -21,7 +21,6 @@
 
 describe('OCA.Files.FileList tests', function() {
 	var testFiles, alertStub, notificationStub, fileList;
-	var FileActions = OCA.Files.FileActions;
 
 	/**
 	 * Generate test file data
@@ -117,15 +116,11 @@ describe('OCA.Files.FileList tests', function() {
 		}];
 
 		fileList = new OCA.Files.FileList($('#app-content-files'));
-		FileActions.clear();
-		FileActions.registerDefaultActions(fileList);
-		fileList.setFileActions(FileActions);
 	});
 	afterEach(function() {
 		testFiles = undefined;
 		fileList = undefined;
 
-		FileActions.clear();
 		notificationStub.restore();
 		alertStub.restore();
 	});
@@ -488,7 +483,7 @@ describe('OCA.Files.FileList tests', function() {
 			var $input, request;
 
 			for (var i = 0; i < testFiles.length; i++) {
-				fileList.add(testFiles[i]);
+				fileList.add(testFiles[i], {silent: true});
 			}
 
 			// trigger rename prompt
@@ -753,6 +748,20 @@ describe('OCA.Files.FileList tests', function() {
 			fileList.setFiles(testFiles);
 			expect(handler.calledOnce).toEqual(true);
 		});
+		it('triggers "fileActionsReady" event after single add', function() {
+			var handler = sinon.stub();
+			fileList.setFiles(testFiles);
+			fileList.$fileList.on('fileActionsReady', handler);
+			fileList.add({name: 'test.txt'});
+			expect(handler.calledOnce).toEqual(true);
+		});
+		it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
+			var handler = sinon.stub();
+			fileList.setFiles(testFiles);
+			fileList.$fileList.on('fileActionsReady', handler);
+			fileList.add({name: 'test.txt'}, {silent: true});
+			expect(handler.notCalled).toEqual(true);
+		});
 		it('triggers "updated" event after update', function() {
 			var handler = sinon.stub();
 			fileList.$fileList.on('updated', handler);
@@ -1512,6 +1521,32 @@ describe('OCA.Files.FileList tests', function() {
 			expect(fileList.getSelectedFiles()).toEqual([]);
 		});
 	});
+	describe('File actions', function() {
+		it('Clicking on a file name will trigger default action', function() {
+			var actionStub = sinon.stub();
+			fileList.setFiles(testFiles);
+			fileList.fileActions.register(
+				'text/plain',
+				'Test',
+				OC.PERMISSION_ALL,
+				function() {
+					// Specify icon for hitory button
+					return OC.imagePath('core','actions/history');
+				},
+				actionStub
+			);
+			fileList.fileActions.setDefault('text/plain', 'Test');
+			var $tr = fileList.findFileEl('One.txt');
+			$tr.find('td.filename>a.name').click();
+			expect(actionStub.calledOnce).toEqual(true);
+			expect(actionStub.getCall(0).args[0]).toEqual('One.txt');
+			var context = actionStub.getCall(0).args[1];
+			expect(context.$file.is($tr)).toEqual(true);
+			expect(context.fileList).toBeDefined();
+			expect(context.fileActions).toBeDefined();
+			expect(context.dir).toEqual('/subdir');
+		});
+	});
 	describe('Sorting files', function() {
 		it('Sorts by name by default', function() {
 			fileList.reload();
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index fa43f33721c4606ba087356ee46954676221c125..21b2646c5eade7a57cde704acf06e6cc346c08a9 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -1,4 +1,5 @@
 <?php
+$l = OC_L10N::get('files_sharing');
 
 OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.php';
 OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php';
@@ -21,3 +22,22 @@ OCP\Util::addScript('files_sharing', 'share');
 \OC_Hook::connect('OC_Appconfig', 'post_set_value', '\OCA\Files\Share\Maintainer', 'configChangeHook');
 
 OC_FileProxy::register(new OCA\Files\Share\Proxy());
+
+\OCA\Files\App::getNavigationManager()->add(
+	array(
+		"id" => 'sharingin',
+		"appname" => 'files_sharing',
+		"script" => 'list.php',
+		"order" => 10,
+		"name" => $l->t('Shared with you')
+	)
+);
+\OCA\Files\App::getNavigationManager()->add(
+	array(
+		"id" => 'sharingout',
+		"appname" => 'files_sharing',
+		"script" => 'list.php',
+		"order" => 15,
+		"name" => $l->t('Shared with others')
+	)
+);
diff --git a/apps/files_sharing/css/sharedfilelist.css b/apps/files_sharing/css/sharedfilelist.css
new file mode 100644
index 0000000000000000000000000000000000000000..6b0c7d2226ed88fa8555a20c5844573cdc1ce1f4
--- /dev/null
+++ b/apps/files_sharing/css/sharedfilelist.css
@@ -0,0 +1,3 @@
+#filestable.shareList .summary .filesize {
+	display: none;
+}
diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..3764328a5d04624107538433a2f272440d19e316
--- /dev/null
+++ b/apps/files_sharing/js/app.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.
+ *
+ */
+
+OCA.Sharing = {};
+OCA.Sharing.App = {
+
+	_inFileList: null,
+	_outFileList: null,
+
+	initSharingIn: function($el) {
+		if (this._inFileList) {
+			return this._inFileList;
+		}
+
+		this._inFileList = new OCA.Sharing.FileList(
+			$el,
+			{
+				scrollContainer: $('#app-content'),
+				sharedWithUser: true,
+				fileActions: this._createFileActions()
+			}
+		);
+
+		this._extendFileList(this._inFileList);
+		this._inFileList.appName = t('files_sharing', 'Shared with you');
+		this._inFileList.$el.find('#emptycontent').text(t('files_sharing', 'No files have been shared with you yet.'));
+		return this._inFileList;
+	},
+
+	initSharingOut: function($el) {
+		if (this._outFileList) {
+			return this._outFileList;
+		}
+		this._outFileList = new OCA.Sharing.FileList(
+			$el,
+			{
+				scrollContainer: $('#app-content'),
+				sharedWithUser: false,
+				fileActions: this._createFileActions()
+			}
+		);
+
+		this._extendFileList(this._outFileList);
+		this._outFileList.appName = t('files_sharing', 'Shared with others');
+		this._outFileList.$el.find('#emptycontent').text(t('files_sharing', 'You haven\'t shared any files yet.'));
+		return this._outFileList;
+	},
+
+	removeSharingIn: function() {
+		if (this._inFileList) {
+			this._inFileList.$fileList.empty();
+		}
+	},
+
+	removeSharingOut: function() {
+		if (this._outFileList) {
+			this._outFileList.$fileList.empty();
+		}
+	},
+
+	_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);
+
+		// 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});
+			OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true);
+		});
+		fileActions.setDefault('dir', 'Open');
+		return fileActions;
+	},
+
+	_extendFileList: function(fileList) {
+		// remove size column from summary
+		fileList.fileSummary.$el.find('.filesize').remove();
+	}
+};
+
+$(document).ready(function() {
+	$('#app-content-sharingin').on('show', function(e) {
+		OCA.Sharing.App.initSharingIn($(e.target));
+	});
+	$('#app-content-sharingin').on('hide', function() {
+		OCA.Sharing.App.removeSharingIn();
+	});
+	$('#app-content-sharingout').on('show', function(e) {
+		OCA.Sharing.App.initSharingOut($(e.target));
+	});
+	$('#app-content-sharingout').on('hide', function() {
+		OCA.Sharing.App.removeSharingOut();
+	});
+});
+
diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js
index d825ee9de15b227c2475192fc5e525553afc8b53..27e8d361ff9bd9d5387c88c35a77846a385d72dd 100644
--- a/apps/files_sharing/js/public.js
+++ b/apps/files_sharing/js/public.js
@@ -19,9 +19,18 @@ OCA.Sharing.PublicApp = {
 
 	initialize: function($el) {
 		var self = this;
+		var fileActions;
 		if (this._initialized) {
 			return;
 		}
+		fileActions = new OCA.Files.FileActions();
+		// default actions
+		fileActions.registerDefaultActions();
+		// legacy actions
+		fileActions.merge(window.FileActions);
+		// regular actions
+		fileActions.merge(OCA.Files.fileActions);
+
 		this._initialized = true;
 		this.initialDir = $('#dir').val();
 
@@ -32,7 +41,8 @@ OCA.Sharing.PublicApp = {
 				{
 					scrollContainer: $(window),
 					dragOptions: dragOptions,
-					folderDropOptions: folderDropOptions
+					folderDropOptions: folderDropOptions,
+					fileActions: fileActions
 				}
 			);
 			this.files = OCA.Files.Files;
@@ -121,10 +131,8 @@ OCA.Sharing.PublicApp = {
 				};
 			});
 
-			this.fileActions = _.extend({}, OCA.Files.FileActions);
-			this.fileActions.registerDefaultActions(this.fileList);
-			delete this.fileActions.actions.all.Share;
-			this.fileList.setFileActions(this.fileActions);
+			// do not allow sharing from the public page
+			delete this.fileList.fileActions.actions.all.Share;
 
 			this.fileList.changeDirectory(this.initialDir || '/', false, true);
 
@@ -158,7 +166,10 @@ OCA.Sharing.PublicApp = {
 
 $(document).ready(function() {
 	var App = OCA.Sharing.PublicApp;
-	App.initialize($('#preview'));
+	// defer app init, to give a chance to plugins to register file actions
+	_.defer(function() {
+		App.initialize($('#preview'));
+	});
 
 	if (window.Files) {
 		// HACK: for oc-dialogs previews that depends on Files:
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 84c5bf57b38b457c6fa6d8c5254147a3ae18066f..5a42604c866dbc3c10d84a80f4897ac22e2fc783 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -8,12 +8,8 @@
  *
  */
 
-/* global FileList, FileActions */
 $(document).ready(function() {
-
-	var sharesLoaded = false;
-
-	if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined') {
+	if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) {
 		// TODO: make a separate class for this or a hook or jQuery event ?
 		if (OCA.Files.FileList) {
 			var oldCreateRow = OCA.Files.FileList.prototype._createRow;
@@ -31,10 +27,12 @@ $(document).ready(function() {
 			};
 		}
 
-		$('#fileList').on('fileActionsReady',function(){
+		// use delegate to catch the case with multiple file lists
+		$('#content').delegate('#fileList', 'fileActionsReady',function(ev){
 			// if no share action exists because the admin disabled sharing for this user
 			// we create a share notification action to inform the user about files
 			// shared with him otherwise we just update the existing share action.
+			var fileList = ev.fileList;
 			var $fileList = $(this);
 			$fileList.find('[data-share-owner]').each(function() {
 				var $tr = $(this);
@@ -62,46 +60,50 @@ $(document).ready(function() {
 						return $result;
 					});
 				}
-			})
+			});
 
-			// FIXME: these calls are also working on hard-coded
-			// list selectors...
-			if (!sharesLoaded){
-				OC.Share.loadIcons('file');
+			if (!OCA.Sharing.sharesLoaded){
+				OC.Share.loadIcons('file', fileList);
 				// assume that we got all shares, so switching directories
 				// will not invalidate that list
-				sharesLoaded = true;
+				OCA.Sharing.sharesLoaded = true;
 			}
 			else{
-				OC.Share.updateIcons('file');
+				OC.Share.updateIcons('file', fileList);
 			}
 		});
 
-		FileActions.register('all', 'Share', OC.PERMISSION_SHARE, OC.imagePath('core', 'actions/share'), function(filename) {
-			var tr = FileList.findFileEl(filename);
+		OCA.Files.fileActions.register(
+				'all',
+				'Share',
+				OC.PERMISSION_SHARE,
+				OC.imagePath('core', 'actions/share'),
+				function(filename, context) {
+
+			var $tr = context.$file;
 			var itemType = 'file';
-			if ($(tr).data('type') == 'dir') {
+			if ($tr.data('type') === 'dir') {
 				itemType = 'folder';
 			}
-			var possiblePermissions = $(tr).data('reshare-permissions');
+			var possiblePermissions = $tr.data('reshare-permissions');
 			if (_.isUndefined(possiblePermissions)) {
-				possiblePermissions = $(tr).data('permissions');
+				possiblePermissions = $tr.data('permissions');
 			}
 
-			var appendTo = $(tr).find('td.filename');
+			var appendTo = $tr.find('td.filename');
 			// Check if drop down is already visible for a different file
 			if (OC.Share.droppedDown) {
-				if ($(tr).data('id') != $('#dropdown').attr('data-item-source')) {
+				if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) {
 					OC.Share.hideDropDown(function () {
-						$(tr).addClass('mouseOver');
-						OC.Share.showDropDown(itemType, $(tr).data('id'), appendTo, true, possiblePermissions, filename);
+						$tr.addClass('mouseOver');
+						OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
 					});
 				} else {
 					OC.Share.hideDropDown();
 				}
 			} else {
-				$(tr).addClass('mouseOver');
-				OC.Share.showDropDown(itemType, $(tr).data('id'), appendTo, true, possiblePermissions, filename);
+				$tr.addClass('mouseOver');
+				OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
 			}
 		});
 	}
diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef1034ecfdc874b97a0334a07d351f337bb6df8b
--- /dev/null
+++ b/apps/files_sharing/js/sharedfilelist.js
@@ -0,0 +1,235 @@
+/*
+ * 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() {
+
+	/**
+	 * Sharing file list
+	 *
+	 * Contains both "shared with others" and "shared with you" modes.
+	 */
+	var FileList = function($el, options) {
+		this.initialize($el, options);
+	};
+
+	FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, {
+		appName: 'Shares',
+
+		/**
+		 * Whether the list shows the files shared with the user (true) or
+		 * the files that the user shared with others (false).
+		 */
+		_sharedWithUser: false,
+
+		initialize: function($el, options) {
+			OCA.Files.FileList.prototype.initialize.apply(this, arguments);
+			if (this.initialized) {
+				return;
+			}
+
+			if (options && options.sharedWithUser) {
+				this._sharedWithUser = true;
+			}
+		},
+
+		_createRow: function(fileData) {
+			// TODO: hook earlier and render the whole row here
+			var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments);
+			$tr.find('.filesize').remove();
+			$tr.find('td.date').before($tr.children('td:first'));
+			$tr.find('td.filename input:checkbox').remove();
+			$tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(','));
+			if (this._sharedWithUser) {
+				$tr.attr('data-share-owner', fileData.shares[0].ownerDisplayName);
+			}
+			return $tr;
+		},
+
+		/**
+		 * Set whether the list should contain outgoing shares
+		 * or incoming shares.
+		 *
+		 * @param state true for incoming shares, false otherwise
+		 */
+		setSharedWithUser: function(state) {
+			this._sharedWithUser = !!state;
+		},
+
+		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() {
+			var self = this;
+			this.showMask();
+			if (this._reloadCall) {
+				this._reloadCall.abort();
+			}
+			this._reloadCall = $.ajax({
+				url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares',
+				/* jshint camelcase: false */
+				data: {
+					format: 'json',
+					shared_with_me: !!this._sharedWithUser
+				},
+				type: 'GET',
+				beforeSend: function(xhr) {
+					xhr.setRequestHeader('OCS-APIREQUEST', 'true');
+				},
+				error: function(result) {
+					self.reloadCallback(result);
+				},
+				success: function(result) {
+					self.reloadCallback(result);
+				}
+			});
+		},
+
+		reloadCallback: function(result) {
+			delete this._reloadCall;
+			this.hideMask();
+
+			this.$el.find('#headerSharedWith').text(
+				t('files_sharing', this._sharedWithUser ? 'Shared by' : 'Shared with')
+			);
+			if (result.ocs && result.ocs.data) {
+				this.setFiles(this._makeFilesFromShares(result.ocs.data));
+			}
+			else {
+				// TODO: error handling
+			}
+		},
+
+		/**
+		 * Converts the OCS API share response data to a file info
+		 * list
+		 * @param OCS API share array
+		 * @return array of file info maps
+		 */
+		_makeFilesFromShares: function(data) {
+			var self = this;
+			// OCS API uses non-camelcased names
+			var files = _.chain(data)
+				// convert share data to file data
+				.map(function(share) {
+					/* jshint camelcase: false */
+					var file = {
+						id: share.file_source,
+						mimetype: share.mimetype
+					};
+					if (share.item_type === 'folder') {
+						file.type = 'dir';
+						file.mimetype = 'httpd/unix-directory';
+					}
+					else {
+						file.type = 'file';
+						// force preview retrieval as we don't have mime types,
+						// the preview endpoint will fall back to the mime type
+						// icon if no preview exists
+						file.isPreviewAvailable = true;
+						file.icon = true;
+					}
+					file.share = {
+						id: share.id,
+						type: share.share_type,
+						target: share.share_with,
+						stime: share.stime * 1000,
+					};
+					if (self._sharedWithUser) {
+						file.share.ownerDisplayName = share.displayname_owner;
+						file.name = OC.basename(share.file_target);
+						file.path = OC.dirname(share.file_target);
+						file.permissions = share.permissions;
+					}
+					else {
+						file.share.targetDisplayName = share.share_with_displayname;
+						file.name = OC.basename(share.path);
+						file.path = OC.dirname(share.path);
+						file.permissions = OC.PERMISSION_ALL;
+					}
+					return file;
+				})
+				// Group all files and have a "shares" array with
+				// the share info for each file.
+				//
+				// This uses a hash memo to cumulate share information
+				// inside the same file object (by file id).
+				.reduce(function(memo, file) {
+					var data = memo[file.id];
+					var counterPart = file.share.ownerDisplayName || file.share.targetDisplayName;
+					if (!data) {
+						data = memo[file.id] = file;
+						data.shares = [file.share];
+						// using a hash to make them unique,
+						// this is only a list to be displayed
+						data.counterParts = {};
+						// counter is cheaper than calling _.keys().length
+						data.counterPartsCount = 0;
+						data.mtime = file.share.stime;
+					}
+					else {
+						// always take the most recent stime
+						if (file.share.stime > data.mtime) {
+							data.mtime = file.share.stime;
+						}
+						data.shares.push(file.share);
+					}
+
+					if (file.share.type === OC.Share.SHARE_TYPE_LINK) {
+						data.hasLinkShare = true;
+					} else if (counterPart && data.counterPartsCount < 10) {
+						// limit counterparts for output
+						data.counterParts[counterPart] = true;
+						data.counterPartsCount++;
+					}
+
+					delete file.share;
+					return memo;
+				}, {})
+				// Retrieve only the values of the returned hash
+				.values()
+				// Clean up
+				.each(function(data) {
+					// convert the counterParts map to a flat
+					// array of sorted names
+					data.counterParts = _.chain(data.counterParts).keys().sort().value();
+					if (data.hasLinkShare) {
+						data.counterParts.unshift(t('files_sharing', 'link'));
+						delete data.hasLinkShare;
+					}
+					delete data.counterPartsCount;
+				})
+				// Sort by expected sort comparator
+				.sortBy(this._sortComparator)
+				// Finish the chain by getting the result
+				.value();
+
+			return files;
+		}
+	});
+
+	OCA.Sharing.FileList = FileList;
+})();
diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php
index 21fd5d00a4ce227c1c79f2093323e1824d610e6a..dc4e5cf6c49f03ad1b69782f49fcd92cf4c5ddf0 100644
--- a/apps/files_sharing/lib/api.php
+++ b/apps/files_sharing/lib/api.php
@@ -31,6 +31,9 @@ class Api {
 	 * @return \OC_OCS_Result share information
 	 */
 	public static function getAllShares($params) {
+		if (isset($_GET['shared_with_me']) && $_GET['shared_with_me'] !== 'false') {
+				return self::getFilesSharedWithMe();
+			}
 		// if a file is specified, get the share for this file
 		if (isset($_GET['path'])) {
 			$params['itemSource'] = self::getFileId($_GET['path']);
@@ -49,12 +52,20 @@ class Api {
 			return self::collectShares($params);
 		}
 
-		$share = \OCP\Share::getItemShared('file', null);
+		$shares = \OCP\Share::getItemShared('file', null);
 
-		if ($share === false) {
+		if ($shares === false) {
 			return new \OC_OCS_Result(null, 404, 'could not get shares');
 		} else {
-			return new \OC_OCS_Result($share);
+			foreach ($shares as &$share) {
+				// file_target might not be set if the target user hasn't mounted
+				// the filesystem yet
+				if ($share['item_type'] === 'file' && isset($share['file_target'])) {
+					$share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']);
+				}
+				$newShares[] = $share;
+			}
+			return new \OC_OCS_Result($shares);
 		}
 
 	}
@@ -195,6 +206,27 @@ class Api {
 		return new \OC_OCS_Result($result);
 	}
 
+	/**
+	 * get files shared with the user
+	 * @return \OC_OCS_Result
+	 */
+	private static function getFilesSharedWithMe() {
+		try	{
+			$shares = \OCP\Share::getItemsSharedWith('file');
+			foreach ($shares as &$share) {
+				if ($share['item_type'] === 'file') {
+					$share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']);
+				}
+			}
+			$result = new \OC_OCS_Result($shares);
+		} catch (\Exception $e) {
+			$result = new \OC_OCS_Result(null, 403, $e->getMessage());
+		}
+
+		return $result;
+
+	}
+
 	/**
 	 * create a new share
 	 * @param array $params
diff --git a/apps/files_sharing/list.php b/apps/files_sharing/list.php
new file mode 100644
index 0000000000000000000000000000000000000000..bad690ea95f9dd05a50387a1e03cc59a95e5bb72
--- /dev/null
+++ b/apps/files_sharing/list.php
@@ -0,0 +1,11 @@
+<?php
+
+// Check if we are a user
+OCP\User::checkLoggedIn();
+
+$tmpl = new OCP\Template('files_sharing', 'list', '');
+
+OCP\Util::addScript('files_sharing', 'app');
+OCP\Util::addScript('files_sharing', 'sharedfilelist');
+
+$tmpl->printPage();
diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php
new file mode 100644
index 0000000000000000000000000000000000000000..a1d95ebc1f115d01efa9ae8820c3794afcb01ce9
--- /dev/null
+++ b/apps/files_sharing/templates/list.php
@@ -0,0 +1,28 @@
+<?php /** @var $l OC_L10N */ ?>
+<div id="controls">
+	<div id="file_action_panel"></div>
+</div>
+<div id='notification'></div>
+
+<div id="emptycontent" class="hidden"></div>
+
+<input type="hidden" name="dir" value="" id="dir">
+
+<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><span class="sort-indicator"></span></a>
+				</div>
+			</th>
+			<th id="headerDate" class="hidden column-mtime">
+				<a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Share time' )); ?></span><span class="sort-indicator"></span></a>
+			</th>
+		</tr>
+	</thead>
+	<tbody id="fileList">
+	</tbody>
+	<tfoot>
+	</tfoot>
+</table>
diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad95ee5394251157121f25fc6a3bceaea8f3a503
--- /dev/null
+++ b/apps/files_sharing/tests/js/appSpec.js
@@ -0,0 +1,143 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2014 Vincent Petry <pvince81@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/>.
+*
+*/
+
+describe('OCA.Sharing.App tests', function() {
+	var App = OCA.Sharing.App;
+	var fileListIn;
+	var fileListOut;
+
+	beforeEach(function() {
+		$('#testArea').append(
+			'<div id="app-navigation">' +
+			'<ul><li data-id="files"><a>Files</a></li>' +
+			'<li data-id="sharingin"><a></a></li>' +
+			'<li data-id="sharingout"><a></a></li>' +
+			'</ul></div>' +
+			'<div id="app-content">' +
+			'<div id="app-content-files" class="hidden">' +
+			'</div>' +
+			'<div id="app-content-sharingin" class="hidden">' +
+			'</div>' +
+			'<div id="app-content-sharingout" class="hidden">' +
+			'</div>' +
+			'</div>' +
+			'</div>'
+		);
+		fileListIn = App.initSharingIn($('#app-content-sharingin'));
+		fileListOut = App.initSharingOut($('#app-content-sharingout'));
+	});
+	afterEach(function() {
+		App._inFileList = null;
+		App._outFileList = null;
+		fileListIn = null;
+		fileListOut = null;
+	});
+
+	describe('initialization', function() {
+		it('inits sharing-in list on show', function() {
+			expect(fileListIn._sharedWithUser).toEqual(true);		
+		});
+		it('inits sharing-out list on show', function() {
+			expect(fileListOut._sharedWithUser).toBeFalsy();
+		});
+	});
+	describe('file actions', function() {
+		it('provides default file actions', function() {
+			_.each([fileListIn, fileListOut], function(fileList) {
+				var fileActions = fileList.fileActions;
+
+				expect(fileActions.actions.all).toBeDefined();
+				expect(fileActions.actions.all.Delete).toBeDefined();
+				expect(fileActions.actions.all.Rename).toBeDefined();
+				expect(fileActions.actions.file.Download).toBeDefined();
+
+				expect(fileActions.defaults.dir).toEqual('Open');
+			});
+		});
+		it('provides custom file actions', function() {
+			var actionStub = sinon.stub();
+			// regular file action
+			OCA.Files.fileActions.register(
+					'all',
+					'RegularTest',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/shared'),
+					actionStub
+			);
+
+			App._inFileList = null;
+			fileListIn = App.initSharingIn($('#app-content-sharingin'));
+
+			expect(fileListIn.fileActions.actions.all.RegularTest).toBeDefined();
+		});
+		it('does not provide legacy file actions', function() {
+			var actionStub = sinon.stub();
+			// legacy file action
+			window.FileActions.register(
+					'all',
+					'LegacyTest',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/shared'),
+					actionStub
+			);
+
+			App._inFileList = null;
+			fileListIn = App.initSharingIn($('#app-content-sharingin'));
+
+			expect(fileListIn.fileActions.actions.all.LegacyTest).not.toBeDefined();
+		});
+		it('redirects to files app when opening a directory', function() {
+			var oldList = OCA.Files.App.fileList;
+			// dummy new list to make sure it exists
+			OCA.Files.App.fileList = new OCA.Files.FileList($('<table><thead></thead><tbody></tbody></table>'));
+
+			var setActiveViewStub = sinon.stub(OCA.Files.App, 'setActiveView');
+			// create dummy table so we can click the dom
+			var $table = '<table><thead></thead><tbody id="fileList"></tbody></table>';
+			$('#app-content-sharingin').append($table);
+
+			App._inFileList = null;
+			fileListIn = App.initSharingIn($('#app-content-sharingin'));
+
+			fileListIn.setFiles([{
+				name: 'testdir',
+				type: 'dir',
+				path: '/somewhere/inside/subdir',
+				counterParts: ['user2'],
+				shares: [{
+					ownerDisplayName: 'user2'
+				}]
+			}]);
+
+			fileListIn.findFileEl('testdir').find('td a.name').click();
+
+			expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir');
+
+			expect(setActiveViewStub.calledOnce).toEqual(true);
+			expect(setActiveViewStub.calledWith('files')).toEqual(true);
+
+			setActiveViewStub.restore();
+
+			// restore old list
+			OCA.Files.App.fileList = oldList;
+		});
+	});
+});
diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js
new file mode 100644
index 0000000000000000000000000000000000000000..7aec8322a4467a4b3ec25880a5155a0e6ce61220
--- /dev/null
+++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js
@@ -0,0 +1,412 @@
+/*
+ * 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.
+ *
+ */
+
+describe('OCA.Sharing.FileList tests', function() {
+	var testFiles, alertStub, notificationStub, fileList;
+
+	beforeEach(function() {
+		alertStub = sinon.stub(OC.dialogs, 'alert');
+		notificationStub = sinon.stub(OC.Notification, 'show');
+
+		// init parameters and test table elements
+		$('#testArea').append(
+			'<div id="app-content-container">' +
+			// init horrible parameters
+			'<input type="hidden" id="dir" value="/"></input>' +
+			'<input type="hidden" id="permissions" value="31"></input>' +
+			// dummy controls
+			'<div id="controls">' +
+			'   <div class="actions creatable"></div>' +
+			'   <div class="notCreatable"></div>' +
+			'</div>' +
+			// dummy table
+			// TODO: at some point this will be rendered by the fileList class itself!
+			'<table id="filestable">' +
+			'<thead><tr>' +
+			'<th id="headerName" class="hidden column-name">' +
+			'<input type="checkbox" id="select_all_files" class="select-all">' +
+			'<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
+			'<span class="selectedActions hidden">' +
+			'</th>' +
+			'<th class="hidden column-mtime">' +
+			'<a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a>' +
+			'</th>' +
+			'</tr></thead>' +
+			'<tbody id="fileList"></tbody>' +
+			'<tfoot></tfoot>' +
+			'</table>' +
+			'<div id="emptycontent">Empty content message</div>' +
+			'</div>'
+		);
+	});
+	afterEach(function() {
+		testFiles = undefined;
+		fileList = undefined;
+
+		notificationStub.restore();
+		alertStub.restore();
+	});
+
+	describe('loading file list for incoming shares', function() {
+		var ocsResponse;
+
+		beforeEach(function() {
+			fileList = new OCA.Sharing.FileList(
+				$('#app-content-container'), {
+					sharedWithUser: true
+				}
+			);
+
+			fileList.reload();
+
+			/* jshint camelcase: false */
+			ocsResponse = {
+				ocs: {
+					meta: {
+						status: 'ok',
+						statuscode: 100,
+						message: null
+					},
+					data: [{
+						id: 7,
+						item_type: 'file',
+						item_source: 49,
+						item_target: '/49',
+						file_source: 49,
+						file_target: '/local path/local name.txt',
+						path: 'files/something shared.txt',
+						permissions: 31,
+						stime: 11111,
+						share_type: OC.Share.SHARE_TYPE_USER,
+						share_with: 'user1',
+						share_with_displayname: 'User One',
+						mimetype: 'text/plain',
+						uid_owner: 'user2',
+						displayname_owner: 'User Two'
+					}]
+				}
+			};
+		});
+		it('render file shares', function() {
+			var request;
+
+			expect(fakeServer.requests.length).toEqual(1);
+			request = fakeServer.requests[0];
+			expect(request.url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1') +
+				'shares?format=json&shared_with_me=true'
+			);
+
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify(ocsResponse)
+			);
+
+			var $rows = fileList.$el.find('tbody tr');
+			var $tr = $rows.eq(0);
+			expect($rows.length).toEqual(1);
+			expect($tr.attr('data-id')).toEqual('49');
+			expect($tr.attr('data-type')).toEqual('file');
+			expect($tr.attr('data-file')).toEqual('local name.txt');
+			expect($tr.attr('data-path')).toEqual('/local path');
+			expect($tr.attr('data-size')).not.toBeDefined();
+			expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
+			expect($tr.attr('data-mime')).toEqual('text/plain');
+			expect($tr.attr('data-mtime')).toEqual('11111000');
+			expect($tr.attr('data-share-owner')).toEqual('User Two');
+			expect($tr.attr('data-share-id')).toEqual('7');
+			expect($tr.find('a.name').attr('href')).toEqual(
+				OC.webroot +
+				'/index.php/apps/files/ajax/download.php' +
+				'?dir=%2Flocal%20path&files=local%20name.txt'
+			);
+			expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
+		});
+		it('render folder shares', function() {
+			/* jshint camelcase: false */
+			var request;
+			ocsResponse.ocs.data[0] = _.extend(ocsResponse.ocs.data[0], {
+				item_type: 'folder',
+				file_target: '/local path/local name',
+				path: 'files/something shared',
+			});
+
+			expect(fakeServer.requests.length).toEqual(1);
+			request = fakeServer.requests[0];
+			expect(request.url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1') +
+				'shares?format=json&shared_with_me=true'
+			);
+
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify(ocsResponse)
+			);
+
+			var $rows = fileList.$el.find('tbody tr');
+			var $tr = $rows.eq(0);
+			expect($rows.length).toEqual(1);
+			expect($tr.attr('data-id')).toEqual('49');
+			expect($tr.attr('data-type')).toEqual('dir');
+			expect($tr.attr('data-file')).toEqual('local name');
+			expect($tr.attr('data-path')).toEqual('/local path');
+			expect($tr.attr('data-size')).not.toBeDefined();
+			expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
+			expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
+			expect($tr.attr('data-mtime')).toEqual('11111000');
+			expect($tr.attr('data-share-owner')).toEqual('User Two');
+			expect($tr.attr('data-share-id')).toEqual('7');
+			expect($tr.find('a.name').attr('href')).toEqual(
+				OC.webroot +
+				'/index.php/apps/files' +
+				'?dir=/local%20path/local%20name'
+			);
+			expect($tr.find('.nametext').text().trim()).toEqual('local name');
+		});
+	});
+	describe('loading file list for outgoing shares', function() {
+		var ocsResponse;
+
+		beforeEach(function() {
+			fileList = new OCA.Sharing.FileList(
+				$('#app-content-container'), {
+					sharedWithUser: false
+				}
+			);
+
+			fileList.reload();
+
+			/* jshint camelcase: false */
+			ocsResponse = {
+				ocs: {
+					meta: {
+						status: 'ok',
+						statuscode: 100,
+						message: null
+					},
+					data: [{
+						id: 7,
+						item_type: 'file',
+						item_source: 49,
+						file_source: 49,
+						path: '/local path/local name.txt',
+						permissions: 27,
+						stime: 11111,
+						share_type: OC.Share.SHARE_TYPE_USER,
+						share_with: 'user2',
+						share_with_displayname: 'User Two',
+						mimetype: 'text/plain',
+						uid_owner: 'user1',
+						displayname_owner: 'User One'
+					}]
+				}
+			};
+		});
+		it('render file shares', function() {
+			var request;
+
+			expect(fakeServer.requests.length).toEqual(1);
+			request = fakeServer.requests[0];
+			expect(request.url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1') +
+				'shares?format=json&shared_with_me=false'
+			);
+
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify(ocsResponse)
+			);
+
+			var $rows = fileList.$el.find('tbody tr');
+			var $tr = $rows.eq(0);
+			expect($rows.length).toEqual(1);
+			expect($tr.attr('data-id')).toEqual('49');
+			expect($tr.attr('data-type')).toEqual('file');
+			expect($tr.attr('data-file')).toEqual('local name.txt');
+			expect($tr.attr('data-path')).toEqual('/local path');
+			expect($tr.attr('data-size')).not.toBeDefined();
+			expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
+			expect($tr.attr('data-mime')).toEqual('text/plain');
+			expect($tr.attr('data-mtime')).toEqual('11111000');
+			expect($tr.attr('data-share-owner')).not.toBeDefined();
+			expect($tr.attr('data-share-id')).toEqual('7');
+			expect($tr.find('a.name').attr('href')).toEqual(
+				OC.webroot +
+				'/index.php/apps/files/ajax/download.php' +
+				'?dir=%2Flocal%20path&files=local%20name.txt'
+			);
+			expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
+		});
+		it('render folder shares', function() {
+			var request;
+			/* jshint camelcase: false */
+			ocsResponse.ocs.data[0] = _.extend(ocsResponse.ocs.data[0], {
+				item_type: 'folder',
+				path: '/local path/local name',
+			});
+
+			expect(fakeServer.requests.length).toEqual(1);
+			request = fakeServer.requests[0];
+			expect(request.url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1') +
+				'shares?format=json&shared_with_me=false'
+			);
+
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify(ocsResponse)
+			);
+
+			var $rows = fileList.$el.find('tbody tr');
+			var $tr = $rows.eq(0);
+			expect($rows.length).toEqual(1);
+			expect($tr.attr('data-id')).toEqual('49');
+			expect($tr.attr('data-type')).toEqual('dir');
+			expect($tr.attr('data-file')).toEqual('local name');
+			expect($tr.attr('data-path')).toEqual('/local path');
+			expect($tr.attr('data-size')).not.toBeDefined();
+			expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
+			expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
+			expect($tr.attr('data-mtime')).toEqual('11111000');
+			expect($tr.attr('data-share-owner')).not.toBeDefined();
+			expect($tr.attr('data-share-id')).toEqual('7');
+			expect($tr.find('a.name').attr('href')).toEqual(
+				OC.webroot +
+				'/index.php/apps/files' +
+				'?dir=/local%20path/local%20name'
+			);
+			expect($tr.find('.nametext').text().trim()).toEqual('local name');
+		});
+		it('render link shares', function() {
+			/* jshint camelcase: false */
+			var request;
+			ocsResponse.ocs.data[0] = {
+				id: 7,
+				item_type: 'file',
+				item_source: 49,
+				file_source: 49,
+				path: '/local path/local name.txt',
+				permissions: 1,
+				stime: 11111,
+				share_type: OC.Share.SHARE_TYPE_LINK,
+				share_with: null,
+				token: 'abc',
+				mimetype: 'text/plain',
+				uid_owner: 'user1',
+				displayname_owner: 'User One'
+			};
+			expect(fakeServer.requests.length).toEqual(1);
+			request = fakeServer.requests[0];
+			expect(request.url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1') +
+				'shares?format=json&shared_with_me=false'
+			);
+
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify(ocsResponse)
+			);
+
+			var $rows = fileList.$el.find('tbody tr');
+			var $tr = $rows.eq(0);
+			expect($rows.length).toEqual(1);
+			expect($tr.attr('data-id')).toEqual('49');
+			expect($tr.attr('data-type')).toEqual('file');
+			expect($tr.attr('data-file')).toEqual('local name.txt');
+			expect($tr.attr('data-path')).toEqual('/local path');
+			expect($tr.attr('data-size')).not.toBeDefined();
+			expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
+			expect($tr.attr('data-mime')).toEqual('text/plain');
+			expect($tr.attr('data-mtime')).toEqual('11111000');
+			expect($tr.attr('data-share-owner')).not.toBeDefined();
+			expect($tr.attr('data-share-id')).toEqual('7');
+			expect($tr.find('a.name').attr('href')).toEqual(
+					OC.webroot +
+					'/index.php/apps/files/ajax/download.php' +
+					'?dir=%2Flocal%20path&files=local%20name.txt');
+
+			expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
+		});
+		it('groups link shares with regular shares', function() {
+			/* jshint camelcase: false */
+			var request;
+			// link share
+			ocsResponse.ocs.data.push({
+				id: 8,
+				item_type: 'file',
+				item_source: 49,
+				file_source: 49,
+				path: '/local path/local name.txt',
+				permissions: 1,
+				stime: 11111,
+				share_type: OC.Share.SHARE_TYPE_LINK,
+				share_with: null,
+				token: 'abc',
+				mimetype: 'text/plain',
+				uid_owner: 'user1',
+				displayname_owner: 'User One'
+			});
+			// another share of the same file
+			ocsResponse.ocs.data.push({
+				id: 9,
+				item_type: 'file',
+				item_source: 49,
+				file_source: 49,
+				path: '/local path/local name.txt',
+				permissions: 27,
+				stime: 22222,
+				share_type: OC.Share.SHARE_TYPE_USER,
+				share_with: 'user3',
+				share_with_displayname: 'User Three',
+				mimetype: 'text/plain',
+				uid_owner: 'user1',
+				displayname_owner: 'User One'
+			});
+			expect(fakeServer.requests.length).toEqual(1);
+			request = fakeServer.requests[0];
+			expect(request.url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1') +
+				'shares?format=json&shared_with_me=false'
+			);
+
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify(ocsResponse)
+			);
+
+			var $rows = fileList.$el.find('tbody tr');
+			var $tr = $rows.eq(0);
+			expect($rows.length).toEqual(1);
+			expect($tr.attr('data-id')).toEqual('49');
+			expect($tr.attr('data-type')).toEqual('file');
+			expect($tr.attr('data-file')).toEqual('local name.txt');
+			expect($tr.attr('data-path')).toEqual('/local path');
+			expect($tr.attr('data-size')).not.toBeDefined();
+			expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
+			expect($tr.attr('data-mime')).toEqual('text/plain');
+			// always use the most recent stime
+			expect($tr.attr('data-mtime')).toEqual('22222000');
+			expect($tr.attr('data-share-owner')).not.toBeDefined();
+			expect($tr.attr('data-share-id')).toEqual('7,8,9');
+			expect($tr.find('a.name').attr('href')).toEqual(
+				OC.webroot +
+				'/index.php/apps/files/ajax/download.php' +
+				'?dir=%2Flocal%20path&files=local%20name.txt'
+			);
+			expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
+		});
+	});
+});
diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php
index b8900ee0de3a48cb781ae5dd4e630dcb57f04316..383115b8e634d241c2f9e8db8928eb03c0ca913d 100644
--- a/apps/files_trashbin/appinfo/app.php
+++ b/apps/files_trashbin/appinfo/app.php
@@ -9,7 +9,7 @@ array(
 	"id" => 'trashbin',
 	"appname" => 'files_trashbin',
 	"script" => 'list.php',
-	"order" => 1,
+	"order" => 50,
 	"name" => $l->t('Deleted files')
 )
 );
diff --git a/apps/files_trashbin/js/app.js b/apps/files_trashbin/js/app.js
index aa499ae1791b61660b4ea36b272bf419b5b9d830..c59a132b8c4475ea4b466441ce6d7eb188229007 100644
--- a/apps/files_trashbin/js/app.js
+++ b/apps/files_trashbin/js/app.js
@@ -19,27 +19,26 @@ OCA.Trashbin.App = {
 		this._initialized = true;
 		this.fileList = new OCA.Trashbin.FileList(
 			$('#app-content-trashbin'), {
-				scrollContainer: $('#app-content')
+				scrollContainer: $('#app-content'),
+				fileActions: this._createFileActions()
 			}
 		);
-		this.registerFileActions(this.fileList);
 	},
 
-	registerFileActions: function(fileList) {
-		var self = this;
-		var fileActions = _.extend({}, OCA.Files.FileActions);
-		fileActions.clear();
-		fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
-			var dir = fileList.getCurrentDirectory();
+	_createFileActions: function() {
+		var fileActions = new OCA.Files.FileActions();
+		fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
+			var dir = context.fileList.getCurrentDirectory();
 			if (dir !== '/') {
 				dir = dir + '/';
 			}
-			fileList.changeDirectory(dir + filename);
+			context.fileList.changeDirectory(dir + filename);
 		});
 
 		fileActions.setDefault('dir', 'Open');
 
-		fileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) {
+		fileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename, context) {
+			var fileList = context.fileList;
 			var tr = fileList.findFileEl(filename);
 			var deleteAction = tr.children("td.date").children(".action.delete");
 			deleteAction.removeClass('delete-icon').addClass('progress-icon');
@@ -54,7 +53,8 @@ OCA.Trashbin.App = {
 
 		fileActions.register('all', 'Delete', OC.PERMISSION_READ, function() {
 			return OC.imagePath('core', 'actions/delete');
-		}, function(filename) {
+		}, function(filename, context) {
+			var fileList = context.fileList;
 			$('.tipsy').remove();
 			var tr = fileList.findFileEl(filename);
 			var deleteAction = tr.children("td.date").children(".action.delete");
@@ -67,7 +67,7 @@ OCA.Trashbin.App = {
 				_.bind(fileList._removeCallback, fileList)
 			);
 		});
-		fileList.setFileActions(fileActions);
+		return fileActions;
 	}
 };
 
diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js
index 205f879f335f944631013b10bb272f3fc6e972f9..826c1bd64d51a633ec34c4fa10cbb8a3682af3fa 100644
--- a/apps/files_trashbin/js/filelist.js
+++ b/apps/files_trashbin/js/filelist.js
@@ -26,8 +26,8 @@
 		return name;
 	}
 
-	var FileList = function($el) {
-		this.initialize($el);
+	var FileList = function($el, options) {
+		this.initialize($el, options);
 	};
 	FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, {
 		id: 'trashbin',
diff --git a/apps/files_trashbin/tests/js/filelistSpec.js b/apps/files_trashbin/tests/js/filelistSpec.js
index d41c24c3cc9be9f74beb87189ea919c24daee292..11eeff68df81caf353410ba04a5bce9330e15839 100644
--- a/apps/files_trashbin/tests/js/filelistSpec.js
+++ b/apps/files_trashbin/tests/js/filelistSpec.js
@@ -21,7 +21,6 @@
 
 describe('OCA.Trashbin.FileList tests', function() {
 	var testFiles, alertStub, notificationStub, fileList;
-	var FileActions = OCA.Files.FileActions;
 
 	beforeEach(function() {
 		alertStub = sinon.stub(OC.dialogs, 'alert');
@@ -87,14 +86,18 @@ describe('OCA.Trashbin.FileList tests', function() {
 			etag: '456'
 		}];
 
-		fileList = new OCA.Trashbin.FileList($('#app-content-trashbin'));
-		OCA.Trashbin.App.registerFileActions(fileList);
+		// register file actions like the trashbin App does
+		var fileActions = OCA.Trashbin.App._createFileActions(fileList);
+		fileList = new OCA.Trashbin.FileList(
+			$('#app-content-trashbin'), {
+				fileActions: fileActions
+			}
+		);
 	});
 	afterEach(function() {
 		testFiles = undefined;
 		fileList = undefined;
 
-		FileActions.clear();
 		$('#dir').remove();
 		notificationStub.restore();
 		alertStub.restore();
diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js
index b452bc25b13d4c3c9bf5dd6d5afcdea78d8fb96d..942a1a929f796b56e8603e822a8e129113349a0a 100644
--- a/apps/files_versions/js/versions.js
+++ b/apps/files_versions/js/versions.js
@@ -1,3 +1,14 @@
+/*
+ * Copyright (c) 2014
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+/* global scanFiles, escapeHTML, formatDate */
 $(document).ready(function(){
 
 	if ($('#isPublic').val()){
@@ -7,21 +18,20 @@ $(document).ready(function(){
 		return;
 	}
 
-	if (typeof FileActions !== 'undefined') {
+	if (OCA.Files) {
 		// Add versions button to 'files/index.php'
-		FileActions.register(
-			'file'
-			, 'Versions'
-			, OC.PERMISSION_UPDATE
-			, function() {
+		OCA.Files.fileActions.register(
+			'file',
+			'Versions',
+			OC.PERMISSION_UPDATE,
+			function() {
 				// Specify icon for hitory button
 				return OC.imagePath('core','actions/history');
-			}
-			,function(filename){
+			}, function(filename, context){
 				// Action to perform when clicked
 				if (scanFiles.scanning){return;}//workaround to prevent additional http request block scanning feedback
 
-				var file = $('#dir').val().replace(/(?!<=\/)$|\/$/, '/' + filename);
+				var file = context.dir.replace(/(?!<=\/)$|\/$/, '/' + filename);
 				var createDropDown = true;
 				// Check if drop down is already visible for a different file
 				if (($('#dropdown').length > 0) ) {
@@ -33,10 +43,9 @@ $(document).ready(function(){
 				}
 
 				if(createDropDown === true) {
-					createVersionsDropdown(filename, file);
+					createVersionsDropdown(filename, file, context.fileList);
 				}
-			}
-			, t('files_versions', 'Versions')
+			}, t('files_versions', 'Versions')
 		);
 	}
 
@@ -75,7 +84,7 @@ function goToVersionPage(url){
 	window.location.assign(url);
 }
 
-function createVersionsDropdown(filename, files) {
+function createVersionsDropdown(filename, files, fileList) {
 
 	var start = 0;
 	var fileEl;
@@ -88,7 +97,7 @@ function createVersionsDropdown(filename, files) {
 	html += '<input type="button" value="'+ t('files_versions', 'More versions...') + '" name="show-more-versions" id="show-more-versions" style="display: none;" />';
 
 	if (filename) {
-		fileEl = FileList.findFileEl(filename);
+		fileEl = fileList.findFileEl(filename);
 		fileEl.addClass('mouseOver');
 		$(html).appendTo(fileEl.find('td.filename'));
 	} else {
diff --git a/core/js/js.js b/core/js/js.js
index 3c3efc469bfbffcaed5024bf349ca13727c01af4..e31f67cca9cf028222d235115822da2ac14bceed 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -211,7 +211,16 @@ var OC={
 	linkToRemote:function(service) {
 		return window.location.protocol + '//' + window.location.host + OC.linkToRemoteBase(service);
 	},
-	
+
+	/**
+	 * Gets the base path for the given OCS API service.
+	 * @param {string} service name
+	 * @return {string} OCS API base path
+	 */
+	linkToOCS: function(service) {
+		return window.location.protocol + '//' + window.location.host + OC.webroot + '/ocs/v1.php/' + service + '/';
+	},
+
 	/**
 	 * Generates the absolute url for the given relative url, which can contain parameters.
 	 * @param {string} url
@@ -1241,7 +1250,7 @@ OC.Util = {
 	 * @return {string} fixed image path with png extension if SVG is not supported
 	 */
 	replaceSVGIcon: function(file) {
-		if (!OC.Util.hasSVGSupport()) {
+		if (file && !OC.Util.hasSVGSupport()) {
 			var i = file.lastIndexOf('.svg');
 			if (i >= 0) {
 				file = file.substr(0, i) + '.png' + file.substr(i+4);
diff --git a/core/js/share.js b/core/js/share.js
index d013f2575799d0655d524d1117a4a8556c35d36f..90f6c7fdc7c46b780c506876cba0c8545d75ad30 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -10,8 +10,11 @@ OC.Share={
 	 * Loads ALL share statuses from server, stores them in OC.Share.statuses then
 	 * calls OC.Share.updateIcons() to update the files "Share" icon to "Shared"
 	 * according to their share status and share type.
+	 *
+	 * @param itemType item type
+	 * @param fileList file list instance, defaults to OCA.Files.App.fileList
 	 */
-	loadIcons:function(itemType) {
+	loadIcons:function(itemType, fileList) {
 		// Load all share icons
 		$.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getItemsSharedStatuses', itemType: itemType }, function(result) {
 			if (result && result.status === 'success') {
@@ -19,7 +22,7 @@ OC.Share={
 				$.each(result.data, function(item, data) {
 					OC.Share.statuses[item] = data;
 				});
-				OC.Share.updateIcons(itemType);
+				OC.Share.updateIcons(itemType, fileList);
 			}
 		});
 	},
@@ -27,40 +30,55 @@ OC.Share={
 	 * Updates the files' "Share" icons according to the known
 	 * sharing states stored in OC.Share.statuses.
 	 * (not reloaded from server)
+	 *
+	 * @param itemType item type
+	 * @param fileList file list instance
+	 * defaults to OCA.Files.App.fileList
 	 */
-	updateIcons:function(itemType){
+	updateIcons:function(itemType, fileList){
 		var item;
+		var $fileList;
+		var currentDir;
+		if (!fileList && OCA.Files) {
+			fileList = OCA.Files.App.fileList;
+		}
+		// fileList is usually only defined in the files app
+		if (fileList) {
+			$fileList = fileList.$fileList;
+			currentDir = fileList.getCurrentDirectory();
+		}
 		for (item in OC.Share.statuses){
+			var image;
 			var data = OC.Share.statuses[item];
 
-			var hasLink = data['link'];
+			var hasLink = data.link;
 			// Links override shared in terms of icon display
 			if (hasLink) {
-				var image = OC.imagePath('core', 'actions/public');
+				image = OC.imagePath('core', 'actions/public');
 			} else {
-				var image = OC.imagePath('core', 'actions/shared');
+				image = OC.imagePath('core', 'actions/shared');
 			}
-			if (itemType != 'file' && itemType != 'folder') {
-				$('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center');
+			if (itemType !== 'file' && itemType !== 'folder') {
+				$fileList.find('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center');
 			} else {
-				var file = $('tr[data-id="'+item+'"]');
+				var file = $fileList.find('tr[data-id="'+item+'"]');
 				if (file.length > 0) {
 					var action = $(file).find('.fileactions .action[data-action="Share"]');
 					var img = action.find('img').attr('src', image);
 					action.addClass('permanent');
 					action.html(' <span>'+t('core', 'Shared')+'</span>').prepend(img);
 				} else {
-					var dir = $('#dir').val();
+					var dir = currentDir;
 					if (dir.length > 1) {
 						var last = '';
 						var path = dir;
 						// Search for possible parent folders that are shared
 						while (path != last) {
-							if (path == data['path'] && !data['link']) {
-								var actions = $('.fileactions .action[data-action="Share"]');
+							if (path === data.path && !data.link) {
+								var actions = $fileList.find('.fileactions .action[data-action="Share"]');
 								$.each(actions, function(index, action) {
 									var img = $(action).find('img');
-									if (img.attr('src') != OC.imagePath('core', 'actions/public')) {
+									if (img.attr('src') !== OC.imagePath('core', 'actions/public')) {
 										img.attr('src', image);
 										$(action).addClass('permanent');
 										$(action).html(' <span>'+t('core', 'Shared')+'</span>').prepend(img);
@@ -100,14 +118,18 @@ OC.Share={
 			var file = $('tr').filterAttr('data-id', String(itemSource));
 			if (file.length > 0) {
 				var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share');
-				var img = action.find('img').attr('src', image);
-				if (shares) {
-					action.addClass('permanent');
-					action.html(' <span>'+ escapeHTML(t('core', 'Shared'))+'</span>').prepend(img);
-				} else {
-					action.removeClass('permanent');
-					action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img);
-				}
+				// in case of multiple lists/rows, there might be more than one visible
+				action.each(function() {
+					var action = $(this);
+					var img = action.find('img').attr('src', image);
+					if (shares) {
+						action.addClass('permanent');
+						action.html(' <span>'+ escapeHTML(t('core', 'Shared'))+'</span>').prepend(img);
+					} else {
+						action.removeClass('permanent');
+						action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img);
+					}
+				});
 			}
 		}
 		if (shares) {
diff --git a/tests/karma.config.js b/tests/karma.config.js
index 08b49d854e0bb37b77f25b5bdeaf63cd963f9101..846e8f7be915993d1e19d53abbc86e62bdeb9672 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -43,7 +43,19 @@ module.exports = function(config) {
 		return apps;
 		*/
 		// other apps tests don't run yet... needs further research / clean up
-		return ['files', 'files_trashbin'];
+		return [
+			'files',
+			'files_trashbin',
+			{
+				name: 'files_sharing',
+				srcFiles: [
+					// only test these files, others are not ready and mess
+					// up with the global namespace/classes/state
+					'apps/files_sharing/js/app.js',
+					'apps/files_sharing/js/sharedfilelist.js'
+				],
+				testFiles: ['apps/files_sharing/tests/js/*.js']
+			}];
 	}
 
 	// respect NOCOVERAGE env variable
@@ -110,15 +122,30 @@ module.exports = function(config) {
 		files.push(corePath + 'tests/specs/*.js');
 	}
 
-	for ( var i = 0; i < appsToTest.length; i++ ) {
-		// add app JS
-		var srcFile = 'apps/' + appsToTest[i] + '/js/*.js';
-		files.push(srcFile);
+	function addApp(app) {
+		// if only a string was specified, expand to structure
+		if (typeof(app) === 'string') {
+			app = {
+				srcFiles: 'apps/' + app + '/js/*.js',
+				testFiles: 'apps/' + app + '/tests/js/*.js'
+			};
+		}
+
+		// add source files/patterns
+		files = files.concat(app.srcFiles || []);
+		// add test files/patterns
+		files = files.concat(app.testFiles || []);
 		if (enableCoverage) {
-			preprocessors[srcFile] = 'coverage';
+			// add coverage entry for each file/pattern
+			for (var i = 0; i < app.srcFiles.length; i++) {
+				preprocessors[app.srcFiles[i]] = 'coverage';
+			}
 		}
-		// add test specs
-		files.push('apps/' + appsToTest[i] + '/tests/js/*.js');
+	}
+
+	// add source files for apps to test
+	for ( var i = 0; i < appsToTest.length; i++ ) {
+		addApp(appsToTest[i]);
 	}
 
 	// serve images to avoid warnings