You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

filelist.js 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /* eslint-disable */
  2. /*
  3. * Copyright (c) 2014
  4. *
  5. * This file is licensed under the Affero General Public License version 3
  6. * or later.
  7. *
  8. * See the COPYING-README file.
  9. *
  10. */
  11. (function() {
  12. var DELETED_REGEXP = new RegExp(/^(.+)\.d[0-9]+$/)
  13. var FILENAME_PROP = '{http://nextcloud.org/ns}trashbin-filename'
  14. var DELETION_TIME_PROP = '{http://nextcloud.org/ns}trashbin-deletion-time'
  15. var TRASHBIN_ORIGINAL_LOCATION = '{http://nextcloud.org/ns}trashbin-original-location'
  16. /**
  17. * Convert a file name in the format filename.d12345 to the real file name.
  18. * This will use basename.
  19. * The name will not be changed if it has no ".d12345" suffix.
  20. * @param {String} name file name
  21. * @returns {String} converted file name
  22. */
  23. function getDeletedFileName(name) {
  24. name = OC.basename(name)
  25. var match = DELETED_REGEXP.exec(name)
  26. if (match && match.length > 1) {
  27. name = match[1]
  28. }
  29. return name
  30. }
  31. /**
  32. * @class OCA.Trashbin.FileList
  33. * @augments OCA.Files.FileList
  34. * @classdesc List of deleted files
  35. *
  36. * @param $el container element with existing markup for the #controls
  37. * and a table
  38. * @param [options] map of options
  39. */
  40. var FileList = function($el, options) {
  41. this.client = options.client
  42. this.initialize($el, options)
  43. }
  44. FileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
  45. /** @lends OCA.Trashbin.FileList.prototype */ {
  46. id: 'trashbin',
  47. appName: t('files_trashbin', 'Deleted files'),
  48. /** @type {OC.Files.Client} */
  49. client: null,
  50. /**
  51. * @private
  52. */
  53. initialize: function() {
  54. this.client.addFileInfoParser(function(response, data) {
  55. var props = response.propStat[0].properties
  56. var path = props[TRASHBIN_ORIGINAL_LOCATION]
  57. return {
  58. displayName: props[FILENAME_PROP],
  59. mtime: parseInt(props[DELETION_TIME_PROP], 10) * 1000,
  60. hasPreview: true,
  61. path: path,
  62. extraData: path
  63. }
  64. })
  65. var result = OCA.Files.FileList.prototype.initialize.apply(this, arguments)
  66. this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this))
  67. this.setSort('mtime', 'desc')
  68. /**
  69. * Override crumb making to add "Deleted Files" entry
  70. * and convert files with ".d" extensions to a more
  71. * user friendly name.
  72. */
  73. this.breadcrumb._makeCrumbs = function() {
  74. var parts = OCA.Files.BreadCrumb.prototype._makeCrumbs.apply(this, arguments)
  75. for (var i = 1; i < parts.length; i++) {
  76. parts[i].name = getDeletedFileName(parts[i].name)
  77. }
  78. return parts
  79. }
  80. OC.Plugins.attach('OCA.Trashbin.FileList', this)
  81. return result
  82. },
  83. /**
  84. * Override to only return read permissions
  85. */
  86. getDirectoryPermissions: function() {
  87. return OC.PERMISSION_READ | OC.PERMISSION_DELETE
  88. },
  89. _setCurrentDir: function(targetDir) {
  90. OCA.Files.FileList.prototype._setCurrentDir.apply(this, arguments)
  91. var baseDir = OC.basename(targetDir)
  92. if (baseDir !== '') {
  93. this.setPageTitle(getDeletedFileName(baseDir))
  94. }
  95. },
  96. _createRow: function() {
  97. // FIXME: MEGAHACK until we find a better solution
  98. var tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments)
  99. tr.find('td.filesize').remove()
  100. return tr
  101. },
  102. getAjaxUrl: function(action, params) {
  103. var q = ''
  104. if (params) {
  105. q = '?' + OC.buildQueryString(params)
  106. }
  107. return OC.filePath('files_trashbin', 'ajax', action + '.php') + q
  108. },
  109. setupUploadEvents: function() {
  110. // override and do nothing
  111. },
  112. linkTo: function(dir) {
  113. return OC.linkTo('files', 'index.php') + '?view=trashbin&dir=' + encodeURIComponent(dir).replace(/%2F/g, '/')
  114. },
  115. elementToFile: function($el) {
  116. var fileInfo = OCA.Files.FileList.prototype.elementToFile($el)
  117. if (this.getCurrentDirectory() === '/') {
  118. fileInfo.displayName = getDeletedFileName(fileInfo.name)
  119. }
  120. // no size available
  121. delete fileInfo.size
  122. return fileInfo
  123. },
  124. updateEmptyContent: function() {
  125. var exists = this.$fileList.find('tr:first').exists()
  126. this.$el.find('#emptycontent').toggleClass('hidden', exists)
  127. this.$el.find('#filestable th').toggleClass('hidden', !exists)
  128. },
  129. _removeCallback: function(files) {
  130. var $el
  131. for (var i = 0; i < files.length; i++) {
  132. $el = this.remove(OC.basename(files[i]), { updateSummary: false })
  133. this.fileSummary.remove({ type: $el.attr('data-type'), size: $el.attr('data-size') })
  134. }
  135. this.fileSummary.update()
  136. this.updateEmptyContent()
  137. },
  138. _onClickRestoreSelected: function(event) {
  139. event.preventDefault()
  140. var self = this
  141. var files = _.pluck(this.getSelectedFiles(), 'name')
  142. for (var i = 0; i < files.length; i++) {
  143. var tr = this.findFileEl(files[i])
  144. this.showFileBusyState(tr, true)
  145. }
  146. this.fileMultiSelectMenu.toggleLoading('restore', true)
  147. var restorePromises = files.map(function(file) {
  148. return self.client.move(OC.joinPaths('trash', self.getCurrentDirectory(), file), OC.joinPaths('restore', file), true)
  149. .then(
  150. function() {
  151. self._removeCallback([file])
  152. }
  153. )
  154. })
  155. return Promise.all(restorePromises).then(
  156. function() {
  157. self.fileMultiSelectMenu.toggleLoading('restore', false)
  158. },
  159. function() {
  160. OC.Notification.show(t('files_trashbin', 'Error while restoring files from trashbin'))
  161. }
  162. )
  163. },
  164. _onClickDeleteSelected: function(event) {
  165. event.preventDefault()
  166. var self = this
  167. var allFiles = this.$el.find('.select-all').is(':checked')
  168. var files = _.pluck(this.getSelectedFiles(), 'name')
  169. for (var i = 0; i < files.length; i++) {
  170. var tr = this.findFileEl(files[i])
  171. this.showFileBusyState(tr, true)
  172. }
  173. if (allFiles) {
  174. return this.client.remove(OC.joinPaths('trash', this.getCurrentDirectory()))
  175. .then(
  176. function() {
  177. self.hideMask()
  178. self.setFiles([])
  179. },
  180. function() {
  181. OC.Notification.show(t('files_trashbin', 'Error while emptying trashbin'))
  182. }
  183. )
  184. } else {
  185. this.fileMultiSelectMenu.toggleLoading('delete', true)
  186. var deletePromises = files.map(function(file) {
  187. return self.client.remove(OC.joinPaths('trash', self.getCurrentDirectory(), file))
  188. .then(
  189. function() {
  190. self._removeCallback([file])
  191. }
  192. )
  193. })
  194. return Promise.all(deletePromises).then(
  195. function() {
  196. self.fileMultiSelectMenu.toggleLoading('delete', false)
  197. },
  198. function() {
  199. OC.Notification.show(t('files_trashbin', 'Error while removing files from trashbin'))
  200. }
  201. )
  202. }
  203. },
  204. _onClickFile: function(event) {
  205. var mime = $(this).parent().parent().data('mime')
  206. if (mime !== 'httpd/unix-directory') {
  207. event.preventDefault()
  208. }
  209. return OCA.Files.FileList.prototype._onClickFile.apply(this, arguments)
  210. },
  211. generatePreviewUrl: function(urlSpec) {
  212. return OC.generateUrl('/apps/files_trashbin/preview?') + $.param(urlSpec)
  213. },
  214. getDownloadUrl: function() {
  215. // no downloads
  216. return '#'
  217. },
  218. updateStorageStatistics: function() {
  219. // no op because the trashbin doesn't have
  220. // storage info like free space / used space
  221. },
  222. isSelectedDeletable: function() {
  223. return true
  224. },
  225. /**
  226. * Returns list of webdav properties to request
  227. */
  228. _getWebdavProperties: function() {
  229. return [FILENAME_PROP, DELETION_TIME_PROP, TRASHBIN_ORIGINAL_LOCATION].concat(this.filesClient.getPropfindProperties())
  230. },
  231. /**
  232. * Reloads the file list using ajax call
  233. *
  234. * @returns ajax call object
  235. */
  236. reload: function() {
  237. this._selectedFiles = {}
  238. this._selectionSummary.clear()
  239. this.$el.find('.select-all').prop('checked', false)
  240. this.showMask()
  241. if (this._reloadCall) {
  242. this._reloadCall.abort()
  243. }
  244. this._reloadCall = this.client.getFolderContents(
  245. 'trash/' + this.getCurrentDirectory(), {
  246. includeParent: false,
  247. properties: this._getWebdavProperties()
  248. }
  249. )
  250. var callBack = this.reloadCallback.bind(this)
  251. return this._reloadCall.then(callBack, callBack)
  252. },
  253. reloadCallback: function(status, result) {
  254. delete this._reloadCall
  255. this.hideMask()
  256. if (status === 401) {
  257. return false
  258. }
  259. // Firewall Blocked request?
  260. if (status === 403) {
  261. // Go home
  262. this.changeDirectory('/')
  263. OC.Notification.show(t('files', 'This operation is forbidden'))
  264. return false
  265. }
  266. // Did share service die or something else fail?
  267. if (status === 500) {
  268. // Go home
  269. this.changeDirectory('/')
  270. OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'))
  271. return false
  272. }
  273. if (status === 404) {
  274. // go back home
  275. this.changeDirectory('/')
  276. return false
  277. }
  278. // aborted ?
  279. if (status === 0) {
  280. return true
  281. }
  282. this.setFiles(result)
  283. return true
  284. }
  285. })
  286. OCA.Trashbin.FileList = FileList
  287. })()