diff --git a/apps/files/js/detailfileinfoview.js b/apps/files/js/detailfileinfoview.js
index 9a88b5e2d8..4359500121 100644
--- a/apps/files/js/detailfileinfoview.js
+++ b/apps/files/js/detailfileinfoview.js
@@ -16,34 +16,12 @@
* Displays a block of details about the file info.
*
*/
- var DetailFileInfoView = function() {
- this.initialize();
- };
- /**
- * @memberof OCA.Files
- */
- DetailFileInfoView.prototype = {
- /**
- * jQuery element
- */
- $el: null,
+ var DetailFileInfoView = OC.Backbone.View.extend({
+ tagName: 'div',
+ className: 'detailFileInfoView',
_template: null,
- /**
- * Currently displayed file info
- *
- * @type OCA.Files.FileInfo
- */
- _fileInfo: null,
-
- /**
- * Initialize the details view
- */
- initialize: function() {
- this.$el = $('
');
- },
-
/**
* returns the jQuery object for HTML output
*
@@ -53,31 +31,13 @@
return this.$el;
},
- /**
- * Destroy / uninitialize this instance.
- */
- destroy: function() {
- if (this.$el) {
- this.$el.remove();
- }
- },
-
- /**
- * Renders this details view
- *
- * @abstract
- */
- render: function() {
- // to be implemented in subclass
- },
-
/**
* Sets the file info to be displayed in the view
*
* @param {OCA.Files.FileInfo} fileInfo file info to set
*/
setFileInfo: function(fileInfo) {
- this._fileInfo = fileInfo;
+ this.model = fileInfo;
this.render();
},
@@ -87,9 +47,9 @@
* @return {OCA.Files.FileInfo} file info
*/
getFileInfo: function() {
- return this._fileInfo;
+ return this.model;
}
- };
+ });
OCA.Files.DetailFileInfoView = DetailFileInfoView;
})();
diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js
index 7b7bd013f9..4df359e452 100644
--- a/apps/files/js/detailsview.js
+++ b/apps/files/js/detailsview.js
@@ -33,30 +33,14 @@
* The details view show details about a selected file.
*
*/
- var DetailsView = function() {
- this.initialize();
- };
-
- /**
- * @memberof OCA.Files
- */
- DetailsView.prototype = {
-
- /**
- * jQuery element
- */
- $el: null,
+ var DetailsView = OC.Backbone.View.extend({
+ id: 'app-sidebar',
+ tabName: 'div',
+ className: 'detailsView',
_template: null,
_templateTabHeader: null,
- /**
- * Currently displayed file info
- *
- * @type OCA.Files.FileInfo
- */
- _fileInfo: null,
-
/**
* List of detail tab views
*
@@ -78,33 +62,25 @@
*/
_currentTabId: null,
+ events: {
+ 'click a.close': '_onClose',
+ 'click .tabHeaders .tabHeader': '_onClickTab'
+ },
+
/**
* Initialize the details view
*/
initialize: function() {
- this.$el = $('');
- this.fileInfo = null;
this._tabViews = [];
this._detailFileInfoViews = [];
- this.$el.on('click', 'a.close', function(event) {
- OC.Apps.hideAppSidebar();
- event.preventDefault();
- });
-
- this.$el.on('click', '.tabHeaders .tabHeader', _.bind(this._onClickTab, this));
-
// uncomment to add some dummy tabs for testing
- //this._addTestTabs();
+ // this._addTestTabs();
},
- /**
- * Destroy / uninitialize this instance.
- */
- destroy: function() {
- if (this.$el) {
- this.$el.remove();
- }
+ _onClose: function(event) {
+ OC.Apps.hideAppSidebar();
+ event.preventDefault();
},
_onClickTab: function(e) {
@@ -148,7 +124,6 @@
*/
render: function() {
var self = this;
- this.$el.empty();
if (!this._template) {
this._template = Handlebars.compile(TEMPLATE);
@@ -158,12 +133,13 @@
this._templateTabHeader = Handlebars.compile(TEMPLATE_TAB_HEADER);
}
- var $el = $(this._template({
+ this.$el.html(this._template({
closeLabel: t('files', 'Close')
}));
- var $tabsContainer = $el.find('.tabsContainer');
- var $tabHeadsContainer = $el.find('.tabHeaders');
- var $detailsContainer = $el.find('.detailFileInfoContainer');
+
+ var $tabsContainer = this.$el.find('.tabsContainer');
+ var $tabHeadsContainer = this.$el.find('.tabHeaders');
+ var $detailsContainer = this.$el.find('.detailFileInfoContainer');
// render details
_.each(this._detailFileInfoViews, function(detailView) {
@@ -172,40 +148,36 @@
if (this._tabViews.length > 0) {
if (!this._currentTab) {
- this._currentTab = this._tabViews[0].getId();
+ this._currentTab = this._tabViews[0].id;
}
// render tabs
_.each(this._tabViews, function(tabView, i) {
// hidden by default
var $el = tabView.get$();
- var isCurrent = (tabView.getId() === self._currentTab);
+ var isCurrent = (tabView.id === self._currentTab);
if (!isCurrent) {
$el.addClass('hidden');
}
$tabsContainer.append($el);
$tabHeadsContainer.append(self._templateTabHeader({
- tabId: tabView.getId(),
+ tabId: tabView.id,
tabIndex: i,
label: tabView.getLabel(),
selected: isCurrent
}));
});
}
-
- // TODO: select current tab
-
- this.$el.append($el);
},
/**
* Sets the file info to be displayed in the view
*
- * @param {OCA.Files.FileInfo} fileInfo file info to set
+ * @param {OCA.Files.FileInfoModel} fileInfo file info to set
*/
setFileInfo: function(fileInfo) {
- this._fileInfo = fileInfo;
+ this.model = fileInfo;
this.render();
@@ -221,10 +193,10 @@
/**
* Returns the file info.
*
- * @return {OCA.Files.FileInfo} file info
+ * @return {OCA.Files.FileInfoModel} file info
*/
getFileInfo: function() {
- return this._fileInfo;
+ return this.model;
},
/**
@@ -244,7 +216,7 @@
addDetailView: function(detailView) {
this._detailFileInfoViews.push(detailView);
}
- };
+ });
OCA.Files.DetailsView = DetailsView;
})();
diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js
index b9b1dda2cc..b2e02971fb 100644
--- a/apps/files/js/detailtabview.js
+++ b/apps/files/js/detailtabview.js
@@ -17,23 +17,10 @@
* Base class for tab views to display file information.
*
*/
- var DetailTabView = function(id) {
- this.initialize(id);
- };
+ var DetailTabView = OC.Backbone.View.extend({
+ tag: 'div',
- /**
- * @memberof OCA.Files
- */
- DetailTabView.prototype = {
- /**
- * jQuery element
- */
- $el: null,
-
- /**
- * Tab id
- */
- _id: null,
+ className: 'tab',
/**
* Tab label
@@ -42,43 +29,11 @@
_template: null,
- /**
- * Currently displayed file info
- *
- * @type OCA.Files.FileInfo
- */
- _fileInfo: null,
-
- /**
- * Initialize the details view
- *
- * @param {string} id tab id
- */
- initialize: function(id) {
- if (!id) {
- throw 'Argument "id" is required';
+ initialize: function() {
+ if (!this.id) {
+ this.id = 'detailTabView' + DetailTabView._TAB_COUNT;
+ DetailTabView._TAB_COUNT++;
}
- this._id = id;
- this.$el = $('');
- this.$el.attr('data-tabid', id);
- },
-
- /**
- * Destroy / uninitialize this instance.
- */
- destroy: function() {
- if (this.$el) {
- this.$el.remove();
- }
- },
-
- /**
- * Returns the tab element id
- *
- * @return {string} tab id
- */
- getId: function() {
- return this._id;
},
/**
@@ -87,7 +42,7 @@
* @return {String} label
*/
getLabel: function() {
- return 'Tab ' + this._id;
+ return 'Tab ' + this.id;
},
/**
@@ -107,29 +62,29 @@
render: function() {
// to be implemented in subclass
// FIXME: code is only for testing
- this.$el.empty();
- this.$el.append('Hello ' + this._id + '
');
+ this.$el.html('Hello ' + this.id + '
');
},
/**
* Sets the file info to be displayed in the view
*
- * @param {OCA.Files.FileInfo} fileInfo file info to set
+ * @param {OCA.Files.FileInfoModel} fileInfo file info to set
*/
setFileInfo: function(fileInfo) {
- this._fileInfo = fileInfo;
+ this.model = fileInfo;
this.render();
},
/**
* Returns the file info.
*
- * @return {OCA.Files.FileInfo} file info
+ * @return {OCA.Files.FileInfoModel} file info
*/
getFileInfo: function() {
- return this._fileInfo;
+ return this.model;
}
- };
+ });
+ DetailTabView._TAB_COUNT = 0;
OCA.Files.DetailTabView = DetailTabView;
})();
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index 43f74c5816..a2dcf266bf 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -31,6 +31,10 @@
actions: {},
defaults: {},
icons: {},
+
+ /**
+ * @deprecated
+ */
currentFile: null,
/**
@@ -331,6 +335,9 @@
$trigger.addClass('open');
menu = new OCA.Files.FileActionsMenu();
+
+ context.$file.find('td.filename').append(menu.$el);
+
menu.$el.on('afterHide', function() {
context.$file.removeClass('mouseOver');
$trigger.removeClass('open');
@@ -338,7 +345,6 @@
});
context.$file.addClass('mouseOver');
- context.$file.find('td.filename').append(menu.$el);
menu.show(context);
},
@@ -401,17 +407,85 @@
// also set on global object for legacy apps
window.FileActions.currentFile = currentFile;
+ var callContext = _.extend({}, context);
+
+ if (!context.dir && context.fileList) {
+ callContext.dir = $file.attr('data-path') || context.fileList.getCurrentDirectory();
+ }
+
+ if (!context.fileInfoModel && context.fileList) {
+ callContext.fileInfoModel = context.fileList.getModelForFile(fileName);
+ if (!callContext.fileInfoModel) {
+ console.warn('No file info model found for file "' + fileName + '"');
+ }
+ }
+
actionSpec.action(
fileName,
- _.extend(context, {
- dir: $file.attr('data-path') || context.fileList.getCurrentDirectory()
- })
+ callContext
);
}
);
return $actionEl;
},
+ /**
+ * Trigger the given action on the given file.
+ *
+ * @param {string} actionName action name
+ * @param {OCA.Files.FileInfoModel} fileInfoModel file info model
+ * @param {OCA.Files.FileList} [fileList] file list, for compatibility with older action handlers [DEPRECATED]
+ *
+ * @return {boolean} true if the action handler was called, false otherwise
+ *
+ * @since 8.2
+ */
+ triggerAction: function(actionName, fileInfoModel, fileList) {
+ var actionFunc;
+ var actions = this.get(
+ fileInfoModel.get('mimetype'),
+ fileInfoModel.isDirectory() ? 'dir' : 'file',
+ fileInfoModel.get('permissions')
+ );
+
+ if (actionName) {
+ actionFunc = actions[actionName];
+ } else {
+ actionFunc = this.getDefault(
+ fileInfoModel.get('mimetype'),
+ fileInfoModel.isDirectory() ? 'dir' : 'file',
+ fileInfoModel.get('permissions')
+ );
+ }
+
+ if (!actionFunc) {
+ actionFunc = actions['Download'];
+ }
+
+ if (!actionFunc) {
+ return false;
+ }
+
+ var context = {
+ fileActions: this,
+ fileInfoModel: fileInfoModel,
+ dir: fileInfoModel.get('path')
+ };
+
+ var fileName = fileInfoModel.get('name');
+ this.currentFile = fileName;
+ // also set on global object for legacy apps
+ window.FileActions.currentFile = fileName;
+
+ if (fileList) {
+ // compatibility with action handlers that expect these
+ context.fileList = fileList;
+ context.$file = fileList.findFileEl(fileName);
+ }
+
+ actionFunc(fileName, context);
+ },
+
/**
* Display file actions for the given element
* @param parent "td" element of the file for which to display actions
@@ -626,11 +700,12 @@
* Action handler function for file actions
*
* @callback OCA.Files.FileActions~actionHandler
- * @param {String} fileName name of the clicked file
+ * @param {String} fileName name of the file on which the action must be performed
* @param context context
* @param {String} context.dir directory of the file
- * @param context.$file jQuery element of the file
- * @param {OCA.Files.FileList} context.fileList the FileList instance on which the action occurred
+ * @param {OCA.Files.FileInfoModel} fileInfoModel file info model
+ * @param {Object} [context.$file] jQuery element of the file [DEPRECATED]
+ * @param {OCA.Files.FileList} [context.fileList] the FileList instance on which the action occurred [DEPRECATED]
* @param {OCA.Files.FileActions} context.fileActions the FileActions instance on which the action occurred
*/
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index e297edcf11..1f86f5a392 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -213,7 +213,7 @@
if (_.isUndefined(options.detailsViewEnabled) || options.detailsViewEnabled) {
this._detailsView = new OCA.Files.DetailsView();
- this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView());
+ this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView({fileList: this, fileActions: this.fileActions}));
this._detailsView.$el.insertBefore(this.$el);
this._detailsView.$el.addClass('disappear');
}
@@ -282,33 +282,75 @@
this.fileActions.on('setDefault', this._onFileActionsUpdated);
},
+ /**
+ * Returns a unique model for the given file name.
+ *
+ * @param {string|object} fileName file name or jquery row
+ * @return {OCA.Files.FileInfoModel} file info model
+ */
+ getModelForFile: function(fileName) {
+ var $tr;
+ // jQuery object ?
+ if (fileName.is) {
+ $tr = fileName;
+ fileName = $tr.attr('data-file');
+ } else {
+ $tr = this.findFileEl(fileName);
+ }
+
+ if (!$tr || !$tr.length) {
+ return null;
+ }
+
+ // if requesting the selected model, return it
+ if (this._currentFileModel && this._currentFileModel.get('name') === fileName) {
+ return this._currentFileModel;
+ }
+
+ // TODO: note, this is a temporary model required for synchronising
+ // state between different views.
+ // In the future the FileList should work with Backbone.Collection
+ // and contain existing models that can be used.
+ // This method would in the future simply retrieve the matching model from the collection.
+ var model = new OCA.Files.FileInfoModel(this.elementToFile($tr));
+ if (!model.has('path')) {
+ model.set('path', this.getCurrentDirectory(), {silent: true});
+ }
+ return model;
+ },
+
/**
* Update the details view to display the given file
*
- * @param {OCA.Files.FileInfo} fileInfo file info to display
+ * @param {string} fileName file name from the current list
*/
- _updateDetailsView: function(fileInfo) {
+ _updateDetailsView: function(fileName) {
if (!this._detailsView) {
return;
}
- var self = this;
var oldFileInfo = this._detailsView.getFileInfo();
if (oldFileInfo) {
// TODO: use more efficient way, maybe track the highlight
- this.$fileList.children().filterAttr('data-id', '' + oldFileInfo.id).removeClass('highlighted');
+ this.$fileList.children().filterAttr('data-id', '' + oldFileInfo.get('id')).removeClass('highlighted');
+ oldFileInfo.off('change', this._onSelectedModelChanged, this);
}
- if (!fileInfo) {
+ if (!fileName) {
OC.Apps.hideAppSidebar();
this._detailsView.setFileInfo(null);
+ this._currentFileModel = null;
return;
}
- this.$fileList.children().filterAttr('data-id', '' + fileInfo.id).addClass('highlighted');
- this._detailsView.setFileInfo(_.extend({
- path: this.getCurrentDirectory()
- }, fileInfo));
+ var $tr = this.findFileEl(fileName);
+ var model = this.getModelForFile($tr);
+
+ this._currentFileModel = model;
+
+ $tr.addClass('highlighted');
+
+ this._detailsView.setFileInfo(model);
this._detailsView.$el.scrollTop(0);
_.defer(OC.Apps.showAppSidebar);
},
@@ -367,7 +409,7 @@
this._selectionSummary.remove(data);
}
if (this._selectionSummary.getTotal() === 1) {
- this._updateDetailsView(_.values(this._selectedFiles)[0]);
+ this._updateDetailsView(_.values(this._selectedFiles)[0].name);
} else {
// show nothing when multiple files are selected
this._updateDetailsView(null);
@@ -432,8 +474,7 @@
$(event.target).closest('a').blur();
}
} else {
- var fileInfo = this.files[$tr.index()];
- this._updateDetailsView(fileInfo);
+ this._updateDetailsView($tr.attr('data-file'));
event.preventDefault();
}
}
@@ -1188,6 +1229,8 @@
sortdirection: this._sortDirection
}
});
+ // close sidebar
+ this._updateDetailsView(null);
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
},
@@ -1587,7 +1630,7 @@
tr.remove();
tr = self.add(fileInfo, {updateSummary: false, silent: true});
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
- self._updateDetailsView(fileInfo);
+ self._updateDetailsView(fileInfo.name);
}
});
} else {
diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js
index a00d907d0d..6910e5f2be 100644
--- a/apps/files/js/mainfileinfodetailview.js
+++ b/apps/files/js/mainfileinfodetailview.js
@@ -10,7 +10,7 @@
(function() {
var TEMPLATE =
- '{{name}}
' +
+ '{{name}}
' +
'');
+ _fileList: null,
+
+ /**
+ * File actions
+ *
+ * @type {OCA.Files.FileActions}
+ */
+ _fileActions: null,
+
+ events: {
+ 'click a.action-favorite': '_onClickFavorite',
+ 'click a.action-default': '_onClickDefaultAction'
+ },
+
+ template: function(data) {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template(data);
+ },
+
+ initialize: function(options) {
+ options = options || {};
+ this._fileList = options.fileList;
+ this._fileActions = options.fileActions;
+ if (!this._fileList) {
+ throw 'Missing requird parameter "fileList"';
+ }
+ if (!this._fileActions) {
+ throw 'Missing requird parameter "fileActions"';
+ }
+ },
+
+ _onClickFavorite: function(event) {
+ event.preventDefault();
+ this._fileActions.triggerAction('Favorite', this.model, this._fileList);
+ },
+
+ _onClickDefaultAction: function(event) {
+ event.preventDefault();
+ this._fileActions.triggerAction(null, this.model, this._fileList);
+ },
+
+ _onModelChanged: function() {
+ // simply re-render
+ this.render();
+ },
+
+ setFileInfo: function(fileInfo) {
+ if (this.model) {
+ this.model.off('change', this._onModelChanged, this);
+ }
+ this.model = fileInfo;
+ if (this.model) {
+ this.model.on('change', this._onModelChanged, this);
+ }
+ this.render();
},
/**
* Renders this details view
*/
render: function() {
- this.$el.empty();
-
- if (!this._template) {
- this._template = Handlebars.compile(TEMPLATE);
- }
-
- if (this._fileInfo) {
- var isFavorite = (this._fileInfo.tags || []).indexOf(OC.TAG_FAVORITE) >= 0;
- this.$el.append(this._template({
+ if (this.model) {
+ var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
+ this.$el.html(this.template({
nameLabel: t('files', 'Name'),
- name: this._fileInfo.name,
+ name: this.model.get('name'),
pathLabel: t('files', 'Path'),
- path: this._fileInfo.path,
+ path: this.model.get('path'),
sizeLabel: t('files', 'Size'),
- size: OC.Util.humanFileSize(this._fileInfo.size, true),
- altSize: n('files', '%n byte', '%n bytes', this._fileInfo.size),
+ size: OC.Util.humanFileSize(this.model.get('size'), true),
+ altSize: n('files', '%n byte', '%n bytes', this.model.get('size')),
dateLabel: t('files', 'Modified'),
- altDate: OC.Util.formatDate(this._fileInfo.mtime),
- date: OC.Util.relativeModifiedDate(this._fileInfo.mtime),
+ altDate: OC.Util.formatDate(this.model.get('mtime')),
+ date: OC.Util.relativeModifiedDate(this.model.get('mtime')),
starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'),
starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star')
}));
// TODO: we really need OC.Previews
var $iconDiv = this.$el.find('.thumbnail');
- if (this._fileInfo.mimetype !== 'httpd/unix-directory') {
+ if (!this.model.isDirectory()) {
// TODO: inject utility class?
FileList.lazyLoadPreview({
- path: this._fileInfo.path + '/' + this._fileInfo.name,
- mime: this._fileInfo.mimetype,
- etag: this._fileInfo.etag,
+ path: this.model.getFullPath(),
+ mime: this.model.get('mimetype'),
+ etag: this.model.get('etag'),
x: 50,
y: 50,
callback: function(previewUrl) {
@@ -90,7 +136,10 @@
$iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")');
}
this.$el.find('[title]').tooltip({placement: 'bottom'});
+ } else {
+ this.$el.empty();
}
+ this.delegateEvents();
}
});
diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js
index ec69ce4b96..d8552c71e4 100644
--- a/apps/files/js/tagsplugin.js
+++ b/apps/files/js/tagsplugin.js
@@ -77,7 +77,7 @@
var self = this;
// register "star" action
fileActions.registerAction({
- name: 'favorite',
+ name: 'Favorite',
displayName: 'Favorite',
mime: 'all',
permissions: OC.PERMISSION_READ,
@@ -124,6 +124,7 @@
toggleStar($actionEl, (newTags.indexOf(OC.TAG_FAVORITE) >= 0));
$file.attr('data-tags', newTags.join('|'));
$file.attr('data-favorite', !isFavorite);
+ context.fileInfoModel.set('tags', newTags);
fileInfo.tags = newTags;
});
}
@@ -145,6 +146,12 @@
$tr.find('td:first').prepend('');
return $tr;
};
+ var oldElementToFile = fileList.elementToFile;
+ fileList.elementToFile = function($el) {
+ var fileInfo = oldElementToFile.apply(this, arguments);
+ fileInfo.tags = $el.attr('data-tags') || [];
+ return fileInfo;
+ };
},
attach: function(fileList) {
diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js
index db1e24fd68..4261aa53c9 100644
--- a/apps/files/tests/js/detailsviewSpec.js
+++ b/apps/files/tests/js/detailsviewSpec.js
@@ -26,7 +26,7 @@ describe('OCA.Files.DetailsView tests', function() {
detailsView = new OCA.Files.DetailsView();
});
afterEach(function() {
- detailsView.destroy();
+ detailsView.remove();
detailsView = undefined;
});
it('renders itself empty when nothing registered', function() {
diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js
index 236cff6caf..1254843e66 100644
--- a/apps/files/tests/js/fileactionsSpec.js
+++ b/apps/files/tests/js/fileactionsSpec.js
@@ -27,6 +27,7 @@ describe('OCA.Files.FileActions tests', function() {
var $body = $('#testArea');
$body.append('');
$body.append('');
+ $body.append('');
// dummy files table
fileActions = new OCA.Files.FileActions();
fileActions.registerAction({
@@ -152,9 +153,10 @@ describe('OCA.Files.FileActions tests', function() {
});
});
describe('action handler', function() {
- var actionStub, $tr;
+ var actionStub, $tr, clock;
beforeEach(function() {
+ clock = sinon.useFakeTimers();
var fileData = {
id: 18,
type: 'file',
@@ -175,6 +177,12 @@ describe('OCA.Files.FileActions tests', function() {
});
$tr = fileList.add(fileData);
});
+ afterEach(function() {
+ OC.hideMenus();
+ // jump past animations
+ clock.tick(1000);
+ clock.restore();
+ });
it('passes context to action handler', function() {
$tr.find('.action-test').click();
expect(actionStub.calledOnce).toEqual(true);
@@ -184,6 +192,7 @@ describe('OCA.Files.FileActions tests', function() {
expect(context.fileList).toBeDefined();
expect(context.fileActions).toBeDefined();
expect(context.dir).toEqual('/subdir');
+ expect(context.fileInfoModel.get('name')).toEqual('testName.txt');
// when data-path is defined
actionStub.reset();
@@ -192,6 +201,22 @@ describe('OCA.Files.FileActions tests', function() {
context = actionStub.getCall(0).args[1];
expect(context.dir).toEqual('/somepath');
});
+ it('also triggers action handler when calling triggerAction()', function() {
+ var model = new OCA.Files.FileInfoModel({
+ id: 1,
+ name: 'Test.txt',
+ path: '/subdir',
+ mime: 'text/plain',
+ permissions: 31
+ });
+ fileActions.triggerAction('Test', model, fileList);
+
+ expect(actionStub.calledOnce).toEqual(true);
+ expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
+ expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList);
+ expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions);
+ expect(actionStub.getCall(0).args[1].fileInfoModel).toEqual(model);
+ });
describe('actions menu', function() {
it('shows actions menu inside row when clicking the menu trigger', function() {
expect($tr.find('td.filename .fileActionsMenu').length).toEqual(0);
@@ -203,12 +228,13 @@ describe('OCA.Files.FileActions tests', function() {
expect($tr.hasClass('mouseOver')).toEqual(true);
});
it('cleans up after hiding', function() {
- var clock = sinon.useFakeTimers();
+ var slideUpStub = sinon.stub($.fn, 'slideUp');
$tr.find('.action-menu').click();
expect($tr.find('.fileActionsMenu').length).toEqual(1);
OC.hideMenus();
// sliding animation
- clock.tick(500);
+ expect(slideUpStub.calledOnce).toEqual(true);
+ slideUpStub.getCall(0).args[1]();
expect($tr.hasClass('mouseOver')).toEqual(false);
expect($tr.find('.fileActionsMenu').length).toEqual(0);
});
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 57e1662640..3807338938 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -1912,6 +1912,20 @@ describe('OCA.Files.FileList tests', function() {
expect($tr.hasClass('highlighted')).toEqual(false);
expect(fileList._detailsView.getFileInfo()).toEqual(null);
});
+ it('returns the currently selected model instance when calling getModelForFile', function() {
+ var $tr = fileList.findFileEl('One.txt');
+ $tr.find('td.filename>a.name').click();
+
+ var model1 = fileList.getModelForFile('One.txt');
+ var model2 = fileList.getModelForFile('One.txt');
+ model1.set('test', true);
+
+ // it's the same model
+ expect(model2).toEqual(model1);
+
+ var model3 = fileList.getModelForFile($tr);
+ expect(model3).toEqual(model1);
+ });
});
describe('File actions', function() {
it('Clicking on a file name will trigger default action', function() {
diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js
index 10ad38097c..ca7384f620 100644
--- a/apps/files/tests/js/mainfileinfodetailviewSpec.js
+++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js
@@ -20,32 +20,37 @@
*/
describe('OCA.Files.MainFileInfoDetailView tests', function() {
- var view, tooltipStub, previewStub, fncLazyLoadPreview, fileListMock;
+ var view, tooltipStub, fileListMock, fileActions, fileList, testFileInfo;
beforeEach(function() {
tooltipStub = sinon.stub($.fn, 'tooltip');
fileListMock = sinon.mock(OCA.Files.FileList.prototype);
- view = new OCA.Files.MainFileInfoDetailView();
+ fileActions = new OCA.Files.FileActions();
+ fileList = new OCA.Files.FileList($(''), {
+ fileActions: fileActions
+ });
+ view = new OCA.Files.MainFileInfoDetailView({
+ fileList: fileList,
+ fileActions: fileActions
+ });
+ testFileInfo = new OCA.Files.FileInfoModel({
+ id: 5,
+ name: 'One.txt',
+ mimetype: 'text/plain',
+ permissions: 31,
+ path: '/subdir',
+ size: 123456789,
+ mtime: Date.UTC(2015, 6, 17, 1, 2, 0, 0)
+ });
});
afterEach(function() {
- view.destroy();
+ view.remove();
view = undefined;
tooltipStub.restore();
fileListMock.restore();
});
describe('rendering', function() {
- var testFileInfo;
- beforeEach(function() {
- view = new OCA.Files.MainFileInfoDetailView();
- testFileInfo = {
- id: 5,
- name: 'One.txt',
- path: '/subdir',
- size: 123456789,
- mtime: Date.UTC(2015, 6, 17, 1, 2, 0, 0)
- };
- });
it('displays basic info', function() {
var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3));
var dateExpected = OC.Util.formatDate(Date(Date.UTC(2015, 6, 17, 1, 2, 0, 0)));
@@ -59,39 +64,34 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
clock.restore();
});
it('displays favorite icon', function() {
- view.setFileInfo(_.extend(testFileInfo, {
- tags: [OC.TAG_FAVORITE]
- }));
+ testFileInfo.set('tags', [OC.TAG_FAVORITE]);
+ view.setFileInfo(testFileInfo);
expect(view.$el.find('.favorite img').attr('src'))
.toEqual(OC.imagePath('core', 'actions/starred'));
- view.setFileInfo(_.extend(testFileInfo, {
- tags: []
- }));
+ testFileInfo.set('tags', []);
+ view.setFileInfo(testFileInfo);
expect(view.$el.find('.favorite img').attr('src'))
.toEqual(OC.imagePath('core', 'actions/star'));
});
it('displays mime icon', function() {
// File
- view.setFileInfo(_.extend(testFileInfo, {
- mimetype: 'text/calendar'
- }));
+ testFileInfo.set('mimetype', 'text/calendar');
+ view.setFileInfo(testFileInfo);
expect(view.$el.find('.thumbnail').css('background-image'))
.toContain('filetypes/text-calendar.svg');
// Folder
- view.setFileInfo(_.extend(testFileInfo, {
- mimetype: 'httpd/unix-directory'
- }));
+ testFileInfo.set('mimetype', 'httpd/unix-directory');
+ view.setFileInfo(testFileInfo);
expect(view.$el.find('.thumbnail').css('background-image'))
.toContain('filetypes/folder.svg');
});
it('displays thumbnail', function() {
- view.setFileInfo(_.extend(testFileInfo, {
- mimetype: 'text/plain'
- }));
+ testFileInfo.set('mimetype', 'test/plain');
+ view.setFileInfo(testFileInfo);
var expectation = fileListMock.expects('lazyLoadPreview');
expectation.once();
@@ -100,5 +100,76 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
fileListMock.verify();
});
+ it('rerenders when changes are made on the model', function() {
+ view.setFileInfo(testFileInfo);
+
+ testFileInfo.set('tags', [OC.TAG_FAVORITE]);
+
+ expect(view.$el.find('.favorite img').attr('src'))
+ .toEqual(OC.imagePath('core', 'actions/starred'));
+
+ testFileInfo.set('tags', []);
+
+ expect(view.$el.find('.favorite img').attr('src'))
+ .toEqual(OC.imagePath('core', 'actions/star'));
+ });
+ it('unbinds change listener from model', function() {
+ view.setFileInfo(testFileInfo);
+ view.setFileInfo(new OCA.Files.FileInfoModel({
+ id: 999,
+ name: 'test.txt',
+ path: '/'
+ }));
+
+ // set value on old model
+ testFileInfo.set('tags', [OC.TAG_FAVORITE]);
+
+ // no change
+ expect(view.$el.find('.favorite img').attr('src'))
+ .toEqual(OC.imagePath('core', 'actions/star'));
+ });
+ });
+ describe('events', function() {
+ it('triggers default action when clicking on the thumbnail', function() {
+ var actionHandler = sinon.stub();
+
+ fileActions.registerAction({
+ name: 'Something',
+ mime: 'all',
+ permissions: OC.PERMISSION_READ,
+ actionHandler: actionHandler
+ });
+ fileActions.setDefault('text/plain', 'Something');
+
+ view.setFileInfo(testFileInfo);
+
+ view.$el.find('.thumbnail').click();
+
+ expect(actionHandler.calledOnce).toEqual(true);
+ expect(actionHandler.getCall(0).args[0]).toEqual('One.txt');
+ expect(actionHandler.getCall(0).args[1].fileList).toEqual(fileList);
+ expect(actionHandler.getCall(0).args[1].fileActions).toEqual(fileActions);
+ expect(actionHandler.getCall(0).args[1].fileInfoModel).toEqual(testFileInfo);
+ });
+ it('triggers "Favorite" action when clicking on the star', function() {
+ var actionHandler = sinon.stub();
+
+ fileActions.registerAction({
+ name: 'Favorite',
+ mime: 'all',
+ permissions: OC.PERMISSION_READ,
+ actionHandler: actionHandler
+ });
+
+ view.setFileInfo(testFileInfo);
+
+ view.$el.find('.action-favorite').click();
+
+ expect(actionHandler.calledOnce).toEqual(true);
+ expect(actionHandler.getCall(0).args[0]).toEqual('One.txt');
+ expect(actionHandler.getCall(0).args[1].fileList).toEqual(fileList);
+ expect(actionHandler.getCall(0).args[1].fileActions).toEqual(fileActions);
+ expect(actionHandler.getCall(0).args[1].fileInfoModel).toEqual(testFileInfo);
+ });
});
});
diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js
index e02de92375..5f4a21a4a5 100644
--- a/apps/files_sharing/js/sharetabview.js
+++ b/apps/files_sharing/js/sharetabview.js
@@ -12,30 +12,15 @@
var TEMPLATE =
'Owner: {{owner}}';
- /**
- * @class OCA.Sharing.ShareTabView
- * @classdesc
- *
- * Displays sharing information
- *
- */
- var ShareTabView = function(id) {
- this.initialize(id);
- };
/**
* @memberof OCA.Sharing
*/
- ShareTabView.prototype = _.extend({}, OCA.Files.DetailTabView.prototype,
+ var ShareTabView = OCA.Files.DetailTabView.extend(
/** @lends OCA.Sharing.ShareTabView.prototype */ {
- _template: null,
+ id: 'shareTabView',
+ className: 'tab shareTabView',
- /**
- * Initialize the details view
- */
- initialize: function() {
- OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments);
- this.$el.addClass('shareTabView');
- },
+ _template: null,
getLabel: function() {
return t('files_sharing', 'Sharing');
@@ -51,9 +36,9 @@
this._template = Handlebars.compile(TEMPLATE);
}
- if (this._fileInfo) {
+ if (this.model) {
this.$el.append(this._template({
- owner: this._fileInfo.shareOwner || OC.currentUser
+ owner: this.model.get('shareOwner') || OC.currentUser
}));
} else {