diff --git a/apps/files/js/app.js b/apps/files/js/app.js
index 71802948a5ca70d9eaf9fb1aca8bcd07acc15830..45b6b6a0e1666b7e1ad5df4a640032e183f25dd2 100644
--- a/apps/files/js/app.js
+++ b/apps/files/js/app.js
@@ -32,6 +32,10 @@
 			// regular actions
 			fileActions.merge(OCA.Files.fileActions);
 
+			// in case apps would decide to register file actions later,
+			// replace the global object with this one
+			OCA.Files.fileActions = fileActions;
+
 			this.files = OCA.Files.Files;
 
 			// TODO: ideally these should be in a separate class / app (the embedded "all files" app)
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index 47a6ab2f04b3cecd0e955a639da11cb3ece480f4..fc7c9ccacef655ee25a7f0f487ff3de0be4db536 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -22,9 +22,51 @@
 		defaults: {},
 		icons: {},
 		currentFile: null,
+
+		/**
+		 * List of handlers to be notified whenever a register() or
+		 * setDefault() was called.
+		 */
+		_updateListeners: [],
+
 		initialize: function() {
 			this.clear();
 		},
+
+		/**
+		 * Adds an update listener to be notified whenever register()
+		 * or setDefault() has been called.
+		 *
+		 * @param Function callback
+		 */
+		addUpdateListener: function(callback) {
+			if (!_.isFunction(callback)) {
+				throw 'Argument passed to FileActions.addUpdateListener must be a function';
+			}
+			this._updateListeners.push(callback);
+		},
+
+		/**
+		 * Removes an update listener.
+		 *
+		 * @param Function callback
+		 */
+		removeUpdateListener: function(callback) {
+			if (!_.isFunction(callback)) {
+				throw 'Argument passed to FileActions.removeUpdateListener must be a function';
+			}
+			this._updateListeners = _.without(this._updateListeners, callback);
+		},
+
+		/**
+		 * Notifies the registered update listeners
+		 */
+		_notifyUpdateListeners: function() {
+			for (var i = 0; i < this._updateListeners.length; i++) {
+				this._updateListeners[i](this);
+			}
+		},
+
 		/**
 		 * Merges the actions from the given fileActions into
 		 * this instance.
@@ -59,15 +101,18 @@
 			this.actions[mime][name]['permissions'] = permissions;
 			this.actions[mime][name]['displayName'] = displayName;
 			this.icons[name] = icon;
+			this._notifyUpdateListeners();
 		},
 		clear: function() {
 			this.actions = {};
 			this.defaults = {};
 			this.icons = {};
 			this.currentFile = null;
+			this._updateListeners = [];
 		},
 		setDefault: function (mime, name) {
 			this.defaults[mime] = name;
+			this._notifyUpdateListeners();
 		},
 		get: function (mime, type, permissions) {
 			var actions = this.getActions(mime, type, permissions);
@@ -133,8 +178,7 @@
 		display: function (parent, triggerEvent, fileList) {
 			if (!fileList) {
 				console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance');
-				// using default list instead, which could be wrong
-				fileList = OCA.Files.App.fileList;
+				return;
 			}
 			this.currentFile = parent;
 			var self = this;
@@ -309,9 +353,10 @@
 				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);
+	window.FileActions.display = function (parent, triggerEvent, fileList) {
+		fileList = fileList || OCA.Files.App.fileList;
+		console.warn('FileActions.display() is deprecated, please use OCA.Files.fileActions.register() which automatically redisplays actions', mime, name);
+		OCA.Files.FileActions.prototype.display.call(window.FileActions, parent, triggerEvent, fileList);
 	};
 })();
 
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 55afedb2065829e38cbf5b30ca06264a54e58802..82aa29670dfc4921aed19664d01921c980b61cbc 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -168,12 +168,22 @@
 			this.$container.on('scroll', _.bind(this._onScroll, this));
 		},
 
+		/**
+		 * Destroy / uninitialize this instance.
+		 */
+		destroy: function() {
+			// TODO: also unregister other event handlers
+			this.fileActions.removeUpdateListener(this._onFileActionsUpdated);
+		},
+
 		_initFileActions: function(fileActions) {
 			this.fileActions = fileActions;
 			if (!this.fileActions) {
 				this.fileActions = new OCA.Files.FileActions();
 				this.fileActions.registerDefaultActions();
 			}
+			this._onFileActionsUpdated = _.debounce(_.bind(this._onFileActionsUpdated, this), 100);
+			this.fileActions.addUpdateListener(this._onFileActionsUpdated);
 		},
 
 		/**
@@ -487,6 +497,18 @@
 			}
 		},
 
+		/**
+		 * Event handler for when file actions were updated.
+		 * This will refresh the file actions on the list.
+		 */
+		_onFileActionsUpdated: function() {
+			console.log('onFileActionsUpdated');
+			var self = this;
+			this.$fileList.find('tr td.filename').each(function() {
+				self.fileActions.display($(this), true, self);
+			});
+		},
+
 		/**
 		 * Sets the files to be displayed in the list.
 		 * This operation will re-render the list and update the summary.
diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js
index 355761afa0115023bdcf3b53257fe791e793c496..f464800730a0d9d080af88c9017a8f2a63a3acda 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; 
+	var FileActions;
 
 	beforeEach(function() {
 		// init horrible parameters
@@ -36,6 +36,7 @@ describe('OCA.Files.FileActions tests', function() {
 	});
 	afterEach(function() {
 		FileActions = null;
+		fileList.destroy();
 		fileList = undefined;
 		$('#dir, #permissions, #filestable').remove();
 	});
@@ -192,4 +193,54 @@ describe('OCA.Files.FileActions tests', function() {
 		context = actionStub.getCall(0).args[1];
 		expect(context.dir).toEqual('/somepath');
 	});
+	describe('events', function() {
+		var clock;
+		beforeEach(function() {
+			clock = sinon.useFakeTimers();
+		});
+		afterEach(function() {
+			clock.restore();
+		});
+		it('notifies update event handlers once after multiple changes', function() {
+			var actionStub = sinon.stub();
+			var handler = sinon.stub();
+			FileActions.addUpdateListener(handler);
+			FileActions.register(
+					'all',
+					'Test',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					actionStub
+			);
+			FileActions.register(
+					'all',
+					'Test2',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					actionStub
+			);
+			expect(handler.calledTwice).toEqual(true);
+		});
+		it('does not notifies update event handlers after unregistering', function() {
+			var actionStub = sinon.stub();
+			var handler = sinon.stub();
+			FileActions.addUpdateListener(handler);
+			FileActions.removeUpdateListener(handler);
+			FileActions.register(
+					'all',
+					'Test',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					actionStub
+			);
+			FileActions.register(
+					'all',
+					'Test2',
+					OC.PERMISSION_READ,
+					OC.imagePath('core', 'actions/test'),
+					actionStub
+			);
+			expect(handler.notCalled).toEqual(true);
+		});
+	});
 });
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index dea7c48e05eaa8633df81f3a54cf3b7744f38b48..7100a2667c9e6089c70ff38f641e0ccdb81432fd 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -1623,6 +1623,38 @@ describe('OCA.Files.FileList tests', function() {
 			expect(context.fileActions).toBeDefined();
 			expect(context.dir).toEqual('/subdir');
 		});
+		it('redisplays actions when new actions have been registered', function() {
+			var actionStub = sinon.stub();
+			var clock = sinon.useFakeTimers();
+			var debounceStub = sinon.stub(_, 'debounce', function(callback) {
+				return function() {
+					// defer instead of debounce, to make it work with clock
+					_.defer(callback);
+				};
+			});
+			// need to reinit the list to make the debounce call
+			fileList.destroy();
+			fileList = new OCA.Files.FileList($('#app-content-files'));
+
+			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
+			);
+			var $tr = fileList.findFileEl('One.txt');
+			expect($tr.find('.action-test').length).toEqual(0);
+			// update is delayed
+			clock.tick(100);
+			expect($tr.find('.action-test').length).toEqual(1);
+			clock.restore();
+			debounceStub.restore();
+		});
 	});
 	describe('Sorting files', function() {
 		it('Sorts by name by default', function() {
diff --git a/apps/files_external/tests/appSpec.js b/apps/files_external/tests/appSpec.js
index b00442384dae6aac42685bd1b1c325860f85fcfe..43902d1c1d0009d7807417ce886be1258d03019e 100644
--- a/apps/files_external/tests/appSpec.js
+++ b/apps/files_external/tests/appSpec.js
@@ -42,6 +42,7 @@ describe('OCA.External.App tests', function() {
 	});
 	afterEach(function() {
 		App.fileList = null;
+		fileList.destroy();
 		fileList = null;
 	});
 
diff --git a/apps/files_external/tests/js/mountsfilelistSpec.js b/apps/files_external/tests/js/mountsfilelistSpec.js
index 96a6b622a4e55028646861e5de83d5ee2913331a..b599df77aacf0f4ddaaf03f83a1ae691c89827f3 100644
--- a/apps/files_external/tests/js/mountsfilelistSpec.js
+++ b/apps/files_external/tests/js/mountsfilelistSpec.js
@@ -54,6 +54,7 @@ describe('OCA.External.FileList tests', function() {
 	afterEach(function() {
 		OCA.Files.FileList.prototype = oldFileListPrototype;
 		testFiles = undefined;
+		fileList.destroy();
 		fileList = undefined;
 		fileActions = undefined;
 
diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js
index 9c46b7caf1b6bf74f3a07450448249c41e7d399e..d0480ad1aa436ad1a27b23d61187887fa0429df4 100644
--- a/apps/files_sharing/tests/js/appSpec.js
+++ b/apps/files_sharing/tests/js/appSpec.js
@@ -47,6 +47,8 @@ describe('OCA.Sharing.App tests', function() {
 	afterEach(function() {
 		App._inFileList = null;
 		App._outFileList = null;
+		fileListIn.destroy();
+		fileListOut.destroy();
 		fileListIn = null;
 		fileListOut = null;
 	});
diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js
index 455addaccc0d9587476a17f39ef1ef5385ae6d4b..3d4fc75482131260d060e1acdcb4834f157d1cb2 100644
--- a/apps/files_sharing/tests/js/shareSpec.js
+++ b/apps/files_sharing/tests/js/shareSpec.js
@@ -70,6 +70,8 @@ describe('OCA.Sharing.Util tests', function() {
 		OCA.Files.FileList.prototype = oldFileListPrototype;
 		delete OCA.Sharing.sharesLoaded;
 		delete OC.Share.droppedDown;
+		fileList.destroy();
+		fileList = null;
 		OC.Share.statuses = {};
 		OC.Share.currentShares = {};
 	});
diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js
index 0f6d0a0ba49a2b6d23c131ca145484c6f24e5c4e..4e130885500b770cdb7bc3ffa8d3d6a93e8029a8 100644
--- a/apps/files_sharing/tests/js/sharedfilelistSpec.js
+++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js
@@ -55,6 +55,7 @@ describe('OCA.Sharing.FileList tests', function() {
 	afterEach(function() {
 		OCA.Files.FileList.prototype = oldFileListPrototype;
 		testFiles = undefined;
+		fileList.destroy();
 		fileList = undefined;
 		fileActions = undefined;
 
diff --git a/apps/files_trashbin/tests/js/filelistSpec.js b/apps/files_trashbin/tests/js/filelistSpec.js
index 11eeff68df81caf353410ba04a5bce9330e15839..fd479234b30d6501e15110f91718fd2577947430 100644
--- a/apps/files_trashbin/tests/js/filelistSpec.js
+++ b/apps/files_trashbin/tests/js/filelistSpec.js
@@ -96,6 +96,7 @@ describe('OCA.Trashbin.FileList tests', function() {
 	});
 	afterEach(function() {
 		testFiles = undefined;
+		fileList.destroy();
 		fileList = undefined;
 
 		$('#dir').remove();