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.

389 lines
13 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /* global OC, Handlebars */
  2. /*
  3. * Copyright (c) 2015
  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. /* globals Handlebars */
  12. (function() {
  13. if (!OC.Share) {
  14. OC.Share = {};
  15. }
  16. var TEMPLATE =
  17. '<ul id="shareWithList" class="shareWithList">' +
  18. '{{#each sharees}}' +
  19. '<li data-share-id="{{shareId}}" data-share-type="{{shareType}}" data-share-with="{{shareWith}}">' +
  20. '{{#if avatarEnabled}}' +
  21. '<div class="avatar {{#if modSeed}}imageplaceholderseed{{/if}}" data-username="{{shareWith}}" {{#if modSeed}}data-seed="{{shareWith}} {{shareType}}"{{/if}}></div>' +
  22. '{{/if}}' +
  23. '<span class="has-tooltip username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' +
  24. '<span class="sharingOptionsGroup">' +
  25. '{{#if editPermissionPossible}}' +
  26. '{{#unless isFileSharedByMail}}' +
  27. '<span class="shareOption">' +
  28. '<input id="canEdit-{{cid}}-{{shareWith}}" type="checkbox" name="edit" class="permissions checkbox" {{#if hasEditPermission}}checked="checked"{{/if}} />' +
  29. '<label for="canEdit-{{cid}}-{{shareWith}}">{{canEditLabel}}</label>' +
  30. '</span>' +
  31. '{{/unless}}' +
  32. '{{/if}}' +
  33. '{{#unless isMailShare}}' +
  34. '<a href="#"><span class="icon icon-more"></span></a>' +
  35. '<div class="popovermenu bubble hidden menu">' +
  36. '<ul>' +
  37. '{{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isMailShare}}' +
  38. '<li>' +
  39. '<span class="shareOption">' +
  40. '<input id="canShare-{{cid}}-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
  41. '<label for="canShare-{{cid}}-{{shareWith}}">{{canShareLabel}}</label>' +
  42. '</span>' +
  43. '</li>' +
  44. '{{/unless}} {{/if}} {{/if}}' +
  45. '{{#if isFolder}}' +
  46. '{{#if createPermissionPossible}}' +
  47. '<li>' +
  48. '<span class="shareOption">' +
  49. '<input id="canCreate-{{cid}}-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
  50. '<label for="canCreate-{{cid}}-{{shareWith}}">{{createPermissionLabel}}</label>' +
  51. '</span>' +
  52. '</li>' +
  53. '{{/if}}' +
  54. '{{#if updatePermissionPossible}}' +
  55. '<li>' +
  56. '<span class="shareOption">' +
  57. '<input id="canUpdate-{{cid}}-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
  58. '<label for="canUpdate-{{cid}}-{{shareWith}}">{{updatePermissionLabel}}</label>' +
  59. '</span>' +
  60. '</li>' +
  61. '{{/if}}' +
  62. '{{#if deletePermissionPossible}}' +
  63. '<li>' +
  64. '<span class="shareOption">' +
  65. '<input id="canDelete-{{cid}}-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
  66. '<label for="canDelete-{{cid}}-{{shareWith}}">{{deletePermissionLabel}}</label>' +
  67. '</span>' +
  68. '</li>' +
  69. '{{/if}}' +
  70. '{{/if}}' +
  71. '<li>' +
  72. '<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span>{{unshareLabel}}</span></a>' +
  73. '</li>' +
  74. '</ul>' +
  75. '</div>' +
  76. '{{/unless}}' +
  77. '<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span class="hidden-visually">{{unshareLabel}}</span></a>' +
  78. '</span>' +
  79. '</li>' +
  80. '{{/each}}' +
  81. '{{#each linkReshares}}' +
  82. '<li data-share-id="{{shareId}}" data-share-type="{{shareType}}">' +
  83. '{{#if avatarEnabled}}' +
  84. '<div class="avatar" data-username="{{shareInitiator}}"></div>' +
  85. '{{/if}}' +
  86. '<span class="has-tooltip username" title="{{shareInitiator}}">' + t('core', '{{shareInitiatorDisplayName}} shared via link') + '</span>' +
  87. '<span class="sharingOptionsGroup">' +
  88. '<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span class="hidden-visually">{{unshareLabel}}</span></a>' +
  89. '</span>' +
  90. '</li>' +
  91. '{{/each}}' +
  92. '</ul>'
  93. ;
  94. /**
  95. * @class OCA.Share.ShareDialogShareeListView
  96. * @member {OC.Share.ShareItemModel} model
  97. * @member {jQuery} $el
  98. * @memberof OCA.Sharing
  99. * @classdesc
  100. *
  101. * Represents the sharee list part in the GUI of the share dialogue
  102. *
  103. */
  104. var ShareDialogShareeListView = OC.Backbone.View.extend({
  105. /** @type {string} **/
  106. id: 'shareDialogLinkShare',
  107. /** @type {OC.Share.ShareConfigModel} **/
  108. configModel: undefined,
  109. /** @type {Function} **/
  110. _template: undefined,
  111. _menuOpen: false,
  112. events: {
  113. 'click .unshare': 'onUnshare',
  114. 'click .icon-more': 'onToggleMenu',
  115. 'click .permissions': 'onPermissionChange',
  116. },
  117. initialize: function(options) {
  118. if(!_.isUndefined(options.configModel)) {
  119. this.configModel = options.configModel;
  120. } else {
  121. throw 'missing OC.Share.ShareConfigModel';
  122. }
  123. var view = this;
  124. this.model.on('change:shares', function() {
  125. view.render();
  126. });
  127. },
  128. /**
  129. *
  130. * @param {OC.Share.Types.ShareInfo} shareInfo
  131. * @returns {object}
  132. */
  133. getShareeObject: function(shareIndex) {
  134. var shareWith = this.model.getShareWith(shareIndex);
  135. var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex);
  136. var shareWithTitle = '';
  137. var shareType = this.model.getShareType(shareIndex);
  138. var hasPermissionOverride = {};
  139. if (shareType === OC.Share.SHARE_TYPE_GROUP) {
  140. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')';
  141. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
  142. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')';
  143. } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
  144. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'email') + ')';
  145. }
  146. if (shareType === OC.Share.SHARE_TYPE_GROUP) {
  147. shareWithTitle = shareWith + " (" + t('core', 'group') + ')';
  148. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
  149. shareWithTitle = shareWith + " (" + t('core', 'remote') + ')';
  150. } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
  151. shareWithTitle = shareWith + " (" + t('core', 'email') + ')';
  152. }
  153. return _.extend(hasPermissionOverride, {
  154. cid: this.cid,
  155. hasSharePermission: this.model.hasSharePermission(shareIndex),
  156. hasEditPermission: this.model.hasEditPermission(shareIndex),
  157. hasCreatePermission: this.model.hasCreatePermission(shareIndex),
  158. hasUpdatePermission: this.model.hasUpdatePermission(shareIndex),
  159. hasDeletePermission: this.model.hasDeletePermission(shareIndex),
  160. shareWith: shareWith,
  161. shareWithDisplayName: shareWithDisplayName,
  162. shareWithTitle: shareWithTitle,
  163. shareType: shareType,
  164. shareId: this.model.get('shares')[shareIndex].id,
  165. modSeed: shareType !== OC.Share.SHARE_TYPE_USER,
  166. isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE,
  167. isMailShare: shareType === OC.Share.SHARE_TYPE_EMAIL,
  168. isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder()
  169. });
  170. },
  171. getShareeList: function() {
  172. var universal = {
  173. avatarEnabled: this.configModel.areAvatarsEnabled(),
  174. unshareLabel: t('core', 'Unshare'),
  175. canShareLabel: t('core', 'can reshare'),
  176. canEditLabel: t('core', 'can edit'),
  177. createPermissionLabel: t('core', 'can create'),
  178. updatePermissionLabel: t('core', 'can change'),
  179. deletePermissionLabel: t('core', 'can delete'),
  180. crudsLabel: t('core', 'access control'),
  181. triangleSImage: OC.imagePath('core', 'actions/triangle-s'),
  182. isResharingAllowed: this.configModel.get('isResharingAllowed'),
  183. sharePermissionPossible: this.model.sharePermissionPossible(),
  184. editPermissionPossible: this.model.editPermissionPossible(),
  185. createPermissionPossible: this.model.createPermissionPossible(),
  186. updatePermissionPossible: this.model.updatePermissionPossible(),
  187. deletePermissionPossible: this.model.deletePermissionPossible(),
  188. sharePermission: OC.PERMISSION_SHARE,
  189. createPermission: OC.PERMISSION_CREATE,
  190. updatePermission: OC.PERMISSION_UPDATE,
  191. deletePermission: OC.PERMISSION_DELETE,
  192. isFolder: this.model.isFolder()
  193. };
  194. if(!this.model.hasUserShares()) {
  195. return [];
  196. }
  197. var shares = this.model.get('shares');
  198. var list = [];
  199. for(var index = 0; index < shares.length; index++) {
  200. var share = this.getShareeObject(index);
  201. if (share.shareType === OC.Share.SHARE_TYPE_LINK) {
  202. continue;
  203. }
  204. // first empty {} is necessary, otherwise we get in trouble
  205. // with references
  206. list.push(_.extend({}, universal, share));
  207. }
  208. return list;
  209. },
  210. getLinkReshares: function() {
  211. var universal = {
  212. unshareLabel: t('core', 'Unshare'),
  213. avatarEnabled: this.configModel.areAvatarsEnabled(),
  214. };
  215. if(!this.model.hasUserShares()) {
  216. return [];
  217. }
  218. var shares = this.model.get('shares');
  219. var list = [];
  220. for(var index = 0; index < shares.length; index++) {
  221. var share = this.getShareeObject(index);
  222. if (share.shareType !== OC.Share.SHARE_TYPE_LINK) {
  223. continue;
  224. }
  225. // first empty {} is necessary, otherwise we get in trouble
  226. // with references
  227. list.push(_.extend({}, universal, share, {
  228. shareInitiator: shares[index].uid_owner,
  229. shareInitiatorDisplayName: shares[index].displayname_owner
  230. }));
  231. }
  232. return list;
  233. },
  234. render: function() {
  235. this.$el.html(this.template({
  236. cid: this.cid,
  237. sharees: this.getShareeList(),
  238. linkReshares: this.getLinkReshares()
  239. }));
  240. if(this.configModel.areAvatarsEnabled()) {
  241. this.$('.avatar').each(function() {
  242. var $this = $(this);
  243. if ($this.hasClass('imageplaceholderseed')) {
  244. $this.css({width: 32, height: 32});
  245. $this.imageplaceholder($this.data('seed'));
  246. } else {
  247. $this.avatar($this.data('username'), 32);
  248. }
  249. });
  250. }
  251. this.$('.has-tooltip').tooltip({
  252. placement: 'bottom'
  253. });
  254. var _this = this;
  255. this.$('.popovermenu').on('afterHide', function() {
  256. _this._menuOpen = false;
  257. });
  258. if (this._menuOpen) {
  259. // Open menu again if it was opened before
  260. OC.showMenu(null, this.$('.popovermenu'));
  261. }
  262. this.delegateEvents();
  263. return this;
  264. },
  265. /**
  266. * @returns {Function} from Handlebars
  267. * @private
  268. */
  269. template: function (data) {
  270. if (!this._template) {
  271. this._template = Handlebars.compile(TEMPLATE);
  272. }
  273. return this._template(data);
  274. },
  275. onUnshare: function(event) {
  276. event.preventDefault();
  277. event.stopPropagation();
  278. var self = this;
  279. var $element = $(event.target);
  280. if (!$element.is('a')) {
  281. $element = $element.closest('a');
  282. }
  283. var $loading = $element.find('.icon-loading-small').eq(0);
  284. if(!$loading.hasClass('hidden')) {
  285. // in process
  286. return false;
  287. }
  288. $loading.removeClass('hidden');
  289. var $li = $element.closest('li[data-share-id]');
  290. var shareId = $li.data('share-id');
  291. self.model.removeShare(shareId)
  292. .done(function() {
  293. $li.remove();
  294. })
  295. .fail(function() {
  296. $loading.addClass('hidden');
  297. OC.Notification.showTemporary(t('core', 'Could not unshare'));
  298. });
  299. return false;
  300. },
  301. onToggleMenu: function(event) {
  302. event.preventDefault();
  303. event.stopPropagation();
  304. var $element = $(event.target);
  305. var $li = $element.closest('li[data-share-id]');
  306. var $menu = $li.find('.popovermenu');
  307. OC.showMenu(null, $menu);
  308. this._menuOpen = true;
  309. },
  310. onPermissionChange: function(event) {
  311. event.preventDefault();
  312. event.stopPropagation();
  313. var $element = $(event.target);
  314. var $li = $element.closest('li[data-share-id]');
  315. var shareId = $li.data('share-id');
  316. var permissions = OC.PERMISSION_READ;
  317. if (this.model.isFolder()) {
  318. // adjust checkbox states
  319. var $checkboxes = $('.permissions', $li).not('input[name="edit"]').not('input[name="share"]');
  320. var checked;
  321. if ($element.attr('name') === 'edit') {
  322. checked = $element.is(':checked');
  323. // Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck
  324. $($checkboxes).prop('checked', checked);
  325. } else {
  326. var numberChecked = $checkboxes.filter(':checked').length;
  327. checked = numberChecked > 0;
  328. $('input[name="edit"]', $li).prop('checked', checked);
  329. }
  330. } else {
  331. if ($element.attr('name') === 'edit' && $element.is(':checked')) {
  332. permissions |= OC.PERMISSION_UPDATE;
  333. }
  334. }
  335. $('.permissions', $li).not('input[name="edit"]').filter(':checked').each(function(index, checkbox) {
  336. permissions |= $(checkbox).data('permissions');
  337. });
  338. this.model.updateShare(shareId, {permissions: permissions});
  339. },
  340. });
  341. OC.Share.ShareDialogShareeListView = ShareDialogShareeListView;
  342. })();