summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Petry <pvince81@owncloud.com>2014-02-12 14:50:23 +0100
committerVincent Petry <pvince81@owncloud.com>2014-04-28 14:55:00 +0200
commit9c2fbea6a4396a29ce8c966c9ea7646aa8fc9be5 (patch)
tree5ec4d52ab001b7078397bc676d8fdac6a3bfdde9
parent9f62059efa869ff677130f06bf1b46be49950515 (diff)
downloadnextcloud-server-9c2fbea6a4396a29ce8c966c9ea7646aa8fc9be5.tar.gz
nextcloud-server-9c2fbea6a4396a29ce8c966c9ea7646aa8fc9be5.zip
Fix file selection for infinite scrolling
- moved file selection code to FileList - fix selection summary when all files are selected - nextPage now auto-selects files if "select all" checkbox is checked - fixed trashbin to use the same selection logic as FileList
-rw-r--r--apps/files/css/files.css1
-rw-r--r--apps/files/js/filelist.js266
-rw-r--r--apps/files/js/files.js202
-rw-r--r--apps/files/tests/js/filelistSpec.js217
-rw-r--r--apps/files/tests/js/filesSpec.js28
-rw-r--r--apps/files_trashbin/js/filelist.js126
-rw-r--r--apps/files_trashbin/js/trash.js163
-rw-r--r--core/js/js.js2
8 files changed, 626 insertions, 379 deletions
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 474f1af0720..533050691d5 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -310,7 +310,6 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; }
/* Actions for selected files */
.selectedActions {
- display: none;
position: absolute;
top: -1px;
right: 0;
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 223f4bb4409..02754d7acbb 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -9,7 +9,7 @@
*/
/* global OC, t, n, FileList, FileActions, Files, FileSummary, BreadCrumb */
-/* global procesSelection, dragOptions, folderDropOptions */
+/* global dragOptions, folderDropOptions */
window.FileList = {
appName: t('files', 'Files'),
isEmpty: true,
@@ -60,6 +60,142 @@ window.FileList = {
var width = $(this).width();
FileList.breadcrumb.resize(width, false);
});
+
+ this.$fileList.on('click','td.filename a', this._onClickFile);
+ this.$fileList.on('change', 'td.filename input:checkbox', this._onClickFileCheckbox);
+ this.$el.find('#select_all').click(this._onClickSelectAll);
+ this.$el.find('.download').click(this._onClickDownloadSelected);
+ this.$el.find('.delete-selected').click(this._onClickDeleteSelected);
+ },
+
+ /**
+ * Event handler for when clicking on files to select them
+ */
+ _onClickFile: function(event) {
+ if (event.ctrlKey || event.shiftKey) {
+ event.preventDefault();
+ if (event.shiftKey) {
+ var last = $(FileList._lastChecked).parent().parent().prevAll().length;
+ var first = $(this).parent().parent().prevAll().length;
+ var start = Math.min(first, last);
+ var end = Math.max(first, last);
+ var rows = $(this).parent().parent().parent().children('tr');
+ for (var i = start; i < end; i++) {
+ $(rows).each(function(index) {
+ if (index === i) {
+ var checkbox = $(this).children().children('input:checkbox');
+ $(checkbox).attr('checked', 'checked');
+ $(checkbox).parent().parent().addClass('selected');
+ }
+ });
+ }
+ }
+ var checkbox = $(this).parent().children('input:checkbox');
+ FileList._lastChecked = checkbox;
+ if ($(checkbox).attr('checked')) {
+ $(checkbox).removeAttr('checked');
+ $(checkbox).parent().parent().removeClass('selected');
+ $('#select_all').removeAttr('checked');
+ } else {
+ $(checkbox).attr('checked', 'checked');
+ $(checkbox).parent().parent().toggleClass('selected');
+ var selectedCount = $('td.filename input:checkbox:checked').length;
+ if (selectedCount === $('td.filename input:checkbox').length) {
+ $('#select_all').attr('checked', 'checked');
+ }
+ }
+ FileList.updateSelectionSummary();
+ } else {
+ var filename=$(this).parent().parent().attr('data-file');
+ var tr = FileList.findFileEl(filename);
+ var renaming=tr.data('renaming');
+ if (!renaming) {
+ FileActions.currentFile = $(this).parent();
+ var mime=FileActions.getCurrentMimeType();
+ var type=FileActions.getCurrentType();
+ var permissions = FileActions.getCurrentPermissions();
+ var action=FileActions.getDefault(mime,type, permissions);
+ if (action) {
+ event.preventDefault();
+ action(filename);
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Event handler for when clicking on a file's checkbox
+ */
+ _onClickFileCheckbox: function(event) {
+ // FIXME: not sure what the difference is supposed to be with FileList._onClickFile
+ if (event.shiftKey) {
+ var last = $(FileList._lastChecked).parent().parent().prevAll().length;
+ var first = $(this).parent().parent().prevAll().length;
+ var start = Math.min(first, last);
+ var end = Math.max(first, last);
+ var rows = $(this).parent().parent().parent().children('tr');
+ for (var i = start; i < end; i++) {
+ $(rows).each(function(index) {
+ if (index === i) {
+ var checkbox = $(this).children().children('input:checkbox');
+ $(checkbox).attr('checked', 'checked');
+ $(checkbox).parent().parent().addClass('selected');
+ }
+ });
+ }
+ }
+ var selectedCount=$('td.filename input:checkbox:checked').length;
+ $(this).parent().parent().toggleClass('selected');
+ if (!$(this).attr('checked')) {
+ $('#select_all').attr('checked',false);
+ } else {
+ if (selectedCount===$('td.filename input:checkbox').length) {
+ $('#select_all').attr('checked',true);
+ }
+ }
+ FileList.updateSelectionSummary();
+ },
+
+ /**
+ * Event handler for when selecting/deselecting all files
+ */
+ _onClickSelectAll: function(e) {
+ var checked = $(this).prop('checked');
+ FileList.$fileList.find('td.filename input:checkbox').prop('checked', checked)
+ .parent().parent().toggleClass('selected', checked);
+ FileList.updateSelectionSummary();
+ },
+
+ /**
+ * Event handler for when clicking on "Download" for the selected files
+ */
+ _onClickDownloadSelected: function(event) {
+ var files;
+ var dir = FileList.getCurrentDirectory();
+ if (FileList.isAllSelected()) {
+ files = OC.basename(dir);
+ dir = OC.dirname(dir) || '/';
+ }
+ else {
+ files = FileList.getSelectedFiles('name');
+ }
+ OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.'));
+ OC.redirect(Files.getDownloadUrl(files, dir));
+ return false;
+ },
+
+ /**
+ * Event handler for when clicking on "Delete" for the selected files
+ */
+ _onClickDeleteSelected: function(event) {
+ var files = null;
+ if (!FileList.isAllSelected()) {
+ files = FileList.getSelectedFiles('name');
+ }
+ FileList.do_delete(files);
+ event.preventDefault();
+ return false;
},
/**
@@ -79,7 +215,7 @@ window.FileList = {
if (this.pageNumber + 1 >= this.totalPages) {
return;
}
- if ($(window).scrollTop() + $(window).height() > $(document).height() - 20) {
+ if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) {
this._nextPage(true);
}
},
@@ -113,7 +249,7 @@ window.FileList = {
if (result) {
if (result.status === 'success') {
FileList.remove(file);
- procesSelection();
+ FileList.updateSelectionSummary();
$('#notification').hide();
} else {
$('#notification').hide();
@@ -153,12 +289,29 @@ window.FileList = {
},
/**
+ * Returns the file data from a given file element.
+ * @param $el file tr element
+ * @return file data
+ */
+ elementToFile: function($el){
+ return {
+ id: parseInt($el.attr('data-id'), 10),
+ name: $el.attr('data-file'),
+ mimetype: $el.attr('data-mime'),
+ type: $el.attr('data-type'),
+ size: parseInt($el.attr('data-size'), 10),
+ etag: $el.attr('data-etag'),
+ };
+ },
+
+ /**
* Appends the next page of files into the table
* @param animate true to animate the new elements
*/
_nextPage: function(animate) {
var tr, index, count = this.pageSize,
- newTrs = [];
+ newTrs = [],
+ selected = this.isAllSelected();
if (this.pageNumber + 1 >= this.totalPages) {
return;
@@ -169,6 +322,10 @@ window.FileList = {
while (count > 0 && index < this.files.length) {
tr = this.add(this.files[index], {updateSummary: false});
+ if (selected) {
+ tr.addClass('selected');
+ tr.find('input:checkbox').prop('checked', true);
+ }
if (animate) {
tr.addClass('appear transparent'); // TODO
newTrs.push(tr);
@@ -201,6 +358,9 @@ window.FileList = {
this.$fileList.detach();
this.$fileList.empty();
+ // clear "Select all" checkbox
+ $('#select_all').prop('checked', false);
+
this.isEmpty = this.files.length === 0;
this._nextPage();
@@ -215,7 +375,7 @@ window.FileList = {
this.fileSummary.calculate(filesArray);
- procesSelection();
+ FileList.updateSelectionSummary();
$(window).scrollTop(0);
this.$fileList.trigger(jQuery.Event("updated"));
@@ -580,10 +740,14 @@ window.FileList = {
* @param name name of the file to remove
* @param options optional options as map:
* "updateSummary": true to update the summary (default), false otherwise
+ * @return deleted element
*/
remove:function(name, options){
options = options || {};
var fileEl = FileList.findFileEl(name);
+ if (!fileEl.length) {
+ return null;
+ }
if (fileEl.data('permissions') & OC.PERMISSION_DELETE) {
// file is only draggable when delete permissions are set
fileEl.find('td.filename').draggable('destroy');
@@ -824,21 +988,22 @@ window.FileList = {
function(result) {
if (result.status === 'success') {
if (params.allfiles) {
- // clear whole list
- $('#fileList tr').remove();
+ FileList.setFiles([]);
}
else {
$.each(files,function(index,file) {
var fileEl = FileList.remove(file, {updateSummary: false});
+ // FIXME: not sure why we need this after the
+ // element isn't even in the DOM any more
fileEl.find('input[type="checkbox"]').prop('checked', false);
fileEl.removeClass('selected');
FileList.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')});
});
}
- procesSelection();
checkTrashStatus();
FileList.updateEmptyContent();
FileList.fileSummary.update();
+ FileList.updateSelectionSummary();
Files.updateStorageStatistics();
} else {
if (result.status === 'error' && result.data.message) {
@@ -942,13 +1107,94 @@ window.FileList = {
});
},
/**
+ * Update UI based on the current selection
+ */
+ updateSelectionSummary: function() {
+ var allSelected = this.isAllSelected();
+ var selected;
+ var summary = {
+ totalFiles: 0,
+ totalDirs: 0,
+ totalSize: 0
+ };
+
+ if (allSelected) {
+ summary = this.fileSummary.summary;
+ }
+ else {
+ selected = this.getSelectedFiles();
+ for (var i = 0; i < selected.length; i++ ){
+ if (selected[i].type === 'dir') {
+ summary.totalDirs++;
+ }
+ else {
+ summary.totalFiles++;
+ }
+ summary.totalSize += parseInt(selected[i].size, 10) || 0;
+ }
+ }
+ if (summary.totalFiles === 0 && summary.totalDirs === 0) {
+ $('#headerName span.name').text(t('files','Name'));
+ $('#headerSize').text(t('files','Size'));
+ $('#modified').text(t('files','Modified'));
+ $('table').removeClass('multiselect');
+ $('.selectedActions').addClass('hidden');
+ $('#select_all').removeAttr('checked');
+ }
+ else {
+ $('.selectedActions').removeClass('hidden');
+ $('#headerSize').text(humanFileSize(summary.totalSize));
+ var selection = '';
+ if (summary.totalDirs > 0) {
+ selection += n('files', '%n folder', '%n folders', summary.totalDirs);
+ if (summary.totalFiles > 0) {
+ selection += ' & ';
+ }
+ }
+ if (summary.totalFiles > 0) {
+ selection += n('files', '%n file', '%n files', summary.totalFiles);
+ }
+ $('#headerName span.name').text(selection);
+ $('#modified').text('');
+ $('table').addClass('multiselect');
+ }
+ },
+
+ /**
* Returns whether all files are selected
* @return true if all files are selected, false otherwise
*/
isAllSelected: function() {
- return $('#select_all').prop('checked');
+ return this.$el.find('#select_all').prop('checked');
+ },
+
+ /**
+ * @brief get a list of selected files
+ * @param {string} property (option) the property of the file requested
+ * @return {array}
+ *
+ * possible values for property: name, mime, size and type
+ * if property is set, an array with that property for each file is returnd
+ * if it's ommited an array of objects with all properties is returned
+ */
+ getSelectedFiles: function(property) {
+ var elements=$('td.filename input:checkbox:checked').parent().parent();
+ var files=[];
+ elements.each(function(i,element) {
+ // TODO: make the json format the same as in FileList.add()
+ var file = FileList.elementToFile($(element));
+ // FIXME: legacy attributes
+ file.origin = file.id;
+ file.mime = file.mimetype;
+ if (property) {
+ files.push(file[property]);
+ } else {
+ files.push(file);
+ }
+ });
+ return files;
}
-};
+}
$(document).ready(function() {
FileList.initialize();
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 5e669a796a9..6cb0d41a611 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -209,7 +209,7 @@ $(document).ready(function() {
// Trigger cancelling of file upload
$('#uploadprogresswrapper .stop').on('click', function() {
OC.Upload.cancelUploads();
- procesSelection();
+ FileList.updateSelectionSummary();
});
// Show trash bin
@@ -217,130 +217,6 @@ $(document).ready(function() {
window.location=OC.filePath('files_trashbin', '', 'index.php');
});
- var lastChecked;
-
- // Sets the file link behaviour :
- $('#fileList').on('click','td.filename a',function(event) {
- if (event.ctrlKey || event.shiftKey) {
- event.preventDefault();
- if (event.shiftKey) {
- var last = $(lastChecked).parent().parent().prevAll().length;
- var first = $(this).parent().parent().prevAll().length;
- var start = Math.min(first, last);
- var end = Math.max(first, last);
- var rows = $(this).parent().parent().parent().children('tr');
- for (var i = start; i < end; i++) {
- $(rows).each(function(index) {
- if (index === i) {
- var checkbox = $(this).children().children('input:checkbox');
- $(checkbox).attr('checked', 'checked');
- $(checkbox).parent().parent().addClass('selected');
- }
- });
- }
- }
- var checkbox = $(this).parent().children('input:checkbox');
- lastChecked = checkbox;
- if ($(checkbox).attr('checked')) {
- $(checkbox).removeAttr('checked');
- $(checkbox).parent().parent().removeClass('selected');
- $('#select_all').removeAttr('checked');
- } else {
- $(checkbox).attr('checked', 'checked');
- $(checkbox).parent().parent().toggleClass('selected');
- var selectedCount = $('td.filename input:checkbox:checked').length;
- if (selectedCount === $('td.filename input:checkbox').length) {
- $('#select_all').attr('checked', 'checked');
- }
- }
- procesSelection();
- } else {
- var filename=$(this).parent().parent().attr('data-file');
- var tr = FileList.findFileEl(filename);
- var renaming=tr.data('renaming');
- if (!renaming) {
- FileActions.currentFile = $(this).parent();
- var mime=FileActions.getCurrentMimeType();
- var type=FileActions.getCurrentType();
- var permissions = FileActions.getCurrentPermissions();
- var action=FileActions.getDefault(mime,type, permissions);
- if (action) {
- event.preventDefault();
- action(filename);
- }
- }
- }
-
- });
-
- // Sets the select_all checkbox behaviour :
- $('#select_all').click(function() {
- if ($(this).attr('checked')) {
- // Check all
- $('td.filename input:checkbox').attr('checked', true);
- $('td.filename input:checkbox').parent().parent().addClass('selected');
- } else {
- // Uncheck all
- $('td.filename input:checkbox').attr('checked', false);
- $('td.filename input:checkbox').parent().parent().removeClass('selected');
- }
- procesSelection();
- });
-
- $('#fileList').on('change', 'td.filename input:checkbox',function(event) {
- if (event.shiftKey) {
- var last = $(lastChecked).parent().parent().prevAll().length;
- var first = $(this).parent().parent().prevAll().length;
- var start = Math.min(first, last);
- var end = Math.max(first, last);
- var rows = $(this).parent().parent().parent().children('tr');
- for (var i = start; i < end; i++) {
- $(rows).each(function(index) {
- if (index === i) {
- var checkbox = $(this).children().children('input:checkbox');
- $(checkbox).attr('checked', 'checked');
- $(checkbox).parent().parent().addClass('selected');
- }
- });
- }
- }
- var selectedCount=$('td.filename input:checkbox:checked').length;
- $(this).parent().parent().toggleClass('selected');
- if (!$(this).attr('checked')) {
- $('#select_all').attr('checked',false);
- } else {
- if (selectedCount===$('td.filename input:checkbox').length) {
- $('#select_all').attr('checked',true);
- }
- }
- procesSelection();
- });
-
- $('.download').click('click',function(event) {
- var files;
- var dir = FileList.getCurrentDirectory();
- if (FileList.isAllSelected()) {
- files = OC.basename(dir);
- dir = OC.dirname(dir) || '/';
- }
- else {
- files = Files.getSelectedFiles('name');
- }
- OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.'));
- OC.redirect(Files.getDownloadUrl(files, dir));
- return false;
- });
-
- $('.delete-selected').click(function(event) {
- var files = Files.getSelectedFiles('name');
- event.preventDefault();
- if (FileList.isAllSelected()) {
- files = null;
- }
- FileList.do_delete(files);
- return false;
- });
-
// drag&drop support using jquery.fileupload
// TODO use OC.dialogs
$(document).bind('drop dragover', function (e) {
@@ -440,7 +316,7 @@ var createDragShadow = function(event) {
$(event.target).parents('tr').find('td input:first').prop('checked',true);
}
- var selectedFiles = Files.getSelectedFiles();
+ var selectedFiles = FileList.getSelectedFiles();
if (!isDragSelected && selectedFiles.length === 1) {
//revert the selection
@@ -539,7 +415,7 @@ var folderDropOptions={
oldFile.find('td.filesize').text(humanFileSize(newSize));
FileList.remove(file);
- procesSelection();
+ FileList.updateSelectionSummary();
$('#notification').hide();
} else {
$('#notification').hide();
@@ -556,78 +432,6 @@ var folderDropOptions={
tolerance: 'pointer'
};
-function procesSelection() {
- var selected = Files.getSelectedFiles();
- var selectedFiles = selected.filter(function(el) {
- return el.type==='file';
- });
- var selectedFolders = selected.filter(function(el) {
- return el.type==='dir';
- });
- if (selectedFiles.length === 0 && selectedFolders.length === 0) {
- $('#headerName span.name').text(t('files','Name'));
- $('#headerSize').text(t('files','Size'));
- $('#modified').text(t('files','Modified'));
- $('table').removeClass('multiselect');
- $('.selectedActions').hide();
- $('#select_all').removeAttr('checked');
- }
- else {
- $('.selectedActions').show();
- var totalSize = 0;
- for(var i=0; i<selectedFiles.length; i++) {
- totalSize+=selectedFiles[i].size;
- }
- for(var i=0; i<selectedFolders.length; i++) {
- totalSize+=selectedFolders[i].size;
- }
- $('#headerSize').text(humanFileSize(totalSize));
- var selection = '';
- if (selectedFolders.length > 0) {
- selection += n('files', '%n folder', '%n folders', selectedFolders.length);
- if (selectedFiles.length > 0) {
- selection += ' & ';
- }
- }
- if (selectedFiles.length>0) {
- selection += n('files', '%n file', '%n files', selectedFiles.length);
- }
- $('#headerName span.name').text(selection);
- $('#modified').text('');
- $('table').addClass('multiselect');
- }
-}
-
-/**
- * @brief get a list of selected files
- * @param {string} property (option) the property of the file requested
- * @return {array}
- *
- * possible values for property: name, mime, size and type
- * if property is set, an array with that property for each file is returnd
- * if it's ommited an array of objects with all properties is returned
- */
-Files.getSelectedFiles = function(property) {
- var elements=$('td.filename input:checkbox:checked').parent().parent();
- var files=[];
- elements.each(function(i,element) {
- var file={
- name:$(element).attr('data-file'),
- mime:$(element).data('mime'),
- type:$(element).data('type'),
- size:$(element).data('size'),
- etag:$(element).data('etag'),
- origin: $(element).data('id')
- };
- if (property) {
- files.push(file[property]);
- } else {
- files.push(file);
- }
- });
- return files;
-}
-
Files.getMimeIcon = function(mime, ready) {
if (Files.getMimeIcon.cache[mime]) {
ready(Files.getMimeIcon.cache[mime]);
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 6e80d78eee0..93e7c81cb1f 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -48,8 +48,15 @@ describe('FileList tests', function() {
' <div class="notCreatable"></div>' +
'</div>' +
// dummy table
+ // TODO: at some point this will be rendered by the FileList class itself!
'<table id="filestable">' +
- '<thead><tr><th class="hidden">Name</th></tr></thead>' +
+ '<thead><tr><th id="headerName" class="hidden">' +
+ '<input type="checkbox" id="select_all">' +
+ '<span class="name">Name</span>' +
+ '<span class="selectedActions hidden">' +
+ '<a href class="download">Download</a>' +
+ '<a href class="delete-selected">Delete</a></span>' +
+ '</th></tr></thead>' +
'<tbody id="fileList"></tbody>' +
'<tfoot></tfoot>' +
'</table>' +
@@ -61,25 +68,29 @@ describe('FileList tests', function() {
type: 'file',
name: 'One.txt',
mimetype: 'text/plain',
- size: 12
+ size: 12,
+ etag: 'abc'
}, {
id: 2,
type: 'file',
name: 'Two.jpg',
mimetype: 'image/jpeg',
- size: 12049
+ size: 12049,
+ etag: 'def',
}, {
id: 3,
type: 'file',
name: 'Three.pdf',
mimetype: 'application/pdf',
- size: 58009
+ size: 58009,
+ etag: '123',
}, {
id: 4,
type: 'dir',
name: 'somedir',
mimetype: 'httpd/unix-directory',
- size: 250
+ size: 250,
+ etag: '456'
}];
FileList.initialize();
@@ -380,7 +391,7 @@ describe('FileList tests', function() {
$input.val('One_renamed.txt').blur();
expect(fakeServer.requests.length).toEqual(1);
- var request = fakeServer.requests[0];
+ request = fakeServer.requests[0];
expect(request.url.substr(0, request.url.indexOf('?'))).toEqual(OC.webroot + '/index.php/apps/files/ajax/rename.php');
expect(OC.parseQueryString(request.url)).toEqual({'dir': '/subdir', newname: 'One_renamed.txt', file: 'One.txt'});
@@ -519,6 +530,16 @@ describe('FileList tests', function() {
FileList.setFiles(testFiles);
expect(handler.calledOnce).toEqual(true);
});
+ it('does not update summary when removing non-existing files', function() {
+ // single file
+ FileList.setFiles([testFiles[0]]);
+ $summary = $('#filestable .summary');
+ expect($summary.hasClass('hidden')).toEqual(false);
+ expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
+ FileList.remove('unexist.txt');
+ expect($summary.hasClass('hidden')).toEqual(false);
+ expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
+ });
});
describe('file previews', function() {
var previewLoadStub;
@@ -811,4 +832,188 @@ describe('FileList tests', function() {
expect(Files.getAjaxUrl('test', {a:1, b:'x y'})).toEqual(OC.webroot + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
});
});
+ describe('File selection', function() {
+ beforeEach(function() {
+ FileList.setFiles(testFiles);
+ });
+ it('Selects a file when clicking its checkbox', function() {
+ var $tr = FileList.findFileEl('One.txt');
+ expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
+ $tr.find('td.filename input:checkbox').click();
+
+ expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
+ });
+ it('Selecting all files will automatically check "select all" checkbox', function() {
+ expect($('#select_all').prop('checked')).toEqual(false);
+ $('#fileList tr td.filename input:checkbox').click();
+ expect($('#select_all').prop('checked')).toEqual(true);
+ });
+ it('Clicking "select all" will select/deselect all files', function() {
+ $('#select_all').click();
+ expect($('#select_all').prop('checked')).toEqual(true);
+ $('#fileList tr input:checkbox').each(function() {
+ expect($(this).prop('checked')).toEqual(true);
+ });
+
+ $('#select_all').click();
+ expect($('#select_all').prop('checked')).toEqual(false);
+
+ $('#fileList tr input:checkbox').each(function() {
+ expect($(this).prop('checked')).toEqual(false);
+ });
+ });
+ it('Clicking "select all" then deselecting a file will uncheck "select all"', function() {
+ $('#select_all').click();
+ expect($('#select_all').prop('checked')).toEqual(true);
+
+ var $tr = FileList.findFileEl('One.txt');
+ $tr.find('input:checkbox').click();
+
+ expect($('#select_all').prop('checked')).toEqual(false);
+ });
+ it('Selecting files updates selection summary', function() {
+ var $summary = $('#headerName span.name');
+ expect($summary.text()).toEqual('Name');
+ FileList.findFileEl('One.txt').find('input:checkbox').click();
+ FileList.findFileEl('Three.pdf').find('input:checkbox').click();
+ FileList.findFileEl('somedir').find('input:checkbox').click();
+ expect($summary.text()).toEqual('1 folder & 2 files');
+ });
+ it('Unselecting files hides selection summary', function() {
+ var $summary = $('#headerName span.name');
+ FileList.findFileEl('One.txt').find('input:checkbox').click().click();
+ expect($summary.text()).toEqual('Name');
+ });
+ it('Select/deselect files shows/hides file actions', function() {
+ var $actions = $('#headerName .selectedActions');
+ var $checkbox = FileList.findFileEl('One.txt').find('input:checkbox');
+ expect($actions.hasClass('hidden')).toEqual(true);
+ $checkbox.click();
+ expect($actions.hasClass('hidden')).toEqual(false);
+ $checkbox.click();
+ expect($actions.hasClass('hidden')).toEqual(true);
+ });
+ it('Selection is cleared when switching dirs', function() {
+ $('#select_all').click();
+ var data = {
+ status: 'success',
+ data: {
+ files: testFiles,
+ permissions: 31
+ }
+ };
+ fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php/, [
+ 200, {
+ "Content-Type": "application/json"
+ },
+ JSON.stringify(data)
+ ]);
+ FileList.changeDirectory('/');
+ fakeServer.respond();
+ expect($('#select_all').prop('checked')).toEqual(false);
+ });
+ describe('Actions', function() {
+ beforeEach(function() {
+ FileList.findFileEl('One.txt').find('input:checkbox').click();
+ FileList.findFileEl('Three.pdf').find('input:checkbox').click();
+ FileList.findFileEl('somedir').find('input:checkbox').click();
+ });
+ it('getSelectedFiles returns the selected files', function() {
+ var files = FileList.getSelectedFiles();
+ expect(files.length).toEqual(3);
+ expect(files[0]).toEqual({
+ id: 1,
+ name: 'One.txt',
+ mime: 'text/plain',
+ mimetype: 'text/plain',
+ type: 'file',
+ size: 12,
+ etag: 'abc',
+ origin: 1
+ });
+ expect(files[1]).toEqual({
+ id: 3,
+ type: 'file',
+ name: 'Three.pdf',
+ mime: 'application/pdf',
+ mimetype: 'application/pdf',
+ size: 58009,
+ etag: '123',
+ origin: 3
+ });
+ expect(files[2]).toEqual({
+ id: 4,
+ type: 'dir',
+ name: 'somedir',
+ mime: 'httpd/unix-directory',
+ mimetype: 'httpd/unix-directory',
+ size: 250,
+ etag: '456',
+ origin: 4
+ });
+ });
+ describe('Download', function() {
+ it('Opens download URL when clicking "Download"', function() {
+ var redirectStub = sinon.stub(OC, 'redirect');
+ $('.selectedActions .download').click();
+ expect(redirectStub.calledOnce).toEqual(true);
+ expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D');
+ redirectStub.restore();
+ });
+ it('Downloads root folder when all selected in root folder', function() {
+ $('#dir').val('/');
+ $('#select_all').click();
+ var redirectStub = sinon.stub(OC, 'redirect');
+ $('.selectedActions .download').click();
+ expect(redirectStub.calledOnce).toEqual(true);
+ expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=');
+ redirectStub.restore();
+ });
+ it('Downloads parent folder when all selected in subfolder', function() {
+ $('#select_all').click();
+ var redirectStub = sinon.stub(OC, 'redirect');
+ $('.selectedActions .download').click();
+ expect(redirectStub.calledOnce).toEqual(true);
+ expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir');
+ redirectStub.restore();
+ });
+ });
+ describe('Delete', function() {
+ it('Deletes selected files when "Delete" clicked', function() {
+ var request;
+ $('.selectedActions .delete-selected').click();
+ expect(fakeServer.requests.length).toEqual(1);
+ request = fakeServer.requests[0];
+ expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php');
+ expect(OC.parseQueryString(request.requestBody))
+ .toEqual({'dir': '/subdir', files: '["One.txt","Three.pdf","somedir"]'});
+ fakeServer.requests[0].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify({status: 'success'})
+ );
+ expect(FileList.findFileEl('One.txt').length).toEqual(0);
+ expect(FileList.findFileEl('Three.pdf').length).toEqual(0);
+ expect(FileList.findFileEl('somedir').length).toEqual(0);
+ expect(FileList.findFileEl('Two.jpg').length).toEqual(1);
+ });
+ it('Deletes all files when all selected when "Delete" clicked', function() {
+ var request;
+ $('#select_all').click();
+ $('.selectedActions .delete-selected').click();
+ expect(fakeServer.requests.length).toEqual(1);
+ request = fakeServer.requests[0];
+ expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php');
+ expect(OC.parseQueryString(request.requestBody))
+ .toEqual({'dir': '/subdir', allfiles: 'true'});
+ fakeServer.requests[0].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify({status: 'success'})
+ );
+ expect(FileList.isEmpty).toEqual(true);
+ });
+ });
+ });
+ });
});
diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js
index 018c8ef0f3c..7f8848619f5 100644
--- a/apps/files/tests/js/filesSpec.js
+++ b/apps/files/tests/js/filesSpec.js
@@ -19,7 +19,7 @@
*
*/
-/* global Files */
+/* global OC, Files */
describe('Files tests', function() {
describe('File name validation', function() {
it('Validates correct file names', function() {
@@ -82,4 +82,30 @@ describe('Files tests', function() {
}
});
});
+ describe('getDownloadUrl', function() {
+ var curDirStub;
+ beforeEach(function() {
+ curDirStub = sinon.stub(FileList, 'getCurrentDirectory');
+ });
+ afterEach(function() {
+ curDirStub.restore();
+ });
+ it('returns the ajax download URL when only filename specified', function() {
+ curDirStub.returns('/subdir');
+ var url = Files.getDownloadUrl('test file.txt');
+ expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt');
+ });
+ it('returns the ajax download URL when filename and dir specified', function() {
+ var url = Files.getDownloadUrl('test file.txt', '/subdir');
+ expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt');
+ });
+ it('returns the ajax download URL when filename and root dir specific', function() {
+ var url = Files.getDownloadUrl('test file.txt', '/');
+ expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=test%20file.txt');
+ });
+ it('returns the ajax download URL when multiple files specified', function() {
+ var url = Files.getDownloadUrl(['test file.txt', 'abc.txt'], '/subdir');
+ expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22test%20file.txt%22%2C%22abc.txt%22%5D');
+ });
+ });
});
diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js
index 7795daf2775..6c9b345086a 100644
--- a/apps/files_trashbin/js/filelist.js
+++ b/apps/files_trashbin/js/filelist.js
@@ -75,4 +75,130 @@
$('#emptycontent').toggleClass('hidden', exists);
$('#filestable th').toggleClass('hidden', !exists);
};
+
+ var oldInit = FileList.initialize;
+ FileList.initialize = function() {
+ var result = oldInit.apply(this, arguments);
+ $('.undelete').click('click', FileList._onClickRestoreSelected);
+ return result;
+ };
+
+ FileList._removeCallback = function(result) {
+ if (result.status !== 'success') {
+ OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
+ }
+
+ var files = result.data.success;
+ var $el;
+ for (var i = 0; i < files.length; i++) {
+ $el = FileList.remove(OC.basename(files[i].filename), {updateSummary: false});
+ FileList.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')});
+ }
+ FileList.fileSummary.update();
+ FileList.updateEmptyContent();
+ enableActions();
+ }
+
+ FileList._onClickRestoreSelected = function(event) {
+ event.preventDefault();
+ var allFiles = $('#select_all').is(':checked');
+ var files = [];
+ var params = {};
+ disableActions();
+ if (allFiles) {
+ FileList.showMask();
+ params = {
+ allfiles: true,
+ dir: FileList.getCurrentDirectory()
+ };
+ }
+ else {
+ files = FileList.getSelectedFiles('name');
+ for (var i = 0; i < files.length; i++) {
+ var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
+ deleteAction.removeClass('delete-icon').addClass('progress-icon');
+ }
+ params = {
+ files: JSON.stringify(files),
+ dir: FileList.getCurrentDirectory()
+ };
+ }
+
+ $.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'),
+ params,
+ function(result) {
+ if (allFiles) {
+ if (result.status !== 'success') {
+ OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
+ }
+ FileList.hideMask();
+ // simply remove all files
+ FileList.update('');
+ enableActions();
+ }
+ else {
+ FileList._removeCallback(result);
+ }
+ }
+ );
+ };
+
+ FileList._onClickDeleteSelected = function(event) {
+ event.preventDefault();
+ var allFiles = $('#select_all').is(':checked');
+ var files = [];
+ var params = {};
+ if (allFiles) {
+ params = {
+ allfiles: true,
+ dir: FileList.getCurrentDirectory()
+ };
+ }
+ else {
+ files = FileList.getSelectedFiles('name');
+ params = {
+ files: JSON.stringify(files),
+ dir: FileList.getCurrentDirectory()
+ };
+ }
+
+ disableActions();
+ if (allFiles) {
+ FileList.showMask();
+ }
+ else {
+ for (var i = 0; i < files.length; i++) {
+ var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
+ deleteAction.removeClass('delete-icon').addClass('progress-icon');
+ }
+ }
+
+ $.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'),
+ params,
+ function(result) {
+ if (allFiles) {
+ if (result.status !== 'success') {
+ OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
+ }
+ FileList.hideMask();
+ // simply remove all files
+ FileList.setFiles([]);
+ enableActions();
+ }
+ else {
+ FileList._removeCallback(result);
+ }
+ }
+ );
+ };
+
+ var oldClickFile = FileList._onClickFile;
+ FileList._onClickFile = function(event) {
+ var mime = $(this).parent().parent().data('mime');
+ if (mime !== 'httpd/unix-directory') {
+ event.preventDefault();
+ }
+ return oldClickFile.apply(this, arguments);
+ };
+
})();
diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js
index 4ed5ba1c76e..5f2436de809 100644
--- a/apps/files_trashbin/js/trash.js
+++ b/apps/files_trashbin/js/trash.js
@@ -28,22 +28,6 @@ $(document).ready(function() {
return name;
}
- function removeCallback(result) {
- if (result.status !== 'success') {
- OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
- }
-
- var files = result.data.success;
- var $el;
- for (var i = 0; i < files.length; i++) {
- $el = FileList.remove(OC.basename(files[i].filename), {updateSummary: false});
- FileList.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')});
- }
- FileList.fileSummary.update();
- FileList.updateEmptyContent();
- enableActions();
- }
-
Files.updateStorageStatistics = function() {
// no op because the trashbin doesn't have
// storage info like free space / used space
@@ -59,7 +43,7 @@ $(document).ready(function() {
files: JSON.stringify([filename]),
dir: FileList.getCurrentDirectory()
},
- removeCallback
+ FileList._removeCallback
);
}, t('files_trashbin', 'Restore'));
};
@@ -76,153 +60,10 @@ $(document).ready(function() {
files: JSON.stringify([filename]),
dir: FileList.getCurrentDirectory()
},
- removeCallback
+ FileList._removeCallback
);
});
- // Sets the select_all checkbox behaviour :
- $('#select_all').click(function() {
- if ($(this).attr('checked')) {
- // Check all
- $('td.filename input:checkbox').attr('checked', true);
- $('td.filename input:checkbox').parent().parent().addClass('selected');
- } else {
- // Uncheck all
- $('td.filename input:checkbox').attr('checked', false);
- $('td.filename input:checkbox').parent().parent().removeClass('selected');
- }
- procesSelection();
- });
- $('.undelete').click('click', function(event) {
- event.preventDefault();
- var allFiles = $('#select_all').is(':checked');
- var files = [];
- var params = {};
- disableActions();
- if (allFiles) {
- FileList.showMask();
- params = {
- allfiles: true,
- dir: FileList.getCurrentDirectory()
- };
- }
- else {
- files = Files.getSelectedFiles('name');
- for (var i = 0; i < files.length; i++) {
- var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
- deleteAction.removeClass('delete-icon').addClass('progress-icon');
- }
- params = {
- files: JSON.stringify(files),
- dir: FileList.getCurrentDirectory()
- };
- }
-
- $.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'),
- params,
- function(result) {
- if (allFiles) {
- if (result.status !== 'success') {
- OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
- }
- FileList.hideMask();
- // simply remove all files
- FileList.update('');
- enableActions();
- }
- else {
- removeCallback(result);
- }
- }
- );
- });
-
- $('.delete').click('click', function(event) {
- event.preventDefault();
- var allFiles = $('#select_all').is(':checked');
- var files = [];
- var params = {};
- if (allFiles) {
- params = {
- allfiles: true,
- dir: FileList.getCurrentDirectory()
- };
- }
- else {
- files = Files.getSelectedFiles('name');
- params = {
- files: JSON.stringify(files),
- dir: FileList.getCurrentDirectory()
- };
- }
-
- disableActions();
- if (allFiles) {
- FileList.showMask();
- }
- else {
- for (var i = 0; i < files.length; i++) {
- var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
- deleteAction.removeClass('delete-icon').addClass('progress-icon');
- }
- }
-
- $.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'),
- params,
- function(result) {
- if (allFiles) {
- if (result.status !== 'success') {
- OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
- }
- FileList.hideMask();
- // simply remove all files
- FileList.setFiles([]);
- enableActions();
- }
- else {
- removeCallback(result);
- }
- }
- );
-
- });
-
- $('#fileList').on('click', 'td.filename input', function() {
- var checkbox = $(this).parent().children('input:checkbox');
- $(checkbox).parent().parent().toggleClass('selected');
- if ($(checkbox).is(':checked')) {
- var selectedCount = $('td.filename input:checkbox:checked').length;
- if (selectedCount === $('td.filename input:checkbox').length) {
- $('#select_all').prop('checked', true);
- }
- } else {
- $('#select_all').prop('checked',false);
- }
- procesSelection();
- });
-
- $('#fileList').on('click', 'td.filename a', function(event) {
- var mime = $(this).parent().parent().data('mime');
- if (mime !== 'httpd/unix-directory') {
- event.preventDefault();
- }
- var filename = $(this).parent().parent().attr('data-file');
- var tr = FileList.findFileEl(filename);
- var renaming = tr.data('renaming');
- if(!renaming){
- if(mime.substr(0, 5) === 'text/'){ //no texteditor for now
- return;
- }
- var type = $(this).parent().parent().data('type');
- var permissions = $(this).parent().parent().data('permissions');
- var action = FileActions.getDefault(mime, type, permissions);
- if(action){
- event.preventDefault();
- action(filename);
- }
- }
- });
-
/**
* Override crumb URL maker (hacky!)
*/
diff --git a/core/js/js.js b/core/js/js.js
index 325be6cdc53..27bc3c651e3 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1250,7 +1250,7 @@ function relative_modified_date(timestamp) {
}
/**
- * @todo Write documentation
+ * Utility functions
*/
OC.Util = {
// TODO: remove original functions from global namespace