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.9KB

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