|
|
|
@ -22,11 +22,12 @@ |
|
|
|
|
* |
|
|
|
|
* @param $el container element with existing markup for the #controls |
|
|
|
|
* and a table |
|
|
|
|
* @param [options] map of options, see other parameters |
|
|
|
|
* @param [options.scrollContainer] scrollable container, defaults to $(window) |
|
|
|
|
* @param [options.dragOptions] drag options, disabled by default |
|
|
|
|
* @param [options.folderDropOptions] folder drop options, disabled by default |
|
|
|
|
* @param [options.detailsViewEnabled=true] whether to enable details view |
|
|
|
|
* @param {Object} [options] map of options, see other parameters |
|
|
|
|
* @param {Object} [options.scrollContainer] scrollable container, defaults to $(window) |
|
|
|
|
* @param {Object} [options.dragOptions] drag options, disabled by default |
|
|
|
|
* @param {Object} [options.folderDropOptions] folder drop options, disabled by default |
|
|
|
|
* @param {boolean} [options.detailsViewEnabled=true] whether to enable details view |
|
|
|
|
* @param {OC.Files.Client} [options.filesClient] files client to use |
|
|
|
|
*/ |
|
|
|
|
var FileList = function($el, options) { |
|
|
|
|
this.initialize($el, options); |
|
|
|
@ -73,6 +74,13 @@ |
|
|
|
|
*/ |
|
|
|
|
_detailsView: null, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Files client instance |
|
|
|
|
* |
|
|
|
|
* @type OC.Files.Client |
|
|
|
|
*/ |
|
|
|
|
filesClient: null, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Whether the file list was initialized already. |
|
|
|
|
* @type boolean |
|
|
|
@ -92,10 +100,17 @@ |
|
|
|
|
* Array of files in the current folder. |
|
|
|
|
* The entries are of file data. |
|
|
|
|
* |
|
|
|
|
* @type Array.<Object> |
|
|
|
|
* @type Array.<OC.Files.FileInfo> |
|
|
|
|
*/ |
|
|
|
|
files: [], |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Current directory entry |
|
|
|
|
* |
|
|
|
|
* @type OC.Files.FileInfo |
|
|
|
|
*/ |
|
|
|
|
dirInfo: null, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* File actions handler, defaults to OCA.Files.FileActions |
|
|
|
|
* @type OCA.Files.FileActions |
|
|
|
@ -149,7 +164,7 @@ |
|
|
|
|
* When false, clicking on a table header will call reload(). |
|
|
|
|
* When true, clicking on a table header will simply resort the list. |
|
|
|
|
*/ |
|
|
|
|
_clientSideSort: false, |
|
|
|
|
_clientSideSort: true, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Current directory |
|
|
|
@ -170,6 +185,7 @@ |
|
|
|
|
* @param options.dragOptions drag options, disabled by default |
|
|
|
|
* @param options.folderDropOptions folder drop options, disabled by default |
|
|
|
|
* @param options.scrollTo name of file to scroll to after the first load |
|
|
|
|
* @param {OC.Files.Client} [options.filesClient] files API client |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
initialize: function($el, options) { |
|
|
|
@ -185,6 +201,12 @@ |
|
|
|
|
if (options.folderDropOptions) { |
|
|
|
|
this._folderDropOptions = options.folderDropOptions; |
|
|
|
|
} |
|
|
|
|
if (options.filesClient) { |
|
|
|
|
this.filesClient = options.filesClient; |
|
|
|
|
} else { |
|
|
|
|
// default client if not specified
|
|
|
|
|
this.filesClient = OC.Files.getClient(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.$el = $el; |
|
|
|
|
if (options.id) { |
|
|
|
@ -209,6 +231,8 @@ |
|
|
|
|
this.files = []; |
|
|
|
|
this._selectedFiles = {}; |
|
|
|
|
this._selectionSummary = new OCA.Files.FileSummary(); |
|
|
|
|
// dummy root dir info
|
|
|
|
|
this.dirInfo = new OC.Files.FileInfo({}); |
|
|
|
|
|
|
|
|
|
this.fileSummary = this._createSummary(); |
|
|
|
|
|
|
|
|
@ -359,7 +383,7 @@ |
|
|
|
|
var highlightState = $tr.hasClass('highlighted'); |
|
|
|
|
$tr = self.updateRow( |
|
|
|
|
$tr, |
|
|
|
|
_.extend({isPreviewAvailable: true}, model.toJSON()), |
|
|
|
|
model.toJSON(), |
|
|
|
|
{updateSummary: true, silent: false, animate: true} |
|
|
|
|
); |
|
|
|
|
$tr.toggleClass('highlighted', highlightState); |
|
|
|
@ -618,7 +642,7 @@ |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, true); |
|
|
|
|
OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir), disableLoadingState); |
|
|
|
|
OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir, true), disableLoadingState); |
|
|
|
|
return false; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
@ -894,16 +918,39 @@ |
|
|
|
|
self.$el.closest('#app-content').trigger(jQuery.Event('apprendered')); |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns the icon URL matching the given file info |
|
|
|
|
* |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo file info |
|
|
|
|
* |
|
|
|
|
* @return {string} icon URL |
|
|
|
|
*/ |
|
|
|
|
_getIconUrl: function(fileInfo) { |
|
|
|
|
var mimeType = fileInfo.mimetype || 'application/octet-stream'; |
|
|
|
|
if (mimeType === 'httpd/unix-directory') { |
|
|
|
|
// use default folder icon
|
|
|
|
|
if (fileInfo.mountType === 'shared' || fileInfo.mountType === 'shared-root') { |
|
|
|
|
return OC.MimeType.getIconUrl('dir-shared'); |
|
|
|
|
} else if (fileInfo.mountType === 'external-root') { |
|
|
|
|
return OC.MimeType.getIconUrl('dir-external'); |
|
|
|
|
} |
|
|
|
|
return OC.MimeType.getIconUrl('dir'); |
|
|
|
|
} |
|
|
|
|
return OC.MimeType.getIconUrl(mimeType); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Creates a new table row element using the given file data. |
|
|
|
|
* @param {OCA.Files.FileInfo} fileData file info attributes |
|
|
|
|
* @param {OC.Files.FileInfo} fileData file info attributes |
|
|
|
|
* @param options map of attributes |
|
|
|
|
* @return new tr element (not appended to the table) |
|
|
|
|
*/ |
|
|
|
|
_createRow: function(fileData, options) { |
|
|
|
|
var td, simpleSize, basename, extension, sizeColor, |
|
|
|
|
icon = OC.MimeType.getIconUrl(fileData.mimetype), |
|
|
|
|
icon = fileData.icon || this._getIconUrl(fileData), |
|
|
|
|
name = fileData.name, |
|
|
|
|
// TODO: get rid of type, only use mime type
|
|
|
|
|
type = fileData.type || 'file', |
|
|
|
|
mtime = parseInt(fileData.mtime, 10), |
|
|
|
|
mime = fileData.mimetype, |
|
|
|
@ -943,6 +990,14 @@ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fileData.mountType) { |
|
|
|
|
// FIXME: HACK: detect shared-root
|
|
|
|
|
if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared') { |
|
|
|
|
// if parent folder isn't share, assume the displayed folder is a share root
|
|
|
|
|
fileData.mountType = 'shared-root'; |
|
|
|
|
} else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external') { |
|
|
|
|
// if parent folder isn't external, assume the displayed folder is the external storage root
|
|
|
|
|
fileData.mountType = 'external-root'; |
|
|
|
|
} |
|
|
|
|
tr.attr('data-mounttype', fileData.mountType); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -953,24 +1008,16 @@ |
|
|
|
|
path = this.getCurrentDirectory(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (type === 'dir') { |
|
|
|
|
// use default folder icon
|
|
|
|
|
icon = icon || OC.imagePath('core', 'filetypes/folder'); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
icon = icon || OC.imagePath('core', 'filetypes/file'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// filename td
|
|
|
|
|
td = $('<td class="filename"></td>'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// linkUrl
|
|
|
|
|
if (type === 'dir') { |
|
|
|
|
if (mime === 'httpd/unix-directory') { |
|
|
|
|
linkUrl = this.linkTo(path + '/' + name); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
linkUrl = this.getDownloadUrl(name, path); |
|
|
|
|
linkUrl = this.getDownloadUrl(name, path, type === 'dir'); |
|
|
|
|
} |
|
|
|
|
if (this._allowSelection) { |
|
|
|
|
td.append( |
|
|
|
@ -996,7 +1043,7 @@ |
|
|
|
|
basename = ''; |
|
|
|
|
extension = name; |
|
|
|
|
// split extension from filename for non dirs
|
|
|
|
|
} else if (type !== 'dir' && name.indexOf('.') !== -1) { |
|
|
|
|
} else if (mime !== 'httpd/unix-directory' && name.indexOf('.') !== -1) { |
|
|
|
|
basename = name.substr(0, name.lastIndexOf('.')); |
|
|
|
|
extension = name.substr(name.lastIndexOf('.')); |
|
|
|
|
} else { |
|
|
|
@ -1018,7 +1065,7 @@ |
|
|
|
|
nameSpan.tooltip({placement: 'right'}); |
|
|
|
|
} |
|
|
|
|
// dirs can show the number of uploaded files
|
|
|
|
|
if (type === 'dir') { |
|
|
|
|
if (mime !== 'httpd/unix-directory') { |
|
|
|
|
linkElem.append($('<span></span>').attr({ |
|
|
|
|
'class': 'uploadtext', |
|
|
|
|
'currentUploads': 0 |
|
|
|
@ -1074,7 +1121,7 @@ |
|
|
|
|
* Adds an entry to the files array and also into the DOM |
|
|
|
|
* in a sorted manner. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileInfo} fileData map of file attributes |
|
|
|
|
* @param {OC.Files.FileInfo} fileData map of file attributes |
|
|
|
|
* @param {Object} [options] map of attributes |
|
|
|
|
* @param {boolean} [options.updateSummary] true to update the summary |
|
|
|
|
* after adding (default), false otherwise. Defaults to true. |
|
|
|
@ -1147,7 +1194,7 @@ |
|
|
|
|
* Creates a new row element based on the given attributes |
|
|
|
|
* and returns it. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileInfo} fileData map of file attributes |
|
|
|
|
* @param {OC.Files.FileInfo} fileData map of file attributes |
|
|
|
|
* @param {Object} [options] map of attributes |
|
|
|
|
* @param {int} [options.index] index at which to insert the element |
|
|
|
|
* @param {boolean} [options.updateSummary] true to update the summary |
|
|
|
@ -1182,7 +1229,7 @@ |
|
|
|
|
filenameTd.draggable(this._dragOptions); |
|
|
|
|
} |
|
|
|
|
// allow dropping on folders
|
|
|
|
|
if (this._folderDropOptions && fileData.type === 'dir') { |
|
|
|
|
if (this._folderDropOptions && mime === 'httpd/unix-directory') { |
|
|
|
|
filenameTd.droppable(this._folderDropOptions); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1193,7 +1240,7 @@ |
|
|
|
|
// display actions
|
|
|
|
|
this.fileActions.display(filenameTd, !options.silent, this); |
|
|
|
|
|
|
|
|
|
if (fileData.isPreviewAvailable && mime !== 'httpd/unix-directory') { |
|
|
|
|
if (mime !== 'httpd/unix-directory') { |
|
|
|
|
var iconDiv = filenameTd.find('.thumbnail'); |
|
|
|
|
// lazy load / newly inserted td ?
|
|
|
|
|
// the typeof check ensures that the default value of animate is true
|
|
|
|
@ -1343,17 +1390,7 @@ |
|
|
|
|
this._currentFileModel = null; |
|
|
|
|
this.$el.find('.select-all').prop('checked', false); |
|
|
|
|
this.showMask(); |
|
|
|
|
if (this._reloadCall) { |
|
|
|
|
this._reloadCall.abort(); |
|
|
|
|
} |
|
|
|
|
this._reloadCall = $.ajax({ |
|
|
|
|
url: this.getAjaxUrl('list'), |
|
|
|
|
data: { |
|
|
|
|
dir : this.getCurrentDirectory(), |
|
|
|
|
sort: this._sort, |
|
|
|
|
sortdirection: this._sortDirection |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
this._reloadCall = this.filesClient.getFolderContents(this.getCurrentDirectory(), {includeParent: true}); |
|
|
|
|
if (this._detailsView) { |
|
|
|
|
// close sidebar
|
|
|
|
|
this._updateDetailsView(null); |
|
|
|
@ -1361,24 +1398,19 @@ |
|
|
|
|
var callBack = this.reloadCallback.bind(this); |
|
|
|
|
return this._reloadCall.then(callBack, callBack); |
|
|
|
|
}, |
|
|
|
|
reloadCallback: function(result) { |
|
|
|
|
reloadCallback: function(status, result) { |
|
|
|
|
delete this._reloadCall; |
|
|
|
|
this.hideMask(); |
|
|
|
|
|
|
|
|
|
if (!result || result.status === 'error') { |
|
|
|
|
// if the error is not related to folder we're trying to load, reload the page to handle logout etc
|
|
|
|
|
if (result.data.error === 'authentication_error' || |
|
|
|
|
result.data.error === 'token_expired' || |
|
|
|
|
result.data.error === 'application_not_enabled' |
|
|
|
|
) { |
|
|
|
|
OC.redirect(OC.generateUrl('apps/files')); |
|
|
|
|
} |
|
|
|
|
OC.Notification.showTemporary(result.data.message); |
|
|
|
|
if (status === 401) { |
|
|
|
|
// TODO: append current URL to be able to get back after logging in again
|
|
|
|
|
OC.redirect(OC.generateUrl('apps/files')); |
|
|
|
|
OC.Notification.show(result); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Firewall Blocked request?
|
|
|
|
|
if (result.status === 403) { |
|
|
|
|
if (status === 403) { |
|
|
|
|
// Go home
|
|
|
|
|
this.changeDirectory('/'); |
|
|
|
|
OC.Notification.showTemporary(t('files', 'This operation is forbidden')); |
|
|
|
@ -1386,32 +1418,49 @@ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Did share service die or something else fail?
|
|
|
|
|
if (result.status === 500) { |
|
|
|
|
if (status === 500) { |
|
|
|
|
// Go home
|
|
|
|
|
this.changeDirectory('/'); |
|
|
|
|
OC.Notification.showTemporary(t('files', 'This directory is unavailable, please check the logs or contact the administrator')); |
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'This directory is unavailable, please check the logs or contact the administrator') |
|
|
|
|
); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (status === 503) { |
|
|
|
|
// Go home
|
|
|
|
|
if (this.getCurrentDirectory() !== '/') { |
|
|
|
|
this.changeDirectory('/'); |
|
|
|
|
// TODO: read error message from exception
|
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Storage not available') |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (result.status === 404) { |
|
|
|
|
if (status === 404) { |
|
|
|
|
// go back home
|
|
|
|
|
this.changeDirectory('/'); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
// aborted ?
|
|
|
|
|
if (result.status === 0){ |
|
|
|
|
if (status === 0){ |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: should rather return upload file size through
|
|
|
|
|
// the files list ajax call
|
|
|
|
|
// TODO: parse remaining quota from PROPFIND response
|
|
|
|
|
this.updateStorageStatistics(true); |
|
|
|
|
|
|
|
|
|
if (result.data.permissions) { |
|
|
|
|
this.setDirectoryPermissions(result.data.permissions); |
|
|
|
|
// first entry is the root
|
|
|
|
|
this.dirInfo = result.shift(); |
|
|
|
|
|
|
|
|
|
if (this.dirInfo.permissions) { |
|
|
|
|
this.setDirectoryPermissions(this.dirInfo.permissions); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.setFiles(result.data.files); |
|
|
|
|
result.sort(this._sortComparator); |
|
|
|
|
this.setFiles(result); |
|
|
|
|
return true; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
@ -1419,12 +1468,15 @@ |
|
|
|
|
OCA.Files.Files.updateStorageStatistics(this.getCurrentDirectory(), force); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @deprecated do not use nor override |
|
|
|
|
*/ |
|
|
|
|
getAjaxUrl: function(action, params) { |
|
|
|
|
return OCA.Files.Files.getAjaxUrl(action, params); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
getDownloadUrl: function(files, dir) { |
|
|
|
|
return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory()); |
|
|
|
|
getDownloadUrl: function(files, dir, isDir) { |
|
|
|
|
return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory(), isDir); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -1489,8 +1541,6 @@ |
|
|
|
|
if (etag){ |
|
|
|
|
// use etag as cache buster
|
|
|
|
|
urlSpec.c = etag; |
|
|
|
|
} else { |
|
|
|
|
console.warn('OCA.Files.FileList.lazyLoadPreview(): missing etag argument'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
previewURL = self.generatePreviewUrl(urlSpec); |
|
|
|
@ -1514,6 +1564,9 @@ |
|
|
|
|
img.src = previewURL; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @deprecated |
|
|
|
|
*/ |
|
|
|
|
setDirectoryPermissions: function(permissions) { |
|
|
|
|
var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0; |
|
|
|
|
this.$el.find('#permissions').val(permissions); |
|
|
|
@ -1610,7 +1663,7 @@ |
|
|
|
|
* fileData should be inserted, considering the current |
|
|
|
|
* sorting |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileInfo} fileData file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileData file info |
|
|
|
|
*/ |
|
|
|
|
_findInsertionIndex: function(fileData) { |
|
|
|
|
var index = 0; |
|
|
|
@ -1628,6 +1681,9 @@ |
|
|
|
|
move: function(fileNames, targetPath) { |
|
|
|
|
var self = this; |
|
|
|
|
var dir = this.getCurrentDirectory(); |
|
|
|
|
if (dir.charAt(dir.length - 1) !== '/') { |
|
|
|
|
dir += '/'; |
|
|
|
|
} |
|
|
|
|
var target = OC.basename(targetPath); |
|
|
|
|
if (!_.isArray(fileNames)) { |
|
|
|
|
fileNames = [fileNames]; |
|
|
|
@ -1635,46 +1691,42 @@ |
|
|
|
|
_.each(fileNames, function(fileName) { |
|
|
|
|
var $tr = self.findFileEl(fileName); |
|
|
|
|
self.showFileBusyState($tr, true); |
|
|
|
|
// TODO: improve performance by sending all file names in a single call
|
|
|
|
|
$.post( |
|
|
|
|
OC.filePath('files', 'ajax', 'move.php'), |
|
|
|
|
{ |
|
|
|
|
dir: dir, |
|
|
|
|
file: fileName, |
|
|
|
|
target: targetPath |
|
|
|
|
}, |
|
|
|
|
function(result) { |
|
|
|
|
if (result) { |
|
|
|
|
if (result.status === 'success') { |
|
|
|
|
// if still viewing the same directory
|
|
|
|
|
if (self.getCurrentDirectory() === dir) { |
|
|
|
|
// recalculate folder size
|
|
|
|
|
var oldFile = self.findFileEl(target); |
|
|
|
|
var newFile = self.findFileEl(fileName); |
|
|
|
|
var oldSize = oldFile.data('size'); |
|
|
|
|
var newSize = oldSize + newFile.data('size'); |
|
|
|
|
oldFile.data('size', newSize); |
|
|
|
|
oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize)); |
|
|
|
|
|
|
|
|
|
// TODO: also update entry in FileList.files
|
|
|
|
|
|
|
|
|
|
self.remove(fileName); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
OC.Notification.hide(); |
|
|
|
|
if (result.status === 'error' && result.data.message) { |
|
|
|
|
OC.Notification.showTemporary(result.data.message); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
OC.Notification.showTemporary(t('files', 'Error moving file.')); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (targetPath.charAt(targetPath.length - 1) !== '/') { |
|
|
|
|
// make sure we move the files into the target dir,
|
|
|
|
|
// not overwrite it
|
|
|
|
|
targetPath = targetPath + '/'; |
|
|
|
|
} |
|
|
|
|
self.filesClient.move(dir + fileName, targetPath + fileName) |
|
|
|
|
.done(function() { |
|
|
|
|
// if still viewing the same directory
|
|
|
|
|
if (OC.joinPaths(self.getCurrentDirectory(), '/') === dir) { |
|
|
|
|
// recalculate folder size
|
|
|
|
|
var oldFile = self.findFileEl(target); |
|
|
|
|
var newFile = self.findFileEl(fileName); |
|
|
|
|
var oldSize = oldFile.data('size'); |
|
|
|
|
var newSize = oldSize + newFile.data('size'); |
|
|
|
|
oldFile.data('size', newSize); |
|
|
|
|
oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize)); |
|
|
|
|
|
|
|
|
|
// TODO: also update entry in FileList.files
|
|
|
|
|
self.remove(fileName); |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
.fail(function(status) { |
|
|
|
|
if (status === 412) { |
|
|
|
|
// TODO: some day here we should invoke the conflict dialog
|
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Could not move "{file}", target exists', {file: fileName}) |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); |
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Could not move "{file}"', {file: fileName}) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
.always(function() { |
|
|
|
|
self.showFileBusyState($tr, false); |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
}, |
|
|
|
@ -1700,16 +1752,16 @@ |
|
|
|
|
* Triggers file rename input field for the given file name. |
|
|
|
|
* If the user enters a new name, the file will be renamed. |
|
|
|
|
* |
|
|
|
|
* @param oldname file name of the file to rename |
|
|
|
|
* @param oldName file name of the file to rename |
|
|
|
|
*/ |
|
|
|
|
rename: function(oldname) { |
|
|
|
|
rename: function(oldName) { |
|
|
|
|
var self = this; |
|
|
|
|
var tr, td, input, form; |
|
|
|
|
tr = this.findFileEl(oldname); |
|
|
|
|
tr = this.findFileEl(oldName); |
|
|
|
|
var oldFileInfo = this.files[tr.index()]; |
|
|
|
|
tr.data('renaming',true); |
|
|
|
|
td = tr.children('td.filename'); |
|
|
|
|
input = $('<input type="text" class="filename"/>').val(oldname); |
|
|
|
|
input = $('<input type="text" class="filename"/>').val(oldName); |
|
|
|
|
form = $('<form></form>'); |
|
|
|
|
form.append(input); |
|
|
|
|
td.children('a.name').hide(); |
|
|
|
@ -1724,11 +1776,11 @@ |
|
|
|
|
input.selectRange(0, len); |
|
|
|
|
var checkInput = function () { |
|
|
|
|
var filename = input.val(); |
|
|
|
|
if (filename !== oldname) { |
|
|
|
|
if (filename !== oldName) { |
|
|
|
|
// Files.isFileNameValid(filename) throws an exception itself
|
|
|
|
|
OCA.Files.Files.isFileNameValid(filename); |
|
|
|
|
if (self.inList(filename)) { |
|
|
|
|
throw t('files', '{new_name} already exists', {new_name: filename}); |
|
|
|
|
throw t('files', '{newName} already exists', {newName: filename}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
@ -1741,6 +1793,14 @@ |
|
|
|
|
td.children('a.name').show(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function updateInList(fileInfo) { |
|
|
|
|
tr.remove(); |
|
|
|
|
tr = self.add(fileInfo, {updateSummary: false, silent: true}); |
|
|
|
|
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)})); |
|
|
|
|
self._updateDetailsView(fileInfo.name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: too many nested blocks, move parts into functions
|
|
|
|
|
form.submit(function(event) { |
|
|
|
|
event.stopPropagation(); |
|
|
|
|
event.preventDefault(); |
|
|
|
@ -1753,7 +1813,7 @@ |
|
|
|
|
input.tooltip('hide'); |
|
|
|
|
form.remove(); |
|
|
|
|
|
|
|
|
|
if (newName !== oldname) { |
|
|
|
|
if (newName !== oldName) { |
|
|
|
|
checkInput(); |
|
|
|
|
// mark as loading (temp element)
|
|
|
|
|
self.showFileBusyState(tr, true); |
|
|
|
@ -1765,34 +1825,46 @@ |
|
|
|
|
td.find('a.name span.nametext').text(basename); |
|
|
|
|
td.children('a.name').show(); |
|
|
|
|
|
|
|
|
|
$.ajax({ |
|
|
|
|
url: OC.filePath('files','ajax','rename.php'), |
|
|
|
|
data: { |
|
|
|
|
dir : tr.attr('data-path') || self.getCurrentDirectory(), |
|
|
|
|
newname: newName, |
|
|
|
|
file: oldname |
|
|
|
|
}, |
|
|
|
|
success: function(result) { |
|
|
|
|
var fileInfo; |
|
|
|
|
if (!result || result.status === 'error') { |
|
|
|
|
OC.dialogs.alert(result.data.message, t('files', 'Could not rename file')); |
|
|
|
|
fileInfo = oldFileInfo; |
|
|
|
|
if (result.data.code === 'sourcenotfound') { |
|
|
|
|
self.remove(result.data.newname, {updateSummary: true}); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
fileInfo = result.data; |
|
|
|
|
var path = tr.attr('data-path') || self.getCurrentDirectory(); |
|
|
|
|
self.filesClient.move(path + '/' + oldName, path + '/' + newName) |
|
|
|
|
.done(function() { |
|
|
|
|
var fileInfo = self.files.splice(tr.index(), 1)[0]; |
|
|
|
|
fileInfo.name = newName; |
|
|
|
|
updateInList(fileInfo); |
|
|
|
|
}) |
|
|
|
|
.fail(function(status) { |
|
|
|
|
// TODO: 409 means current folder does not exist, redirect ?
|
|
|
|
|
if (status === 404) { |
|
|
|
|
// source not found, so remove it from the list
|
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t( |
|
|
|
|
'files', |
|
|
|
|
'Could not rename "{fileName}", it does not exist any more', |
|
|
|
|
{fileName: oldName} |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
self.remove(newName, {updateSummary: true}); |
|
|
|
|
return; |
|
|
|
|
} else if (status === 412) { |
|
|
|
|
// target exists
|
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t( |
|
|
|
|
'files', |
|
|
|
|
'The name "{targetName}" is already used in the folder "{dir}". Please choose a different name.', |
|
|
|
|
{ |
|
|
|
|
targetName: newName, |
|
|
|
|
dir: self.getCurrentDirectory() |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
// restore the item to its previous state
|
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Could not rename "{fileName}"', {fileName: oldName}) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
// reinsert row
|
|
|
|
|
self.files.splice(tr.index(), 1); |
|
|
|
|
tr.remove(); |
|
|
|
|
tr = self.add(fileInfo, {updateSummary: false, silent: true}); |
|
|
|
|
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)})); |
|
|
|
|
self._updateDetailsView(fileInfo.name, false); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
updateInList(oldFileInfo); |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
// add back the old file info when cancelled
|
|
|
|
|
self.files.splice(tr.index(), 1); |
|
|
|
@ -1849,32 +1921,44 @@ |
|
|
|
|
var promise = deferred.promise(); |
|
|
|
|
|
|
|
|
|
OCA.Files.Files.isFileNameValid(name); |
|
|
|
|
name = this.getUniqueName(name); |
|
|
|
|
|
|
|
|
|
if (this.lastAction) { |
|
|
|
|
this.lastAction(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$.post( |
|
|
|
|
OC.generateUrl('/apps/files/ajax/newfile.php'), |
|
|
|
|
{ |
|
|
|
|
dir: this.getCurrentDirectory(), |
|
|
|
|
filename: name |
|
|
|
|
}, |
|
|
|
|
function(result) { |
|
|
|
|
if (result.status === 'success') { |
|
|
|
|
self.add(result.data, {animate: true, scrollTo: true}); |
|
|
|
|
deferred.resolve(result.status, result.data); |
|
|
|
|
name = this.getUniqueName(name); |
|
|
|
|
var targetPath = this.getCurrentDirectory() + '/' + name; |
|
|
|
|
|
|
|
|
|
self.filesClient.putFileContents( |
|
|
|
|
targetPath, |
|
|
|
|
'', |
|
|
|
|
{ |
|
|
|
|
contentType: 'text/plain', |
|
|
|
|
overwrite: true |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
.done(function() { |
|
|
|
|
// TODO: error handling / conflicts
|
|
|
|
|
self.filesClient.getFileInfo(targetPath) |
|
|
|
|
.then(function(status, data) { |
|
|
|
|
self.add(data, {animate: true, scrollTo: true}); |
|
|
|
|
deferred.resolve(status, data); |
|
|
|
|
}) |
|
|
|
|
.fail(function(status) { |
|
|
|
|
OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name})); |
|
|
|
|
deferred.reject(status); |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.fail(function(status) { |
|
|
|
|
if (status === 412) { |
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Could not create file "{file}" because it already exists', {file: name}) |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
if (result.data && result.data.message) { |
|
|
|
|
OC.Notification.showTemporary(result.data.message); |
|
|
|
|
} else { |
|
|
|
|
OC.Notification.showTemporary(t('core', 'Could not create file')); |
|
|
|
|
} |
|
|
|
|
deferred.reject(result.status, result.data); |
|
|
|
|
OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name})); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
deferred.reject(status); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return promise; |
|
|
|
|
}, |
|
|
|
@ -1895,32 +1979,50 @@ |
|
|
|
|
var promise = deferred.promise(); |
|
|
|
|
|
|
|
|
|
OCA.Files.Files.isFileNameValid(name); |
|
|
|
|
name = this.getUniqueName(name); |
|
|
|
|
|
|
|
|
|
if (this.lastAction) { |
|
|
|
|
this.lastAction(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$.post( |
|
|
|
|
OC.generateUrl('/apps/files/ajax/newfolder.php'), |
|
|
|
|
{ |
|
|
|
|
dir: this.getCurrentDirectory(), |
|
|
|
|
foldername: name |
|
|
|
|
}, |
|
|
|
|
function(result) { |
|
|
|
|
if (result.status === 'success') { |
|
|
|
|
self.add(result.data, {animate: true, scrollTo: true}); |
|
|
|
|
deferred.resolve(result.status, result.data); |
|
|
|
|
name = this.getUniqueName(name); |
|
|
|
|
var targetPath = this.getCurrentDirectory() + '/' + name; |
|
|
|
|
|
|
|
|
|
this.filesClient.createDirectory(targetPath) |
|
|
|
|
.done(function(createStatus) { |
|
|
|
|
self.filesClient.getFileInfo(targetPath) |
|
|
|
|
.done(function(status, data) { |
|
|
|
|
self.add(data, {animate: true, scrollTo: true}); |
|
|
|
|
deferred.resolve(status, data); |
|
|
|
|
}) |
|
|
|
|
.fail(function() { |
|
|
|
|
OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name})); |
|
|
|
|
deferred.reject(createStatus); |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.fail(function(createStatus) { |
|
|
|
|
// method not allowed, folder might exist already
|
|
|
|
|
if (createStatus === 405) { |
|
|
|
|
self.filesClient.getFileInfo(targetPath) |
|
|
|
|
.done(function(status, data) { |
|
|
|
|
// add it to the list, for completeness
|
|
|
|
|
self.add(data, {animate: true, scrollTo: true}); |
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Could not create folder "{dir}" because it already exists', {dir: name}) |
|
|
|
|
); |
|
|
|
|
// still consider a failure
|
|
|
|
|
deferred.reject(createStatus, data); |
|
|
|
|
}) |
|
|
|
|
.fail(function() { |
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Could not create folder "{dir}"', {dir: name}) |
|
|
|
|
); |
|
|
|
|
deferred.reject(status); |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
if (result.data && result.data.message) { |
|
|
|
|
OC.Notification.showTemporary(result.data.message); |
|
|
|
|
} else { |
|
|
|
|
OC.Notification.showTemporary(t('core', 'Could not create folder')); |
|
|
|
|
} |
|
|
|
|
deferred.reject(result.status); |
|
|
|
|
OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name})); |
|
|
|
|
deferred.reject(createStatus); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return promise; |
|
|
|
|
}, |
|
|
|
@ -1981,76 +2083,59 @@ |
|
|
|
|
*/ |
|
|
|
|
do_delete:function(files, dir) { |
|
|
|
|
var self = this; |
|
|
|
|
var params; |
|
|
|
|
if (files && files.substr) { |
|
|
|
|
files=[files]; |
|
|
|
|
} |
|
|
|
|
if (!files) { |
|
|
|
|
// delete all files in directory
|
|
|
|
|
files = _.pluck(this.files, 'name'); |
|
|
|
|
} |
|
|
|
|
if (files) { |
|
|
|
|
this.showFileBusyState(files, true); |
|
|
|
|
for (var i=0; i<files.length; i++) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Finish any existing actions
|
|
|
|
|
if (this.lastAction) { |
|
|
|
|
this.lastAction(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
params = { |
|
|
|
|
dir: dir || this.getCurrentDirectory() |
|
|
|
|
}; |
|
|
|
|
if (files) { |
|
|
|
|
params.files = JSON.stringify(files); |
|
|
|
|
dir = dir || this.getCurrentDirectory(); |
|
|
|
|
|
|
|
|
|
function removeFromList(file) { |
|
|
|
|
var fileEl = self.remove(file, {updateSummary: false}); |
|
|
|
|
// FIXME: not sure why we need this after the
|
|
|
|
|
// element isn't even in the DOM any more
|
|
|
|
|
fileEl.find('.selectCheckBox').prop('checked', false); |
|
|
|
|
fileEl.removeClass('selected'); |
|
|
|
|
self.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')}); |
|
|
|
|
// TODO: this info should be returned by the ajax call!
|
|
|
|
|
self.updateEmptyContent(); |
|
|
|
|
self.fileSummary.update(); |
|
|
|
|
self.updateSelectionSummary(); |
|
|
|
|
// FIXME: don't repeat this, do it once all files are done
|
|
|
|
|
self.updateStorageStatistics(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// no files passed, delete all in current dir
|
|
|
|
|
params.allfiles = true; |
|
|
|
|
// show spinner for all files
|
|
|
|
|
this.showFileBusyState(this.$fileList.find('tr'), true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$.post(OC.filePath('files', 'ajax', 'delete.php'), |
|
|
|
|
params, |
|
|
|
|
function(result) { |
|
|
|
|
if (result.status === 'success') { |
|
|
|
|
if (params.allfiles) { |
|
|
|
|
self.setFiles([]); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$.each(files,function(index,file) { |
|
|
|
|
var fileEl = self.remove(file, {updateSummary: false}); |
|
|
|
|
// FIXME: not sure why we need this after the
|
|
|
|
|
// element isn't even in the DOM any more
|
|
|
|
|
fileEl.find('.selectCheckBox').prop('checked', false); |
|
|
|
|
fileEl.removeClass('selected'); |
|
|
|
|
self.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
// TODO: this info should be returned by the ajax call!
|
|
|
|
|
self.updateEmptyContent(); |
|
|
|
|
self.fileSummary.update(); |
|
|
|
|
self.updateSelectionSummary(); |
|
|
|
|
self.updateStorageStatistics(); |
|
|
|
|
// in case there was a "storage full" permanent notification
|
|
|
|
|
OC.Notification.hide(); |
|
|
|
|
|
|
|
|
|
_.each(files, function(file) { |
|
|
|
|
self.filesClient.remove(dir + '/' + file) |
|
|
|
|
.done(function() { |
|
|
|
|
removeFromList(file); |
|
|
|
|
}) |
|
|
|
|
.fail(function(status) { |
|
|
|
|
if (status === 404) { |
|
|
|
|
// the file already did not exist, remove it from the list
|
|
|
|
|
removeFromList(file); |
|
|
|
|
} else { |
|
|
|
|
if (result.status === 'error' && result.data.message) { |
|
|
|
|
OC.Notification.showTemporary(result.data.message); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
OC.Notification.showTemporary(t('files', 'Error deleting file.')); |
|
|
|
|
} |
|
|
|
|
if (params.allfiles) { |
|
|
|
|
// reload the page as we don't know what files were deleted
|
|
|
|
|
// and which ones remain
|
|
|
|
|
self.reload(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$.each(files,function(index,file) { |
|
|
|
|
self.showFileBusyState(file, false); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
// only reset the spinner for that one file
|
|
|
|
|
OC.Notification.showTemporary( |
|
|
|
|
t('files', 'Error deleting file "{fileName}".', {fileName: file}), |
|
|
|
|
{timeout: 10} |
|
|
|
|
); |
|
|
|
|
var deleteAction = self.findFileEl(file).find('.action.delete'); |
|
|
|
|
deleteAction.removeClass('icon-loading-small').addClass('icon-delete'); |
|
|
|
|
self.showFileBusyState(files, false); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Creates the file summary section |
|
|
|
@ -2659,8 +2744,8 @@ |
|
|
|
|
* Compares two file infos by name, making directories appear |
|
|
|
|
* first. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileInfo} fileInfo1 file info |
|
|
|
|
* @param {OCA.Files.FileInfo} fileInfo2 file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo1 file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo2 file info |
|
|
|
|
* @return {int} -1 if the first file must appear before the second one, |
|
|
|
|
* 0 if they are identify, 1 otherwise. |
|
|
|
|
*/ |
|
|
|
@ -2676,8 +2761,8 @@ |
|
|
|
|
/** |
|
|
|
|
* Compares two file infos by size. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileInfo} fileInfo1 file info |
|
|
|
|
* @param {OCA.Files.FileInfo} fileInfo2 file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo1 file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo2 file info |
|
|
|
|
* @return {int} -1 if the first file must appear before the second one, |
|
|
|
|
* 0 if they are identify, 1 otherwise. |
|
|
|
|
*/ |
|
|
|
@ -2687,8 +2772,8 @@ |
|
|
|
|
/** |
|
|
|
|
* Compares two file infos by timestamp. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileInfo} fileInfo1 file info |
|
|
|
|
* @param {OCA.Files.FileInfo} fileInfo2 file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo1 file info |
|
|
|
|
* @param {OC.Files.FileInfo} fileInfo2 file info |
|
|
|
|
* @return {int} -1 if the first file must appear before the second one, |
|
|
|
|
* 0 if they are identify, 1 otherwise. |
|
|
|
|
*/ |
|
|
|
@ -2700,23 +2785,14 @@ |
|
|
|
|
/** |
|
|
|
|
* File info attributes. |
|
|
|
|
* |
|
|
|
|
* @todo make this a real class in the future |
|
|
|
|
* @typedef {Object} OCA.Files.FileInfo |
|
|
|
|
* @typedef {Object} OC.Files.FileInfo |
|
|
|
|
* |
|
|
|
|
* @lends OC.Files.FileInfo |
|
|
|
|
* |
|
|
|
|
* @deprecated use OC.Files.FileInfo instead |
|
|
|
|
* |
|
|
|
|
* @property {int} id file id |
|
|
|
|
* @property {String} name file name |
|
|
|
|
* @property {String} [path] file path, defaults to the list's current path |
|
|
|
|
* @property {String} mimetype mime type |
|
|
|
|
* @property {String} type "file" for files or "dir" for directories |
|
|
|
|
* @property {int} permissions file permissions |
|
|
|
|
* @property {int} mtime modification time in milliseconds |
|
|
|
|
* @property {boolean} [isShareMountPoint] whether the file is a share mount |
|
|
|
|
* point |
|
|
|
|
* @property {boolean} [isPreviewAvailable] whether a preview is available |
|
|
|
|
* for the given file type |
|
|
|
|
* @property {String} [icon] path to the mime type icon |
|
|
|
|
* @property {String} etag etag of the file |
|
|
|
|
*/ |
|
|
|
|
OCA.Files.FileInfo = OC.Files.FileInfo; |
|
|
|
|
|
|
|
|
|
OCA.Files.FileList = FileList; |
|
|
|
|
})(); |
|
|
|
|