mirror of https://github.com/nextcloud/server.git
Add versions tab to files sidebar
- move versions to a tab in the files sidebar - added mechanism to auto-update the row in the FileList whenever values are set to the FileInfoModel given to the sidebar - updated tags/favorite action to make use of that new mechanismremotes/origin/db-empty-migrate
parent
e9e42fff61
commit
310d797284
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
OCA.Versions = OCA.Versions || {};
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
OCA.Versions.Util = {
|
||||
/**
|
||||
* Initialize the versions plugin.
|
||||
*
|
||||
* @param {OCA.Files.FileList} fileList file list to be extended
|
||||
*/
|
||||
attach: function(fileList) {
|
||||
if (fileList.id === 'trashbin' || fileList.id === 'files.public') {
|
||||
return;
|
||||
}
|
||||
|
||||
fileList.registerTabView(new OCA.Versions.VersionsTabView('versionsTabView'));
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
OC.Plugins.register('OCA.Files.FileList', OCA.Versions.Util);
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @memberof OCA.Versions
|
||||
*/
|
||||
var VersionCollection = OC.Backbone.Collection.extend({
|
||||
model: OCA.Versions.VersionModel,
|
||||
|
||||
/**
|
||||
* @var OCA.Files.FileInfoModel
|
||||
*/
|
||||
_fileInfo: null,
|
||||
|
||||
_endReached: false,
|
||||
_currentIndex: 0,
|
||||
|
||||
url: function() {
|
||||
var url = OC.generateUrl('/apps/files_versions/ajax/getVersions.php');
|
||||
var query = {
|
||||
source: this._fileInfo.getFullPath(),
|
||||
start: this._currentIndex
|
||||
};
|
||||
return url + '?' + OC.buildQueryString(query);
|
||||
},
|
||||
|
||||
setFileInfo: function(fileInfo) {
|
||||
this._fileInfo = fileInfo;
|
||||
// reset
|
||||
this._endReached = false;
|
||||
this._currentIndex = 0;
|
||||
},
|
||||
|
||||
getFileInfo: function() {
|
||||
return this._fileInfo;
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return !this._endReached;
|
||||
},
|
||||
|
||||
fetch: function(options) {
|
||||
if (!options || options.remove) {
|
||||
this._currentIndex = 0;
|
||||
}
|
||||
return OC.Backbone.Collection.prototype.fetch.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch the next set of results
|
||||
*/
|
||||
fetchNext: function() {
|
||||
if (!this.hasMoreResults()) {
|
||||
return null;
|
||||
}
|
||||
if (this._currentIndex === 0) {
|
||||
return this.fetch();
|
||||
}
|
||||
return this.fetch({remove: false});
|
||||
},
|
||||
|
||||
parse: function(result) {
|
||||
var results = _.map(result.data.versions, function(version) {
|
||||
var revision = parseInt(version.version, 10);
|
||||
return {
|
||||
id: revision,
|
||||
name: version.name,
|
||||
fullPath: version.path,
|
||||
timestamp: revision,
|
||||
size: version.size
|
||||
};
|
||||
});
|
||||
this._endReached = result.data.endReached;
|
||||
this._currentIndex += results.length;
|
||||
return results;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Versions = OCA.Versions || {};
|
||||
|
||||
OCA.Versions.VersionCollection = VersionCollection;
|
||||
})();
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @memberof OCA.Versions
|
||||
*/
|
||||
var VersionModel = OC.Backbone.Model.extend({
|
||||
|
||||
/**
|
||||
* Restores the original file to this revision
|
||||
*/
|
||||
revert: function(options) {
|
||||
options = options ? _.clone(options) : {};
|
||||
var model = this;
|
||||
var file = this.getFullPath();
|
||||
var revision = this.get('timestamp');
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: OC.generateUrl('/apps/files_versions/ajax/rollbackVersion.php'),
|
||||
dataType: 'json',
|
||||
data: {
|
||||
file: file,
|
||||
revision: revision
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.status === 'error') {
|
||||
if (options.error) {
|
||||
options.error.call(options.context, model, response, options);
|
||||
}
|
||||
model.trigger('error', model, response, options);
|
||||
} else {
|
||||
if (options.success) {
|
||||
options.success.call(options.context, model, response, options);
|
||||
}
|
||||
model.trigger('revert', model, response, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getFullPath: function() {
|
||||
return this.get('fullPath');
|
||||
},
|
||||
|
||||
getPreviewUrl: function() {
|
||||
var url = OC.generateUrl('/apps/files_versions/preview');
|
||||
var params = {
|
||||
file: this.get('fullPath'),
|
||||
version: this.get('timestamp')
|
||||
};
|
||||
return url + '?' + OC.buildQueryString(params);
|
||||
},
|
||||
|
||||
getDownloadUrl: function() {
|
||||
var url = OC.generateUrl('/apps/files_versions/download.php');
|
||||
var params = {
|
||||
file: this.get('fullPath'),
|
||||
revision: this.get('timestamp')
|
||||
};
|
||||
return url + '?' + OC.buildQueryString(params);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Versions = OCA.Versions || {};
|
||||
|
||||
OCA.Versions.VersionModel = VersionModel;
|
||||
})();
|
||||
|
@ -1,193 +0,0 @@
|
||||
/*
|
||||
* 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(){
|
||||
|
||||
// TODO: namespace all this as OCA.FileVersions
|
||||
|
||||
if ($('#isPublic').val()){
|
||||
// no versions actions in public mode
|
||||
// beware of https://github.com/owncloud/core/issues/4545
|
||||
// as enabling this might hang Chrome
|
||||
return;
|
||||
}
|
||||
|
||||
if (OCA.Files) {
|
||||
// Add versions button to 'files/index.php'
|
||||
OCA.Files.fileActions.register(
|
||||
'file',
|
||||
'Versions',
|
||||
OC.PERMISSION_UPDATE,
|
||||
function() {
|
||||
// Specify icon for hitory button
|
||||
return OC.imagePath('core','actions/history');
|
||||
}, function(filename, context){
|
||||
// Action to perform when clicked
|
||||
if (scanFiles.scanning){return;}//workaround to prevent additional http request block scanning feedback
|
||||
|
||||
var file = context.dir.replace(/(?!<=\/)$|\/$/, '/' + filename);
|
||||
var createDropDown = true;
|
||||
// Check if drop down is already visible for a different file
|
||||
if (($('#dropdown').length > 0) ) {
|
||||
if ( $('#dropdown').hasClass('drop-versions') && file == $('#dropdown').data('file')) {
|
||||
createDropDown = false;
|
||||
}
|
||||
$('#dropdown').slideUp(OC.menuSpeed);
|
||||
$('#dropdown').remove();
|
||||
$('tr').removeClass('mouseOver');
|
||||
}
|
||||
|
||||
if(createDropDown === true) {
|
||||
createVersionsDropdown(filename, file, context.fileList);
|
||||
}
|
||||
}, t('files_versions', 'Versions')
|
||||
);
|
||||
}
|
||||
|
||||
$(document).on("click", 'span[class="revertVersion"]', function() {
|
||||
var revision = $(this).attr('id');
|
||||
var file = $(this).attr('value');
|
||||
revertFile(file, revision);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function revertFile(file, revision) {
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: OC.linkTo('files_versions', 'ajax/rollbackVersion.php'),
|
||||
dataType: 'json',
|
||||
data: {file: file, revision: revision},
|
||||
async: false,
|
||||
success: function(response) {
|
||||
if (response.status === 'error') {
|
||||
OC.Notification.show( t('files_version', 'Failed to revert {file} to revision {timestamp}.', {file:file, timestamp:formatDate(revision * 1000)}) );
|
||||
} else {
|
||||
$('#dropdown').slideUp(OC.menuSpeed, function() {
|
||||
$('#dropdown').closest('tr').find('.modified:first').html(relative_modified_date(revision));
|
||||
$('#dropdown').remove();
|
||||
$('tr').removeClass('mouseOver');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function goToVersionPage(url){
|
||||
window.location.assign(url);
|
||||
}
|
||||
|
||||
function createVersionsDropdown(filename, files, fileList) {
|
||||
|
||||
var start = 0;
|
||||
var fileEl;
|
||||
|
||||
var html = '<div id="dropdown" class="drop drop-versions" data-file="'+escapeHTML(files)+'">';
|
||||
html += '<div id="private">';
|
||||
html += '<ul id="found_versions">';
|
||||
html += '</ul>';
|
||||
html += '</div>';
|
||||
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.addClass('mouseOver');
|
||||
$(html).appendTo(fileEl.find('td.filename'));
|
||||
} else {
|
||||
$(html).appendTo($('thead .share'));
|
||||
}
|
||||
|
||||
getVersions(start);
|
||||
start = start + 5;
|
||||
|
||||
$("#show-more-versions").click(function() {
|
||||
//get more versions
|
||||
getVersions(start);
|
||||
start = start + 5;
|
||||
});
|
||||
|
||||
function getVersions(start) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: OC.filePath('files_versions', 'ajax', 'getVersions.php'),
|
||||
dataType: 'json',
|
||||
data: {source: files, start: start},
|
||||
async: false,
|
||||
success: function(result) {
|
||||
var versions = result.data.versions;
|
||||
if (result.data.endReached === true) {
|
||||
$("#show-more-versions").css("display", "none");
|
||||
} else {
|
||||
$("#show-more-versions").css("display", "block");
|
||||
}
|
||||
if (versions) {
|
||||
$.each(versions, function(index, row) {
|
||||
addVersion(row);
|
||||
});
|
||||
} else {
|
||||
$('<div style="text-align:center;">'+ t('files_versions', 'No other versions available') + '</div>').appendTo('#dropdown');
|
||||
}
|
||||
$('#found_versions').change(function() {
|
||||
var revision = parseInt($(this).val());
|
||||
revertFile(files, revision);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addVersion( revision ) {
|
||||
var title = formatDate(revision.version*1000);
|
||||
var name ='<span class="versionDate" title="' + title + '">' + revision.humanReadableTimestamp + '</span>';
|
||||
|
||||
var path = OC.filePath('files_versions', '', 'download.php');
|
||||
|
||||
var preview = '<img class="preview" src="'+revision.preview+'"/>';
|
||||
|
||||
var download ='<a href="' + path + "?file=" + encodeURIComponent(files) + '&revision=' + revision.version + '">';
|
||||
download+='<img';
|
||||
download+=' src="' + OC.imagePath('core', 'actions/download') + '"';
|
||||
download+=' name="downloadVersion" />';
|
||||
download+=name;
|
||||
download+='</a>';
|
||||
|
||||
var revert='<span class="revertVersion"';
|
||||
revert+=' id="' + revision.version + '">';
|
||||
revert+='<img';
|
||||
revert+=' src="' + OC.imagePath('core', 'actions/history') + '"';
|
||||
revert+=' name="revertVersion"';
|
||||
revert+='/>'+t('files_versions', 'Restore')+'</span>';
|
||||
|
||||
var version=$('<li/>');
|
||||
version.attr('value', revision.version);
|
||||
version.html(preview + download + revert);
|
||||
// add file here for proper name escaping
|
||||
version.find('span.revertVersion').attr('value', files);
|
||||
|
||||
version.appendTo('#found_versions');
|
||||
}
|
||||
|
||||
$('#dropdown').slideDown(1000);
|
||||
}
|
||||
|
||||
$(this).click(
|
||||
function(event) {
|
||||
if ($('#dropdown').has(event.target).length === 0 && $('#dropdown').hasClass('drop-versions')) {
|
||||
$('#dropdown').slideUp(OC.menuSpeed, function() {
|
||||
$('#dropdown').remove();
|
||||
$('tr').removeClass('mouseOver');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var TEMPLATE_ITEM =
|
||||
'<li data-revision="{{timestamp}}">' +
|
||||
'<img class="preview" src="{{previewUrl}}"/>' +
|
||||
'<a href="{{downloadUrl}}" class="downloadVersion"><img src="{{downloadIconUrl}}" />' +
|
||||
'<span class="versiondate has-tooltip" title="{{formattedTimestamp}}">{{relativeTimestamp}}</span>' +
|
||||
'</a>' +
|
||||
'<a href="#" class="revertVersion"><img src="{{revertIconUrl}}" />{{revertLabel}}</a>' +
|
||||
'</li>';
|
||||
|
||||
var TEMPLATE =
|
||||
'<ul class="versions"></ul>' +
|
||||
'<div class="clear-float"></div>' +
|
||||
'<div class="empty hidden">{{emptyResultLabel}}</div>' +
|
||||
'<input type="button" class="showMoreVersions hidden" value="{{moreVersionsLabel}}"' +
|
||||
' name="show-more-versions" id="show-more-versions" />' +
|
||||
'<div class="loading hidden" style="height: 50px"></div>';
|
||||
|
||||
/**
|
||||
* @memberof OCA.Versions
|
||||
*/
|
||||
var VersionsTabView = OCA.Files.DetailTabView.extend(
|
||||
/** @lends OCA.Versions.VersionsTabView.prototype */ {
|
||||
id: 'versionsTabView',
|
||||
className: 'tab versionsTabView',
|
||||
|
||||
_template: null,
|
||||
|
||||
$versionsContainer: null,
|
||||
|
||||
events: {
|
||||
'click .revertVersion': '_onClickRevertVersion',
|
||||
'click .showMoreVersions': '_onClickShowMoreVersions'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.collection = new OCA.Versions.VersionCollection();
|
||||
this.collection.on('request', this._onRequest, this);
|
||||
this.collection.on('sync', this._onEndRequest, this);
|
||||
this.collection.on('update', this._onUpdate, this);
|
||||
this.collection.on('error', this._onError, this);
|
||||
this.collection.on('add', this._onAddModel, this);
|
||||
},
|
||||
|
||||
getLabel: function() {
|
||||
return t('files_versions', 'Versions');
|
||||
},
|
||||
|
||||
nextPage: function() {
|
||||
if (this._loading || !this.collection.hasMoreResults()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.collection.getFileInfo() && this.collection.getFileInfo().isDirectory()) {
|
||||
return;
|
||||
}
|
||||
this.collection.fetchNext();
|
||||
},
|
||||
|
||||
_onClickShowMoreVersions: function(ev) {
|
||||
ev.preventDefault();
|
||||
this.nextPage();
|
||||
},
|
||||
|
||||
_onClickRevertVersion: function(ev) {
|
||||
var self = this;
|
||||
var $target = $(ev.target);
|
||||
var fileInfoModel = this.collection.getFileInfo();
|
||||
var revision;
|
||||
if (!$target.is('li')) {
|
||||
$target = $target.closest('li');
|
||||
}
|
||||
|
||||
ev.preventDefault();
|
||||
revision = $target.attr('data-revision');
|
||||
|
||||
var versionModel = this.collection.get(revision);
|
||||
versionModel.revert({
|
||||
success: function() {
|
||||
// reset and re-fetch the updated collection
|
||||
self.collection.setFileInfo(fileInfoModel);
|
||||
self.collection.fetch();
|
||||
|
||||
// update original model
|
||||
fileInfoModel.trigger('busy', fileInfoModel, false);
|
||||
fileInfoModel.set({
|
||||
size: versionModel.get('size'),
|
||||
mtime: versionModel.get('timestamp') * 1000,
|
||||
// temp dummy, until we can do a PROPFIND
|
||||
etag: versionModel.get('id') + versionModel.get('timestamp')
|
||||
});
|
||||
},
|
||||
|
||||
error: function() {
|
||||
OC.Notification.showTemporary(
|
||||
t('files_version', 'Failed to revert {file} to revision {timestamp}.', {
|
||||
file: versionModel.getFullPath(),
|
||||
timestamp: OC.Util.formatDate(versionModel.get('timestamp') * 1000)
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// spinner
|
||||
this._toggleLoading(true);
|
||||
fileInfoModel.trigger('busy', fileInfoModel, true);
|
||||
},
|
||||
|
||||
_toggleLoading: function(state) {
|
||||
this._loading = state;
|
||||
this.$el.find('.loading').toggleClass('hidden', !state);
|
||||
},
|
||||
|
||||
_onRequest: function() {
|
||||
this._toggleLoading(true);
|
||||
this.$el.find('.showMoreVersions').addClass('hidden');
|
||||
},
|
||||
|
||||
_onEndRequest: function() {
|
||||
this._toggleLoading(false);
|
||||
this.$el.find('.empty').toggleClass('hidden', !!this.collection.length);
|
||||
this.$el.find('.showMoreVersions').toggleClass('hidden', !this.collection.hasMoreResults());
|
||||
},
|
||||
|
||||
_onAddModel: function(model) {
|
||||
this.$versionsContainer.append(this.itemTemplate(this._formatItem(model)));
|
||||
},
|
||||
|
||||
template: function(data) {
|
||||
if (!this._template) {
|
||||
this._template = Handlebars.compile(TEMPLATE);
|
||||
}
|
||||
|
||||
return this._template(data);
|
||||
},
|
||||
|
||||
itemTemplate: function(data) {
|
||||
if (!this._itemTemplate) {
|
||||
this._itemTemplate = Handlebars.compile(TEMPLATE_ITEM);
|
||||
}
|
||||
|
||||
return this._itemTemplate(data);
|
||||
},
|
||||
|
||||
setFileInfo: function(fileInfo) {
|
||||
if (fileInfo) {
|
||||
this.render();
|
||||
this.collection.setFileInfo(fileInfo);
|
||||
this.collection.reset({silent: true});
|
||||
this.nextPage();
|
||||
} else {
|
||||
this.render();
|
||||
this.collection.reset();
|
||||
}
|
||||
},
|
||||
|
||||
_formatItem: function(version) {
|
||||
var timestamp = version.get('timestamp') * 1000;
|
||||
return _.extend({
|
||||
formattedTimestamp: OC.Util.formatDate(timestamp),
|
||||
relativeTimestamp: OC.Util.relativeModifiedDate(timestamp),
|
||||
downloadUrl: version.getDownloadUrl(),
|
||||
downloadIconUrl: OC.imagePath('core', 'actions/download'),
|
||||
revertIconUrl: OC.imagePath('core', 'actions/history'),
|
||||
previewUrl: version.getPreviewUrl(),
|
||||
revertLabel: t('files_versions', 'Restore'),
|
||||
}, version.attributes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders this details view
|
||||
*/
|
||||
render: function() {
|
||||
this.$el.html(this.template({
|
||||
emptyResultLabel: t('files_versions', 'No other versions available'),
|
||||
moreVersionsLabel: t('files_versions', 'More versions...')
|
||||
}));
|
||||
this.$el.find('.has-tooltip').tooltip();
|
||||
this.$versionsContainer = this.$el.find('ul.versions');
|
||||
this.delegateEvents();
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Versions = OCA.Versions || {};
|
||||
|
||||
OCA.Versions.VersionsTabView = VersionsTabView;
|
||||
})();
|
||||
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
describe('OCA.Versions.VersionCollection', function() {
|
||||
var VersionCollection = OCA.Versions.VersionCollection;
|
||||
var collection, fileInfoModel;
|
||||
|
||||
beforeEach(function() {
|
||||
fileInfoModel = new OCA.Files.FileInfoModel({
|
||||
path: '/subdir',
|
||||
name: 'some file.txt'
|
||||
});
|
||||
collection = new VersionCollection();
|
||||
collection.setFileInfo(fileInfoModel);
|
||||
});
|
||||
it('fetches the next page', function() {
|
||||
collection.fetchNext();
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
expect(fakeServer.requests[0].url).toEqual(
|
||||
OC.generateUrl('apps/files_versions/ajax/getVersions.php') +
|
||||
'?source=%2Fsubdir%2Fsome%20file.txt&start=0'
|
||||
);
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
data: {
|
||||
endReached: false,
|
||||
versions: [{
|
||||
version: 10000000,
|
||||
size: 123,
|
||||
name: 'some file.txt',
|
||||
fullPath: '/subdir/some file.txt'
|
||||
},{
|
||||
version: 15000000,
|
||||
size: 150,
|
||||
name: 'some file.txt',
|
||||
path: '/subdir/some file.txt'
|
||||
}]
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(collection.length).toEqual(2);
|
||||
expect(collection.hasMoreResults()).toEqual(true);
|
||||
|
||||
collection.fetchNext();
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(2);
|
||||
expect(fakeServer.requests[1].url).toEqual(
|
||||
OC.generateUrl('apps/files_versions/ajax/getVersions.php') +
|
||||
'?source=%2Fsubdir%2Fsome%20file.txt&start=2'
|
||||
);
|
||||
fakeServer.requests[1].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
data: {
|
||||
endReached: true,
|
||||
versions: [{
|
||||
version: 18000000,
|
||||
size: 123,
|
||||
name: 'some file.txt',
|
||||
path: '/subdir/some file.txt'
|
||||
}]
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(collection.length).toEqual(3);
|
||||
expect(collection.hasMoreResults()).toEqual(false);
|
||||
|
||||
collection.fetchNext();
|
||||
|
||||
// no further requests
|
||||
expect(fakeServer.requests.length).toEqual(2);
|
||||
});
|
||||
it('properly parses the results', function() {
|
||||
collection.fetchNext();
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
expect(fakeServer.requests[0].url).toEqual(
|
||||
OC.generateUrl('apps/files_versions/ajax/getVersions.php') +
|
||||
'?source=%2Fsubdir%2Fsome%20file.txt&start=0'
|
||||
);
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
data: {
|
||||
endReached: false,
|
||||
versions: [{
|
||||
version: 10000000,
|
||||
size: 123,
|
||||
name: 'some file.txt',
|
||||
path: '/subdir/some file.txt'
|
||||
},{
|
||||
version: 15000000,
|
||||
size: 150,
|
||||
name: 'some file.txt',
|
||||
path: '/subdir/some file.txt'
|
||||
}]
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(collection.length).toEqual(2);
|
||||
|
||||
var model = collection.at(0);
|
||||
expect(model.get('id')).toEqual(10000000);
|
||||
expect(model.get('timestamp')).toEqual(10000000);
|
||||
expect(model.get('name')).toEqual('some file.txt');
|
||||
expect(model.get('fullPath')).toEqual('/subdir/some file.txt');
|
||||
expect(model.get('size')).toEqual(123);
|
||||
|
||||
model = collection.at(1);
|
||||
expect(model.get('id')).toEqual(15000000);
|
||||
expect(model.get('timestamp')).toEqual(15000000);
|
||||
expect(model.get('name')).toEqual('some file.txt');
|
||||
expect(model.get('fullPath')).toEqual('/subdir/some file.txt');
|
||||
expect(model.get('size')).toEqual(150);
|
||||
});
|
||||
it('resets page counted when setting a new file info model', function() {
|
||||
collection.fetchNext();
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
data: {
|
||||
endReached: true,
|
||||
versions: [{
|
||||
version: 18000000,
|
||||
size: 123,
|
||||
name: 'some file.txt',
|
||||
path: '/subdir/some file.txt'
|
||||
}]
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(collection.hasMoreResults()).toEqual(false);
|
||||
|
||||
collection.setFileInfo(fileInfoModel);
|
||||
|
||||
expect(collection.hasMoreResults()).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
describe('OCA.Versions.VersionModel', function() {
|
||||
var VersionModel = OCA.Versions.VersionModel;
|
||||
var model;
|
||||
|
||||
beforeEach(function() {
|
||||
model = new VersionModel({
|
||||
id: 10000000,
|
||||
timestamp: 10000000,
|
||||
fullPath: '/subdir/some file.txt',
|
||||
name: 'some file.txt',
|
||||
size: 150
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the full path', function() {
|
||||
expect(model.getFullPath()).toEqual('/subdir/some file.txt');
|
||||
});
|
||||
it('returns the preview url', function() {
|
||||
expect(model.getPreviewUrl())
|
||||
.toEqual(OC.generateUrl('/apps/files_versions/preview') +
|
||||
'?file=%2Fsubdir%2Fsome%20file.txt&version=10000000'
|
||||
);
|
||||
});
|
||||
it('returns the download url', function() {
|
||||
expect(model.getDownloadUrl())
|
||||
.toEqual(OC.generateUrl('/apps/files_versions/download.php') +
|
||||
'?file=%2Fsubdir%2Fsome%20file.txt&revision=10000000'
|
||||
);
|
||||
});
|
||||
describe('reverting', function() {
|
||||
var revertEventStub;
|
||||
var successStub;
|
||||
var errorStub;
|
||||
|
||||
beforeEach(function() {
|
||||
revertEventStub = sinon.stub();
|
||||
errorStub = sinon.stub();
|
||||
successStub = sinon.stub();
|
||||
|
||||
model.on('revert', revertEventStub);
|
||||
model.on('error', errorStub);
|
||||
});
|
||||
it('tells the server to revert when calling the revert method', function() {
|
||||
model.revert({
|
||||
success: successStub
|
||||
});
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
expect(fakeServer.requests[0].url)
|
||||
.toEqual(
|
||||
OC.generateUrl('/apps/files_versions/ajax/rollbackVersion.php') +
|
||||
'?file=%2Fsubdir%2Fsome+file.txt&revision=10000000'
|
||||
);
|
||||
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
})
|
||||
);
|
||||
|
||||
expect(revertEventStub.calledOnce).toEqual(true);
|
||||
expect(successStub.calledOnce).toEqual(true);
|
||||
expect(errorStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('triggers error event when server returns a failure', function() {
|
||||
model.revert({
|
||||
success: successStub
|
||||
});
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'error',
|
||||
})
|
||||
);
|
||||
|
||||
expect(revertEventStub.notCalled).toEqual(true);
|
||||
expect(successStub.notCalled).toEqual(true);
|
||||
expect(errorStub.calledOnce).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
describe('OCA.Versions.VersionsTabView', function() {
|
||||
var VersionCollection = OCA.Versions.VersionCollection;
|
||||
var VersionModel = OCA.Versions.VersionModel;
|
||||
var VersionsTabView = OCA.Versions.VersionsTabView;
|
||||
|
||||
var fetchStub, fileInfoModel, tabView, testVersions, clock;
|
||||
|
||||
beforeEach(function() {
|
||||
clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3));
|
||||
var time1 = Date.UTC(2015, 6, 17, 1, 2, 0, 3) / 1000;
|
||||
var time2 = Date.UTC(2015, 6, 15, 1, 2, 0, 3) / 1000;
|
||||
|
||||
var version1 = new VersionModel({
|
||||
id: time1,
|
||||
timestamp: time1,
|
||||
name: 'some file.txt',
|
||||
size: 140,
|
||||
fullPath: '/subdir/some file.txt'
|
||||
});
|
||||
var version2 = new VersionModel({
|
||||
id: time2,
|
||||
timestamp: time2,
|
||||
name: 'some file.txt',
|
||||
size: 150,
|
||||
fullPath: '/subdir/some file.txt'
|
||||
});
|
||||
|
||||
testVersions = [version1, version2];
|
||||
|
||||
fetchStub = sinon.stub(VersionCollection.prototype, 'fetch');
|
||||
fileInfoModel = new OCA.Files.FileInfoModel({
|
||||
id: 123,
|
||||
name: 'test.txt'
|
||||
});
|
||||
tabView = new VersionsTabView();
|
||||
tabView.render();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
fetchStub.restore();
|
||||
tabView.remove();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
describe('rendering', function() {
|
||||
it('reloads matching versions when setting file info model', function() {
|
||||
tabView.setFileInfo(fileInfoModel);
|
||||
expect(fetchStub.calledOnce).toEqual(true);
|
||||
});
|
||||
|
||||
it('renders loading icon while fetching versions', function() {
|
||||
tabView.setFileInfo(fileInfoModel);
|
||||
tabView.collection.trigger('request');
|
||||
|
||||
expect(tabView.$el.find('.loading').length).toEqual(1);
|
||||
expect(tabView.$el.find('.versions li').length).toEqual(0);
|
||||
});
|
||||
|
||||
it('renders versions', function() {
|
||||
|
||||
tabView.setFileInfo(fileInfoModel);
|
||||
tabView.collection.set(testVersions);
|
||||
|
||||
var version1 = testVersions[0];
|
||||
var version2 = testVersions[1];
|
||||
var $versions = tabView.$el.find('.versions>li');
|
||||
expect($versions.length).toEqual(2);
|
||||
var $item = $versions.eq(0);
|
||||
expect($item.find('.downloadVersion').attr('href')).toEqual(version1.getDownloadUrl());
|
||||
expect($item.find('.versiondate').text()).toEqual('a few seconds ago');
|
||||
expect($item.find('.revertVersion').length).toEqual(1);
|
||||
expect($item.find('.preview').attr('src')).toEqual(version1.getPreviewUrl());
|
||||
|
||||
$item = $versions.eq(1);
|
||||
expect($item.find('.downloadVersion').attr('href')).toEqual(version2.getDownloadUrl());
|
||||
expect($item.find('.versiondate').text()).toEqual('2 days ago');
|
||||
expect($item.find('.revertVersion').length).toEqual(1);
|
||||
expect($item.find('.preview').attr('src')).toEqual(version2.getPreviewUrl());
|
||||
});
|
||||
});
|
||||
|
||||
describe('More versions', function() {
|
||||
var hasMoreResultsStub;
|
||||
|
||||
beforeEach(function() {
|
||||
tabView.collection.set(testVersions);
|
||||
hasMoreResultsStub = sinon.stub(VersionCollection.prototype, 'hasMoreResults');
|
||||
});
|
||||
afterEach(function() {
|
||||
hasMoreResultsStub.restore();
|
||||
});
|
||||
|
||||
it('shows "More versions" button when more versions are available', function() {
|
||||
hasMoreResultsStub.returns(true);
|
||||
tabView.collection.trigger('sync');
|
||||
|
||||
expect(tabView.$el.find('.showMoreVersions').hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('does not show "More versions" button when more versions are available', function() {
|
||||
hasMoreResultsStub.returns(false);
|
||||
tabView.collection.trigger('sync');
|
||||
|
||||
expect(tabView.$el.find('.showMoreVersions').hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('fetches and appends the next page when clicking the "More" button', function() {
|
||||
hasMoreResultsStub.returns(true);
|
||||
|
||||
expect(fetchStub.notCalled).toEqual(true);
|
||||
|
||||
tabView.$el.find('.showMoreVersions').click();
|
||||
|
||||
expect(fetchStub.calledOnce).toEqual(true);
|
||||
});
|
||||
it('appends version to the list when added to collection', function() {
|
||||
var time3 = Date.UTC(2015, 6, 10, 1, 0, 0, 0) / 1000;
|
||||
|
||||
var version3 = new VersionModel({
|
||||
id: time3,
|
||||
timestamp: time3,
|
||||
name: 'some file.txt',
|
||||
size: 54,
|
||||
fullPath: '/subdir/some file.txt'
|
||||
});
|
||||
|
||||
tabView.collection.add(version3);
|
||||
|
||||
expect(tabView.$el.find('.versions>li').length).toEqual(3);
|
||||
|
||||
var $item = tabView.$el.find('.versions>li').eq(2);
|
||||
expect($item.find('.downloadVersion').attr('href')).toEqual(version3.getDownloadUrl());
|
||||
expect($item.find('.versiondate').text()).toEqual('7 days ago');
|
||||
expect($item.find('.revertVersion').length).toEqual(1);
|
||||
expect($item.find('.preview').attr('src')).toEqual(version3.getPreviewUrl());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reverting', function() {
|
||||
var revertStub;
|
||||
|
||||
beforeEach(function() {
|
||||
revertStub = sinon.stub(VersionModel.prototype, 'revert');
|
||||
tabView.setFileInfo(fileInfoModel);
|
||||
tabView.collection.set(testVersions);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
revertStub.restore();
|
||||
});
|
||||
|
||||
it('tells the model to revert when clicking "Revert"', function() {
|
||||
tabView.$el.find('.revertVersion').eq(1).click();
|
||||
|
||||
expect(revertStub.calledOnce).toEqual(true);
|
||||
});
|
||||
it('triggers busy state during revert', function() {
|
||||
var busyStub = sinon.stub();
|
||||
fileInfoModel.on('busy', busyStub);
|
||||
|
||||
tabView.$el.find('.revertVersion').eq(1).click();
|
||||
|
||||
expect(busyStub.calledOnce).toEqual(true);
|
||||
expect(busyStub.calledWith(fileInfoModel, true)).toEqual(true);
|
||||
|
||||
busyStub.reset();
|
||||
revertStub.getCall(0).args[0].success();
|
||||
|
||||
expect(busyStub.calledOnce).toEqual(true);
|
||||
expect(busyStub.calledWith(fileInfoModel, false)).toEqual(true);
|
||||
});
|
||||
it('updates the file info model with the information from the reverted revision', function() {
|
||||
var changeStub = sinon.stub();
|
||||
fileInfoModel.on('change', changeStub);
|
||||
|
||||
tabView.$el.find('.revertVersion').eq(1).click();
|
||||
|
||||
expect(changeStub.notCalled).toEqual(true);
|
||||
|
||||
revertStub.getCall(0).args[0].success();
|
||||
|
||||
expect(changeStub.calledOnce).toEqual(true);
|
||||
var changes = changeStub.getCall(0).args[0].changed;
|
||||
expect(changes.size).toEqual(150);
|
||||
expect(changes.mtime).toEqual(testVersions[1].get('timestamp') * 1000);
|
||||
expect(changes.etag).toBeDefined();
|
||||
});
|
||||
it('shows notification on revert error', function() {
|
||||
var notificationStub = sinon.stub(OC.Notification, 'showTemporary');
|
||||
|
||||
tabView.$el.find('.revertVersion').eq(1).click();
|
||||
|
||||
revertStub.getCall(0).args[0].error();
|
||||
|
||||
expect(notificationStub.calledOnce).toEqual(true);
|
||||
|
||||
notificationStub.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue