Distinguish legacy file actions from regular file actions

Legacy file actions are registered by legacy apps through
window.FileActions.register(). These actions can only be used by the
main file list ("all files") because legacy apps can only deal with a
single list / container.

New file actions of compatible apps must be registered through
OCA.Files.fileActions. These will be used for other lists like the
sharing overview.

Fixed versions and sharing actions to use OCA.Files.fileActions, which
makes them available in the sharing overview list.
This commit is contained in:
Vincent Petry 2014-05-20 16:01:34 +02:00
parent fa32243d84
commit ef59c69dc8
13 changed files with 183 additions and 81 deletions

View File

@ -24,20 +24,27 @@
initialize: function() { initialize: function() {
this.navigation = new OCA.Files.Navigation($('#app-navigation')); this.navigation = new OCA.Files.Navigation($('#app-navigation'));
// TODO: ideally these should be in a separate class / app (the embedded "all files" app) var fileActions = new OCA.Files.FileActions();
this.fileActions = OCA.Files.FileActions.clone(); // default actions
fileActions.registerDefaultActions();
// legacy actions
fileActions.merge(window.FileActions);
// regular actions
fileActions.merge(OCA.Files.fileActions);
this.files = OCA.Files.Files; 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( this.fileList = new OCA.Files.FileList(
$('#app-content-files'), { $('#app-content-files'), {
scrollContainer: $('#app-content'), scrollContainer: $('#app-content'),
dragOptions: dragOptions, dragOptions: dragOptions,
folderDropOptions: folderDropOptions folderDropOptions: folderDropOptions,
fileActions: fileActions,
allowLegacyActions: true
} }
); );
this.files.initialize(); this.files.initialize();
this.fileActions.registerDefaultActions();
this.fileList.setFileActions(this.fileActions);
// for backward compatibility, the global FileList will // for backward compatibility, the global FileList will
// refer to the one of the "files" view // refer to the one of the "files" view
@ -146,7 +153,7 @@
})(); })();
$(document).ready(function() { $(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 // in the "ready" clause
_.defer(function() { _.defer(function() {
OCA.Files.App.initialize(); OCA.Files.App.initialize();

View File

@ -11,11 +11,40 @@
/* global trashBinApp */ /* global trashBinApp */
(function() { (function() {
var FileActions = { /**
* Construct a new FileActions instance
*/
var FileActions = function() {
this.initialize();
}
FileActions.prototype = {
actions: {}, actions: {},
defaults: {}, defaults: {},
icons: {}, icons: {},
currentFile: null, 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) { register: function (mime, name, permissions, icon, action, displayName) {
if (!this.actions[mime]) { if (!this.actions[mime]) {
this.actions[mime] = {}; this.actions[mime] = {};
@ -31,18 +60,6 @@
this.actions[mime][name]['displayName'] = displayName; this.actions[mime][name]['displayName'] = displayName;
this.icons[name] = icon; this.icons[name] = icon;
}, },
/**
* Clones the current file actions handler including the already
* registered actions.
*/
clone: function() {
var fileActions = _.extend({}, this);
// need to deep copy the actions as well
fileActions.actions = _.extend({}, this.actions);
fileActions.defaults = _.extend({}, this.defaults);
//fileActions.icons = _.extend({}, this.icons);
return fileActions;
},
clear: function() { clear: function() {
this.actions = {}; this.actions = {};
this.defaults = {}; this.defaults = {};
@ -137,6 +154,9 @@
event.preventDefault(); event.preventDefault();
self.currentFile = event.data.elem; self.currentFile = event.data.elem;
// also set on global object for legacy apps
window.FileActions.currentFile = self.currentFile;
var file = self.getCurrentFile(); var file = self.getCurrentFile();
var $tr = $(this).closest('tr'); var $tr = $(this).closest('tr');
@ -276,8 +296,25 @@
}; };
OCA.Files.FileActions = FileActions; OCA.Files.FileActions = 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');
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');
OCA.Files.FileActions.prototype.setDefault.call(window.FileActions, mime, name);
};
})(); })();
// for backward compatibility
window.FileActions = OCA.Files.FileActions;

View File

@ -125,7 +125,7 @@
this.$container = options.scrollContainer || $(window); this.$container = options.scrollContainer || $(window);
this.$table = $el.find('table:first'); this.$table = $el.find('table:first');
this.$fileList = $el.find('#fileList'); this.$fileList = $el.find('#fileList');
this.fileActions = OCA.Files.FileActions; this._initFileActions(options.fileActions);
this.files = []; this.files = [];
this._selectedFiles = {}; this._selectedFiles = {};
this._selectionSummary = new OCA.Files.FileSummary(); this._selectionSummary = new OCA.Files.FileSummary();
@ -168,6 +168,14 @@
this.$container.on('scroll', _.bind(this._onScroll, this)); 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 * Event handler for when the URL changed
*/ */
@ -248,6 +256,8 @@
var action = this.fileActions.getDefault(mime,type, permissions); var action = this.fileActions.getDefault(mime,type, permissions);
if (action) { if (action) {
event.preventDefault(); event.preventDefault();
// also set on global object for legacy apps
window.FileActions.currentFile = this.fileActions.currentFile;
action(filename, { action(filename, {
$file: $tr, $file: $tr,
fileList: this, fileList: this,
@ -791,15 +801,6 @@
return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
}, },
/**
* Sets the file actions handler.
*
* @param fileActions FileActions handler
*/
setFileActions: function(fileActions) {
this.fileActions = fileActions;
},
/** /**
* Sets the current directory name and updates the breadcrumb. * Sets the current directory name and updates the breadcrumb.
* @param targetDir directory to display * @param targetDir directory to display

View File

@ -41,6 +41,10 @@ describe('OCA.Files.App tests', function() {
'</div>' '</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'); pushStateStub = sinon.stub(OC.Util.History, 'pushState');
parseUrlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery'); parseUrlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery');
parseUrlQueryStub.returns({}); parseUrlQueryStub.returns({});
@ -51,8 +55,6 @@ describe('OCA.Files.App tests', function() {
App.navigation = null; App.navigation = null;
App.fileList = null; App.fileList = null;
App.files = null; App.files = null;
App.fileActions.clear();
App.fileActions = null;
pushStateStub.restore(); pushStateStub.restore();
parseUrlQueryStub.restore(); parseUrlQueryStub.restore();
@ -64,6 +66,53 @@ describe('OCA.Files.App tests', function() {
expect(App.fileList.fileActions.actions.all).toBeDefined(); expect(App.fileList.fileActions.actions.all).toBeDefined();
expect(App.fileList.$el.is('#app-content-files')).toEqual(true); 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() { describe('URL handling', function() {

View File

@ -21,7 +21,7 @@
describe('OCA.Files.FileActions tests', function() { describe('OCA.Files.FileActions tests', function() {
var $filesTable, fileList; var $filesTable, fileList;
var FileActions = OCA.Files.FileActions; var FileActions;
beforeEach(function() { beforeEach(function() {
// init horrible parameters // init horrible parameters
@ -31,10 +31,11 @@ describe('OCA.Files.FileActions tests', function() {
// dummy files table // dummy files table
$filesTable = $body.append('<table id="filestable"></table>'); $filesTable = $body.append('<table id="filestable"></table>');
fileList = new OCA.Files.FileList($('#testArea')); fileList = new OCA.Files.FileList($('#testArea'));
FileActions.registerDefaultActions(fileList); FileActions = new OCA.Files.FileActions();
FileActions.registerDefaultActions();
}); });
afterEach(function() { afterEach(function() {
FileActions.clear(); FileActions = null;
fileList = undefined; fileList = undefined;
$('#dir, #permissions, #filestable').remove(); $('#dir, #permissions, #filestable').remove();
}); });

View File

@ -21,7 +21,6 @@
describe('OCA.Files.FileList tests', function() { describe('OCA.Files.FileList tests', function() {
var testFiles, alertStub, notificationStub, fileList; var testFiles, alertStub, notificationStub, fileList;
var FileActions = OCA.Files.FileActions;
/** /**
* Generate test file data * Generate test file data
@ -117,15 +116,11 @@ describe('OCA.Files.FileList tests', function() {
}]; }];
fileList = new OCA.Files.FileList($('#app-content-files')); fileList = new OCA.Files.FileList($('#app-content-files'));
FileActions.clear();
FileActions.registerDefaultActions(fileList);
fileList.setFileActions(FileActions);
}); });
afterEach(function() { afterEach(function() {
testFiles = undefined; testFiles = undefined;
fileList = undefined; fileList = undefined;
FileActions.clear();
notificationStub.restore(); notificationStub.restore();
alertStub.restore(); alertStub.restore();
}); });

View File

@ -23,11 +23,11 @@ OCA.Sharing.App = {
$el, $el,
{ {
scrollContainer: $('#app-content'), scrollContainer: $('#app-content'),
sharedWithUser: true sharedWithUser: true,
fileActions: this._createFileActions()
} }
); );
this._initFileActions(this._inFileList);
this._extendFileList(this._inFileList); this._extendFileList(this._inFileList);
this._inFileList.appName = t('files_sharing', 'Shared with you'); 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.')); this._inFileList.$el.find('#emptycontent').text(t('files_sharing', 'No files have been shared with you yet.'));
@ -41,25 +41,31 @@ OCA.Sharing.App = {
$el, $el,
{ {
scrollContainer: $('#app-content'), scrollContainer: $('#app-content'),
sharedWithUser: false sharedWithUser: false,
fileActions: this._createFileActions()
} }
); );
this._initFileActions(this._outFileList);
this._extendFileList(this._outFileList); this._extendFileList(this._outFileList);
this._outFileList.appName = t('files_sharing', 'Shared with others'); 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.')); this._outFileList.$el.find('#emptycontent').text(t('files_sharing', 'You haven\'t shared any files yet.'));
}, },
_initFileActions: function(fileList) { _createFileActions: function() {
var fileActions = OCA.Files.FileActions.clone(); // 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.merge(OCA.Files.fileActions);
// when the user clicks on a folder, redirect to the corresponding // when the user clicks on a folder, redirect to the corresponding
// folder in the files app // folder in the files app instead of opening it directly
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
OCA.Files.App.setActiveView('files', {silent: true}); OCA.Files.App.setActiveView('files', {silent: true});
OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true); OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true);
}); });
fileList.setFileActions(fileActions); fileActions.setDefault('dir', 'Open');
return fileActions;
}, },
_extendFileList: function(fileList) { _extendFileList: function(fileList) {

View File

@ -19,9 +19,18 @@ OCA.Sharing.PublicApp = {
initialize: function($el) { initialize: function($el) {
var self = this; var self = this;
var fileActions;
if (this._initialized) { if (this._initialized) {
return; 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._initialized = true;
this.initialDir = $('#dir').val(); this.initialDir = $('#dir').val();
@ -32,7 +41,8 @@ OCA.Sharing.PublicApp = {
{ {
scrollContainer: $(window), scrollContainer: $(window),
dragOptions: dragOptions, dragOptions: dragOptions,
folderDropOptions: folderDropOptions folderDropOptions: folderDropOptions,
fileActions: fileActions
} }
); );
this.files = OCA.Files.Files; this.files = OCA.Files.Files;
@ -121,10 +131,8 @@ OCA.Sharing.PublicApp = {
}; };
}); });
this.fileActions = _.extend({}, OCA.Files.FileActions); // do not allow sharing from the public page
this.fileActions.registerDefaultActions(this.fileList); delete this.fileList.fileActions.actions.all.Share;
delete this.fileActions.actions.all.Share;
this.fileList.setFileActions(this.fileActions);
this.fileList.changeDirectory(this.initialDir || '/', false, true); this.fileList.changeDirectory(this.initialDir || '/', false, true);

View File

@ -9,10 +9,7 @@
*/ */
$(document).ready(function() { $(document).ready(function() {
if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) {
var sharesLoaded = false;
if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined') {
// TODO: make a separate class for this or a hook or jQuery event ? // TODO: make a separate class for this or a hook or jQuery event ?
if (OCA.Files.FileList) { if (OCA.Files.FileList) {
var oldCreateRow = OCA.Files.FileList.prototype._createRow; var oldCreateRow = OCA.Files.FileList.prototype._createRow;
@ -64,18 +61,18 @@ $(document).ready(function() {
} }
}) })
if (!sharesLoaded){ if (!OCA.Sharing.sharesLoaded){
OC.Share.loadIcons('file', $fileList); OC.Share.loadIcons('file', $fileList);
// assume that we got all shares, so switching directories // assume that we got all shares, so switching directories
// will not invalidate that list // will not invalidate that list
sharesLoaded = true; OCA.Sharing.sharesLoaded = true;
} }
else{ else{
OC.Share.updateIcons('file', $fileList); OC.Share.updateIcons('file', $fileList);
} }
}); });
OCA.Files.FileActions.register( OCA.Files.fileActions.register(
'all', 'all',
'Share', 'Share',
OC.PERMISSION_SHARE, OC.PERMISSION_SHARE,

View File

@ -19,22 +19,20 @@ OCA.Trashbin.App = {
this._initialized = true; this._initialized = true;
this.fileList = new OCA.Trashbin.FileList( this.fileList = new OCA.Trashbin.FileList(
$('#app-content-trashbin'), { $('#app-content-trashbin'), {
scrollContainer: $('#app-content') scrollContainer: $('#app-content'),
fileActions: this._createFileActions()
} }
); );
this.registerFileActions(this.fileList);
}, },
registerFileActions: function(fileList) { _createFileActions: function() {
var self = this; var fileActions = new OCA.Files.FileActions();
var fileActions = _.extend({}, OCA.Files.FileActions); fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
fileActions.clear(); var dir = context.fileList.getCurrentDirectory();
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
var dir = fileList.getCurrentDirectory();
if (dir !== '/') { if (dir !== '/') {
dir = dir + '/'; dir = dir + '/';
} }
fileList.changeDirectory(dir + filename); context.fileList.changeDirectory(dir + filename);
}); });
fileActions.setDefault('dir', 'Open'); fileActions.setDefault('dir', 'Open');
@ -69,7 +67,7 @@ OCA.Trashbin.App = {
_.bind(fileList._removeCallback, fileList) _.bind(fileList._removeCallback, fileList)
); );
}); });
fileList.setFileActions(fileActions); return fileActions;
} }
}; };

View File

@ -26,8 +26,8 @@
return name; return name;
} }
var FileList = function($el) { var FileList = function($el, options) {
this.initialize($el); this.initialize($el, options);
}; };
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, { FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, {
id: 'trashbin', id: 'trashbin',

View File

@ -21,7 +21,6 @@
describe('OCA.Trashbin.FileList tests', function() { describe('OCA.Trashbin.FileList tests', function() {
var testFiles, alertStub, notificationStub, fileList; var testFiles, alertStub, notificationStub, fileList;
var FileActions = OCA.Files.FileActions;
beforeEach(function() { beforeEach(function() {
alertStub = sinon.stub(OC.dialogs, 'alert'); alertStub = sinon.stub(OC.dialogs, 'alert');
@ -87,14 +86,18 @@ describe('OCA.Trashbin.FileList tests', function() {
etag: '456' etag: '456'
}]; }];
fileList = new OCA.Trashbin.FileList($('#app-content-trashbin')); // register file actions like the trashbin App does
OCA.Trashbin.App.registerFileActions(fileList); var fileActions = OCA.Trashbin.App._createFileActions(fileList);
fileList = new OCA.Trashbin.FileList(
$('#app-content-trashbin'), {
fileActions: fileActions
}
);
}); });
afterEach(function() { afterEach(function() {
testFiles = undefined; testFiles = undefined;
fileList = undefined; fileList = undefined;
FileActions.clear();
$('#dir').remove(); $('#dir').remove();
notificationStub.restore(); notificationStub.restore();
alertStub.restore(); alertStub.restore();

View File

@ -8,7 +8,7 @@
* *
*/ */
/* global FileActions, scanFiles, escapeHTML, formatDate */ /* global scanFiles, escapeHTML, formatDate */
$(document).ready(function(){ $(document).ready(function(){
if ($('#isPublic').val()){ if ($('#isPublic').val()){
@ -18,9 +18,9 @@ $(document).ready(function(){
return; return;
} }
if (typeof FileActions !== 'undefined') { if (OCA.Files) {
// Add versions button to 'files/index.php' // Add versions button to 'files/index.php'
FileActions.register( OCA.Files.fileActions.register(
'file', 'file',
'Versions', 'Versions',
OC.PERMISSION_UPDATE, OC.PERMISSION_UPDATE,