share.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. _.extend(OC.Files.Client, {
  13. PROPERTY_SHARE_TYPES: '{' + OC.Files.Client.NS_OWNCLOUD + '}share-types',
  14. PROPERTY_OWNER_ID: '{' + OC.Files.Client.NS_OWNCLOUD + '}owner-id',
  15. PROPERTY_OWNER_DISPLAY_NAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}owner-display-name'
  16. })
  17. if (!OCA.Sharing) {
  18. OCA.Sharing = {}
  19. }
  20. /**
  21. * @namespace
  22. */
  23. OCA.Sharing.Util = {
  24. /**
  25. * Initialize the sharing plugin.
  26. *
  27. * Registers the "Share" file action and adds additional
  28. * DOM attributes for the sharing file info.
  29. *
  30. * @param {OCA.Files.FileList} fileList file list to be extended
  31. */
  32. attach: function(fileList) {
  33. // core sharing is disabled/not loaded
  34. if (!OC.Share) {
  35. return
  36. }
  37. if (fileList.id === 'trashbin' || fileList.id === 'files.public') {
  38. return
  39. }
  40. var fileActions = fileList.fileActions
  41. var oldCreateRow = fileList._createRow
  42. fileList._createRow = function(fileData) {
  43. var tr = oldCreateRow.apply(this, arguments)
  44. var sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData)
  45. if (fileData.permissions === 0) {
  46. // no permission, disabling sidebar
  47. delete fileActions.actions.all.Comment
  48. delete fileActions.actions.all.Details
  49. delete fileActions.actions.all.Goto
  50. }
  51. tr.attr('data-share-permissions', sharePermissions)
  52. if (fileData.shareOwner) {
  53. tr.attr('data-share-owner', fileData.shareOwner)
  54. tr.attr('data-share-owner-id', fileData.shareOwnerId)
  55. // user should always be able to rename a mount point
  56. if (fileData.mountType === 'shared-root') {
  57. tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE)
  58. }
  59. }
  60. if (fileData.recipientData && !_.isEmpty(fileData.recipientData)) {
  61. tr.attr('data-share-recipient-data', JSON.stringify(fileData.recipientData))
  62. }
  63. if (fileData.shareTypes) {
  64. tr.attr('data-share-types', fileData.shareTypes.join(','))
  65. }
  66. return tr
  67. }
  68. var oldElementToFile = fileList.elementToFile
  69. fileList.elementToFile = function($el) {
  70. var fileInfo = oldElementToFile.apply(this, arguments)
  71. fileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined
  72. fileInfo.shareOwner = $el.attr('data-share-owner') || undefined
  73. fileInfo.shareOwnerId = $el.attr('data-share-owner-id') || undefined
  74. if ($el.attr('data-share-types')) {
  75. fileInfo.shareTypes = $el.attr('data-share-types').split(',')
  76. }
  77. if ($el.attr('data-expiration')) {
  78. var expirationTimestamp = parseInt($el.attr('data-expiration'))
  79. fileInfo.shares = []
  80. fileInfo.shares.push({ expiration: expirationTimestamp })
  81. }
  82. return fileInfo
  83. }
  84. var oldGetWebdavProperties = fileList._getWebdavProperties
  85. fileList._getWebdavProperties = function() {
  86. var props = oldGetWebdavProperties.apply(this, arguments)
  87. props.push(OC.Files.Client.PROPERTY_OWNER_ID)
  88. props.push(OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME)
  89. props.push(OC.Files.Client.PROPERTY_SHARE_TYPES)
  90. return props
  91. }
  92. fileList.filesClient.addFileInfoParser(function(response) {
  93. var data = {}
  94. var props = response.propStat[0].properties
  95. var permissionsProp = props[OC.Files.Client.PROPERTY_PERMISSIONS]
  96. if (permissionsProp && permissionsProp.indexOf('S') >= 0) {
  97. data.shareOwner = props[OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME]
  98. data.shareOwnerId = props[OC.Files.Client.PROPERTY_OWNER_ID]
  99. }
  100. var shareTypesProp = props[OC.Files.Client.PROPERTY_SHARE_TYPES]
  101. if (shareTypesProp) {
  102. data.shareTypes = _.chain(shareTypesProp).filter(function(xmlvalue) {
  103. return (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'share-type')
  104. }).map(function(xmlvalue) {
  105. return parseInt(xmlvalue.textContent || xmlvalue.text, 10)
  106. }).value()
  107. }
  108. return data
  109. })
  110. // use delegate to catch the case with multiple file lists
  111. fileList.$el.on('fileActionsReady', function(ev) {
  112. var $files = ev.$files
  113. _.each($files, function(file) {
  114. var $tr = $(file)
  115. var shareTypes = $tr.attr('data-share-types') || ''
  116. var shareOwner = $tr.attr('data-share-owner')
  117. if (shareTypes || shareOwner) {
  118. var hasLink = false
  119. var hasShares = false
  120. _.each(shareTypes.split(',') || [], function(shareType) {
  121. shareType = parseInt(shareType, 10)
  122. if (shareType === OC.Share.SHARE_TYPE_LINK) {
  123. hasLink = true
  124. } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
  125. hasLink = true
  126. } else if (shareType === OC.Share.SHARE_TYPE_USER) {
  127. hasShares = true
  128. } else if (shareType === OC.Share.SHARE_TYPE_GROUP) {
  129. hasShares = true
  130. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
  131. hasShares = true
  132. } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) {
  133. hasShares = true
  134. } else if (shareType === OC.Share.SHARE_TYPE_ROOM) {
  135. hasShares = true
  136. }
  137. })
  138. OCA.Sharing.Util._updateFileActionIcon($tr, hasShares, hasLink)
  139. }
  140. })
  141. })
  142. fileList.$el.on('changeDirectory', function() {
  143. OCA.Sharing.sharesLoaded = false
  144. })
  145. fileActions.registerAction({
  146. name: 'Share',
  147. displayName: function(context) {
  148. if (context && context.$file) {
  149. var shareType = parseInt(context.$file.data('share-types'), 10)
  150. var shareOwner = context.$file.data('share-owner-id')
  151. if (shareType >= 0 || shareOwner) {
  152. return t('core', 'Shared')
  153. }
  154. }
  155. return t('core', 'Share')
  156. },
  157. altText: t('core', 'Share'),
  158. mime: 'all',
  159. order: -150,
  160. permissions: OC.PERMISSION_ALL,
  161. iconClass: function(fileName, context) {
  162. var shareType = parseInt(context.$file.data('share-types'), 10)
  163. if (shareType === OC.Share.SHARE_TYPE_EMAIL
  164. || shareType === OC.Share.SHARE_TYPE_LINK) {
  165. return 'icon-public'
  166. }
  167. return 'icon-shared'
  168. },
  169. icon: function(fileName, context) {
  170. var shareOwner = context.$file.data('share-owner-id')
  171. if (shareOwner) {
  172. return OC.generateUrl(`/avatar/${shareOwner}/32`)
  173. }
  174. },
  175. type: OCA.Files.FileActions.TYPE_INLINE,
  176. actionHandler: function(fileName, context) {
  177. // do not open sidebar if permission is set and equal to 0
  178. var permissions = parseInt(context.$file.data('share-permissions'), 10)
  179. if (isNaN(permissions) || permissions > 0) {
  180. fileList.showDetailsView(fileName, 'sharing')
  181. }
  182. },
  183. render: function(actionSpec, isDefault, context) {
  184. var permissions = parseInt(context.$file.data('permissions'), 10)
  185. // if no share permissions but share owner exists, still show the link
  186. if ((permissions & OC.PERMISSION_SHARE) !== 0 || context.$file.attr('data-share-owner')) {
  187. return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context)
  188. }
  189. // don't render anything
  190. return null
  191. }
  192. })
  193. var shareTab = new OCA.Sharing.ShareTabView('sharing', {order: -20})
  194. // // detect changes and change the matching list entry
  195. // shareTab.on('sharesChanged', function(shareModel) {
  196. // var fileInfoModel = shareModel.fileInfoModel
  197. // var $tr = fileList.findFileEl(fileInfoModel.get('name'))
  198. // // We count email shares as link share
  199. // var hasLinkShares = shareModel.hasLinkShares();
  200. // shareModel.get('shares').forEach(function (share) {
  201. // if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) {
  202. // hasLinkShares = true;
  203. // }
  204. // })
  205. // OCA.Sharing.Util._updateFileListDataAttributes(fileList, $tr, shareModel);
  206. // if (!OCA.Sharing.Util._updateFileActionIcon($tr, shareModel.hasUserShares(), hasLinkShares)) {
  207. // // remove icon, if applicable
  208. // OC.Share.markFileAsShared($tr, false, false)
  209. // }
  210. // // FIXME: this is too convoluted. We need to get rid of the above updates
  211. // // and only ever update the model and let the events take care of rerendering
  212. // fileInfoModel.set({
  213. // shareTypes: shareModel.getShareTypes(),
  214. // // in case markFileAsShared decided to change the icon,
  215. // // we need to modify the model
  216. // // (FIXME: yes, this is hacky)
  217. // icon: $tr.attr('data-icon')
  218. // })
  219. // })
  220. // fileList.registerTabView(shareTab)
  221. var breadCrumbSharingDetailView = new OCA.Sharing.ShareBreadCrumbView({ shareTab: shareTab })
  222. fileList.registerBreadCrumbDetailView(breadCrumbSharingDetailView)
  223. },
  224. /**
  225. * Update file list data attributes
  226. */
  227. _updateFileListDataAttributes: function(fileList, $tr, shareModel) {
  228. // files app current cannot show recipients on load, so we don't update the
  229. // icon when changed for consistency
  230. if (fileList.id === 'files') {
  231. return
  232. }
  233. var recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname')
  234. // note: we only update the data attribute because updateIcon()
  235. if (recipients.length) {
  236. var recipientData = _.mapObject(shareModel.get('shares'), function(share) {
  237. return { shareWith: share.share_with, shareWithDisplayName: share.share_with_displayname }
  238. })
  239. $tr.attr('data-share-recipient-data', JSON.stringify(recipientData))
  240. } else {
  241. $tr.removeAttr('data-share-recipient-data')
  242. }
  243. },
  244. /**
  245. * Update the file action share icon for the given file
  246. *
  247. * @param $tr file element of the file to update
  248. * @param {boolean} hasUserShares true if a user share exists
  249. * @param {boolean} hasLinkShares true if a link share exists
  250. *
  251. * @returns {boolean} true if the icon was set, false otherwise
  252. */
  253. _updateFileActionIcon: function($tr, hasUserShares, hasLinkShares) {
  254. // if the statuses are loaded already, use them for the icon
  255. // (needed when scrolling to the next page)
  256. if (hasUserShares || hasLinkShares || $tr.attr('data-share-recipient-data') || $tr.attr('data-share-owner')) {
  257. OC.Share.markFileAsShared($tr, true, hasLinkShares)
  258. return true
  259. }
  260. return false
  261. },
  262. /**
  263. * @param {Array} fileData
  264. * @returns {String}
  265. */
  266. getSharePermissions: function(fileData) {
  267. return fileData.sharePermissions
  268. }
  269. }
  270. })()
  271. OC.Plugins.register('OCA.Files.FileList', OCA.Sharing.Util)