Source: core/js/sharedialogshareelistview.js

  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. var PASSWORD_PLACEHOLDER = '**********';
  14. var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the mail share');
  15. if (!OC.Share) {
  16. OC.Share = {};
  17. }
  18. /**
  19. * @class OCA.Share.ShareDialogShareeListView
  20. * @member {OC.Share.ShareItemModel} model
  21. * @member {jQuery} $el
  22. * @memberof OCA.Sharing
  23. * @classdesc
  24. *
  25. * Represents the sharee list part in the GUI of the share dialogue
  26. *
  27. */
  28. var ShareDialogShareeListView = OC.Backbone.View.extend({
  29. /** @type {string} **/
  30. id: 'shareDialogLinkShare',
  31. /** @type {OC.Share.ShareConfigModel} **/
  32. configModel: undefined,
  33. _menuOpen: false,
  34. /** @type {boolean|number} **/
  35. _renderPermissionChange: false,
  36. events: {
  37. 'click .unshare': 'onUnshare',
  38. 'click .share-add': 'showNoteForm',
  39. 'click .share-note-delete': 'deleteNote',
  40. 'click .share-note-submit': 'updateNote',
  41. 'click .share-menu .icon-more': 'onToggleMenu',
  42. 'click .permissions': 'onPermissionChange',
  43. 'click .expireDate' : 'onExpireDateChange',
  44. 'click .password' : 'onMailSharePasswordProtectChange',
  45. 'click .passwordByTalk' : 'onMailSharePasswordProtectByTalkChange',
  46. 'click .secureDrop' : 'onSecureDropChange',
  47. 'keyup input.passwordField': 'onMailSharePasswordKeyUp',
  48. 'focusout input.passwordField': 'onMailSharePasswordEntered',
  49. 'change .datepicker': 'onChangeExpirationDate',
  50. 'click .datepicker' : 'showDatePicker'
  51. },
  52. initialize: function(options) {
  53. if(!_.isUndefined(options.configModel)) {
  54. this.configModel = options.configModel;
  55. } else {
  56. throw 'missing OC.Share.ShareConfigModel';
  57. }
  58. var view = this;
  59. this.model.on('change:shares', function() {
  60. view.render();
  61. });
  62. },
  63. /**
  64. *
  65. * @param {OC.Share.Types.ShareInfo} shareInfo
  66. * @returns {object}
  67. */
  68. getShareeObject: function(shareIndex) {
  69. var shareWith = this.model.getShareWith(shareIndex);
  70. var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex);
  71. var shareWithAvatar = this.model.getShareWithAvatar(shareIndex);
  72. var shareWithTitle = '';
  73. var shareType = this.model.getShareType(shareIndex);
  74. var sharedBy = this.model.getSharedBy(shareIndex);
  75. var sharedByDisplayName = this.model.getSharedByDisplayName(shareIndex);
  76. var hasPermissionOverride = {};
  77. if (shareType === OC.Share.SHARE_TYPE_GROUP) {
  78. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')';
  79. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
  80. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')';
  81. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) {
  82. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote group') + ')';
  83. } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
  84. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'email') + ')';
  85. } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) {
  86. } else if (shareType === OC.Share.SHARE_TYPE_ROOM) {
  87. shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'conversation') + ')';
  88. }
  89. if (shareType === OC.Share.SHARE_TYPE_GROUP) {
  90. shareWithTitle = shareWith + " (" + t('core', 'group') + ')';
  91. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
  92. shareWithTitle = shareWith + " (" + t('core', 'remote') + ')';
  93. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) {
  94. shareWithTitle = shareWith + " (" + t('core', 'remote group') + ')';
  95. }
  96. else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
  97. shareWithTitle = shareWith + " (" + t('core', 'email') + ')';
  98. } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) {
  99. shareWithTitle = shareWith;
  100. // Force "shareWith" in the template to a safe value, as the
  101. // original "shareWith" returned by the model may contain
  102. // problematic characters like "'".
  103. shareWith = 'circle-' + shareIndex;
  104. }
  105. if (sharedBy !== oc_current_user) {
  106. var empty = shareWithTitle === '';
  107. if (!empty) {
  108. shareWithTitle += ' (';
  109. }
  110. shareWithTitle += t('core', 'shared by {sharer}', {sharer: sharedByDisplayName});
  111. if (!empty) {
  112. shareWithTitle += ')';
  113. }
  114. }
  115. var share = this.model.get('shares')[shareIndex];
  116. var password = share.password;
  117. var hasPassword = password !== null && password !== '';
  118. var sendPasswordByTalk = share.send_password_by_talk;
  119. return _.extend(hasPermissionOverride, {
  120. cid: this.cid,
  121. hasSharePermission: this.model.hasSharePermission(shareIndex),
  122. editPermissionState: this.model.editPermissionState(shareIndex),
  123. hasCreatePermission: this.model.hasCreatePermission(shareIndex),
  124. hasUpdatePermission: this.model.hasUpdatePermission(shareIndex),
  125. hasDeletePermission: this.model.hasDeletePermission(shareIndex),
  126. shareWith: shareWith,
  127. shareWithDisplayName: shareWithDisplayName,
  128. shareWithAvatar: shareWithAvatar,
  129. shareWithTitle: shareWithTitle,
  130. shareType: shareType,
  131. shareId: this.model.get('shares')[shareIndex].id,
  132. modSeed: shareWithAvatar || (shareType !== OC.Share.SHARE_TYPE_USER && shareType !== OC.Share.SHARE_TYPE_CIRCLE && shareType !== OC.Share.SHARE_TYPE_ROOM),
  133. isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE,
  134. isRemoteGroupShare: shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP,
  135. isNoteAvailable: shareType !== OC.Share.SHARE_TYPE_REMOTE && shareType !== OC.Share.SHARE_TYPE_REMOTE_GROUP,
  136. isMailShare: shareType === OC.Share.SHARE_TYPE_EMAIL,
  137. isCircleShare: shareType === OC.Share.SHARE_TYPE_CIRCLE,
  138. isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder(),
  139. isPasswordSet: hasPassword && !sendPasswordByTalk,
  140. isPasswordByTalkSet: hasPassword && sendPasswordByTalk,
  141. isTalkEnabled: oc_appswebroots['spreed'] !== undefined,
  142. secureDropMode: !this.model.hasReadPermission(shareIndex),
  143. hasExpireDate: this.model.getExpireDate(shareIndex) !== null,
  144. shareNote: this.model.getNote(shareIndex),
  145. expireDate: moment(this.model.getExpireDate(shareIndex), 'YYYY-MM-DD').format('DD-MM-YYYY'),
  146. // The password placeholder does not take into account if
  147. // sending the password by Talk is enabled or not; when
  148. // switching from sending the password by Talk to sending the
  149. // password by email the password is reused and the share
  150. // updated, so the placeholder already shows the password in the
  151. // brief time between disabling sending the password by email
  152. // and receiving the updated share.
  153. passwordPlaceholder: hasPassword ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
  154. passwordByTalkPlaceholder: (hasPassword && sendPasswordByTalk)? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
  155. });
  156. },
  157. getShareProperties: function() {
  158. return {
  159. unshareLabel: t('core', 'Unshare'),
  160. addNoteLabel: t('core', 'Note to recipient'),
  161. canShareLabel: t('core', 'Can reshare'),
  162. canEditLabel: t('core', 'Can edit'),
  163. createPermissionLabel: t('core', 'Can create'),
  164. updatePermissionLabel: t('core', 'Can change'),
  165. deletePermissionLabel: t('core', 'Can delete'),
  166. secureDropLabel: t('core', 'File drop (upload only)'),
  167. expireDateLabel: t('core', 'Set expiration date'),
  168. passwordLabel: t('core', 'Password protect'),
  169. passwordByTalkLabel: t('core', 'Password protect by Talk'),
  170. crudsLabel: t('core', 'Access control'),
  171. expirationDatePlaceholder: t('core', 'Expiration date'),
  172. defaultExpireDate: moment().add(1, 'day').format('DD-MM-YYYY'), // Can't expire today
  173. triangleSImage: OC.imagePath('core', 'actions/triangle-s'),
  174. isResharingAllowed: this.configModel.get('isResharingAllowed'),
  175. isPasswordForMailSharesRequired: this.configModel.get('isPasswordForMailSharesRequired'),
  176. sharePermissionPossible: this.model.sharePermissionPossible(),
  177. editPermissionPossible: this.model.editPermissionPossible(),
  178. createPermissionPossible: this.model.createPermissionPossible(),
  179. updatePermissionPossible: this.model.updatePermissionPossible(),
  180. deletePermissionPossible: this.model.deletePermissionPossible(),
  181. sharePermission: OC.PERMISSION_SHARE,
  182. createPermission: OC.PERMISSION_CREATE,
  183. updatePermission: OC.PERMISSION_UPDATE,
  184. deletePermission: OC.PERMISSION_DELETE,
  185. readPermission: OC.PERMISSION_READ,
  186. isFolder: this.model.isFolder()
  187. };
  188. },
  189. /**
  190. * get an array of sharees' share properties
  191. *
  192. * @returns {Array}
  193. */
  194. getShareeList: function() {
  195. var universal = this.getShareProperties();
  196. if(!this.model.hasUserShares()) {
  197. return [];
  198. }
  199. var shares = this.model.get('shares');
  200. var list = [];
  201. for(var index = 0; index < shares.length; index++) {
  202. var share = this.getShareeObject(index);
  203. if (share.shareType === OC.Share.SHARE_TYPE_LINK) {
  204. continue;
  205. }
  206. // first empty {} is necessary, otherwise we get in trouble
  207. // with references
  208. list.push(_.extend({}, universal, share));
  209. }
  210. return list;
  211. },
  212. getLinkReshares: function() {
  213. var universal = {
  214. unshareLabel: t('core', 'Unshare'),
  215. };
  216. if(!this.model.hasUserShares()) {
  217. return [];
  218. }
  219. var shares = this.model.get('shares');
  220. var list = [];
  221. for(var index = 0; index < shares.length; index++) {
  222. var share = this.getShareeObject(index);
  223. if (share.shareType !== OC.Share.SHARE_TYPE_LINK) {
  224. continue;
  225. }
  226. // first empty {} is necessary, otherwise we get in trouble
  227. // with references
  228. list.push(_.extend({}, universal, share, {
  229. shareInitiator: shares[index].uid_owner,
  230. shareInitiatorText: t('core', '{shareInitiatorDisplayName} shared via link', {shareInitiatorDisplayName: shares[index].displayname_owner})
  231. }));
  232. }
  233. return list;
  234. },
  235. render: function() {
  236. if(!this._renderPermissionChange) {
  237. this.$el.html(this.template({
  238. cid: this.cid,
  239. sharees: this.getShareeList(),
  240. linkReshares: this.getLinkReshares()
  241. }));
  242. this.$('.avatar').each(function () {
  243. var $this = $(this);
  244. if ($this.hasClass('imageplaceholderseed')) {
  245. $this.css({width: 32, height: 32});
  246. if ($this.data('avatar')) {
  247. $this.css('border-radius', '0%');
  248. $this.css('background', 'url(' + $this.data('avatar') + ') no-repeat');
  249. $this.css('background-size', '31px');
  250. } else {
  251. $this.imageplaceholder($this.data('seed'));
  252. }
  253. } else {
  254. // user, size, ie8fix, hidedefault, callback, displayname
  255. $this.avatar($this.data('username'), 32, undefined, undefined, undefined, $this.data('displayname'));
  256. }
  257. });
  258. this.$('.has-tooltip').tooltip({
  259. placement: 'bottom'
  260. });
  261. this.$('ul.shareWithList > li').each(function() {
  262. var $this = $(this);
  263. var shareWith = $this.data('share-with');
  264. var shareType = $this.data('share-type');
  265. $this.find('div.avatar, span.username').contactsMenu(shareWith, shareType, $this);
  266. });
  267. } else {
  268. var permissionChangeShareId = parseInt(this._renderPermissionChange, 10);
  269. var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId);
  270. var sharee = this.getShareeObject(shareWithIndex);
  271. $.extend(sharee, this.getShareProperties());
  272. var $li = this.$('li[data-share-id=' + permissionChangeShareId + ']');
  273. $li.find('.sharingOptionsGroup .popovermenu').replaceWith(this.popoverMenuTemplate(sharee));
  274. }
  275. var _this = this;
  276. this.getShareeList().forEach(function(sharee) {
  277. var $edit = _this.$('#canEdit-' + _this.cid + '-' + sharee.shareId);
  278. if($edit.length === 1) {
  279. $edit.prop('checked', sharee.editPermissionState === 'checked');
  280. $edit.prop('indeterminate', sharee.editPermissionState === 'indeterminate');
  281. }
  282. });
  283. this.$('.popovermenu').on('afterHide', function() {
  284. _this._menuOpen = false;
  285. });
  286. this.$('.popovermenu').on('beforeHide', function() {
  287. var shareId = parseInt(_this._menuOpen, 10);
  288. if(!_.isNaN(shareId)) {
  289. var datePickerClass = '.expirationDateContainer-' + _this.cid + '-' + shareId;
  290. var datePickerInput = '#expirationDatePicker-' + _this.cid + '-' + shareId;
  291. var expireDateCheckbox = '#expireDate-' + _this.cid + '-' + shareId;
  292. if ($(expireDateCheckbox).prop('checked')) {
  293. $(datePickerInput).removeClass('hidden-visually');
  294. $(datePickerClass).removeClass('hasDatepicker');
  295. $(datePickerClass + ' .ui-datepicker').hide();
  296. }
  297. }
  298. });
  299. if (this._menuOpen !== false) {
  300. // Open menu again if it was opened before
  301. var shareId = parseInt(this._menuOpen, 10);
  302. if(!_.isNaN(shareId)) {
  303. var liSelector = 'li[data-share-id=' + shareId + ']';
  304. OC.showMenu(null, this.$(liSelector + ' .sharingOptionsGroup .popovermenu'));
  305. }
  306. }
  307. this._renderPermissionChange = false;
  308. // new note autosize
  309. autosize(this.$el.find('.share-note-form .share-note'));
  310. this.delegateEvents();
  311. return this;
  312. },
  313. /**
  314. * @returns {Function} from Handlebars
  315. * @private
  316. */
  317. template: function (data) {
  318. var sharees = data.sharees;
  319. if(_.isArray(sharees)) {
  320. for (var i = 0; i < sharees.length; i++) {
  321. data.sharees[i].popoverMenu = this.popoverMenuTemplate(sharees[i]);
  322. }
  323. }
  324. return OC.Share.Templates['sharedialogshareelistview'](data);
  325. },
  326. /**
  327. * renders the popover template and returns the resulting HTML
  328. *
  329. * @param {Object} data
  330. * @returns {string}
  331. */
  332. popoverMenuTemplate: function(data) {
  333. return OC.Share.Templates['sharedialogshareelistview_popover_menu'](data);
  334. },
  335. showNoteForm: function(event) {
  336. event.preventDefault();
  337. event.stopPropagation();
  338. var $element = $(event.target);
  339. var $menu = $element.closest('li');
  340. var $form = $menu.next('li.share-note-form');
  341. // show elements
  342. $menu.find('.share-note-delete').toggle();
  343. $form.toggleClass('hidden');
  344. $form.find('textarea').focus();
  345. },
  346. deleteNote: function(event) {
  347. event.preventDefault();
  348. event.stopPropagation();
  349. var self = this;
  350. var $element = $(event.target);
  351. var $li = $element.closest('li[data-share-id]');
  352. var shareId = $li.data('share-id');
  353. var $menu = $element.closest('li');
  354. var $form = $menu.next('li.share-note-form');
  355. console.log($form.find('.share-note'));
  356. $form.find('.share-note').val('');
  357. $form.addClass('hidden');
  358. $menu.find('.share-note-delete').hide();
  359. self.sendNote('', shareId, $menu);
  360. },
  361. updateNote: function(event) {
  362. event.preventDefault();
  363. event.stopPropagation();
  364. var self = this;
  365. var $element = $(event.target);
  366. var $li = $element.closest('li[data-share-id]');
  367. var shareId = $li.data('share-id');
  368. var $form = $element.closest('li.share-note-form');
  369. var $menu = $form.prev('li');
  370. var message = $form.find('.share-note').val().trim();
  371. if (message.length < 1) {
  372. return;
  373. }
  374. self.sendNote(message, shareId, $menu);
  375. },
  376. sendNote: function(note, shareId, $menu) {
  377. var $form = $menu.next('li.share-note-form');
  378. var $submit = $form.find('input.share-note-submit');
  379. var $error = $form.find('input.share-note-error');
  380. $submit.prop('disabled', true);
  381. $menu.find('.icon-loading-small').removeClass('hidden');
  382. $menu.find('.icon-edit').hide();
  383. var complete = function() {
  384. $submit.prop('disabled', false);
  385. $menu.find('.icon-loading-small').addClass('hidden');
  386. $menu.find('.icon-edit').show();
  387. };
  388. var error = function() {
  389. $error.show();
  390. setTimeout(function() {
  391. $error.hide();
  392. }, 3000);
  393. };
  394. // send data
  395. $.ajax({
  396. method: 'PUT',
  397. url: OC.linkToOCS('apps/files_sharing/api/v1/shares',2) + shareId + '?' + OC.buildQueryString({format: 'json'}),
  398. data: { note: note },
  399. complete : complete,
  400. error: error
  401. });
  402. },
  403. onUnshare: function(event) {
  404. event.preventDefault();
  405. event.stopPropagation();
  406. var self = this;
  407. var $element = $(event.target);
  408. if (!$element.is('a')) {
  409. $element = $element.closest('a');
  410. }
  411. var $loading = $element.find('.icon-loading-small').eq(0);
  412. if(!$loading.hasClass('hidden')) {
  413. // in process
  414. return false;
  415. }
  416. $loading.removeClass('hidden');
  417. var $li = $element.closest('li[data-share-id]');
  418. var shareId = $li.data('share-id');
  419. self.model.removeShare(shareId)
  420. .done(function() {
  421. $li.remove();
  422. })
  423. .fail(function() {
  424. $loading.addClass('hidden');
  425. OC.Notification.showTemporary(t('core', 'Could not unshare'));
  426. });
  427. return false;
  428. },
  429. onToggleMenu: function(event) {
  430. event.preventDefault();
  431. event.stopPropagation();
  432. var $element = $(event.target);
  433. var $li = $element.closest('li[data-share-id]');
  434. var $menu = $li.find('.sharingOptionsGroup .popovermenu');
  435. OC.showMenu(null, $menu);
  436. this._menuOpen = $li.data('share-id');
  437. },
  438. onExpireDateChange: function(event) {
  439. var $element = $(event.target);
  440. var li = $element.closest('li[data-share-id]');
  441. var shareId = li.data('share-id');
  442. var datePickerClass = '.expirationDateContainer-' + this.cid + '-' + shareId;
  443. var datePicker = $(datePickerClass);
  444. var state = $element.prop('checked');
  445. datePicker.toggleClass('hidden', !state);
  446. if (!state) {
  447. // disabled, let's hide the input and
  448. // set the expireDate to nothing
  449. $element.closest('li').next('li').addClass('hidden');
  450. this.setExpirationDate(shareId, '');
  451. } else {
  452. // enabled, show the input and the datepicker
  453. $element.closest('li').next('li').removeClass('hidden');
  454. this.showDatePicker(event);
  455. }
  456. },
  457. showDatePicker: function(event) {
  458. var element = $(event.target);
  459. var li = element.closest('li[data-share-id]');
  460. var shareId = li.data('share-id');
  461. var expirationDatePicker = '#expirationDatePicker-' + this.cid + '-' + shareId;
  462. var view = this;
  463. $(expirationDatePicker).datepicker({
  464. dateFormat : 'dd-mm-yy',
  465. onSelect: function (expireDate) {
  466. view.setExpirationDate(shareId, expireDate);
  467. }
  468. });
  469. $(expirationDatePicker).focus();
  470. },
  471. setExpirationDate: function(shareId, expireDate) {
  472. this.model.updateShare(shareId, {expireDate: expireDate}, {});
  473. },
  474. onMailSharePasswordProtectChange: function(event) {
  475. var element = $(event.target);
  476. var li = element.closest('li[data-share-id]');
  477. var shareId = li.data('share-id');
  478. var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId;
  479. var passwordContainer = $(passwordContainerClass);
  480. var loading = this.$el.find(passwordContainerClass + ' .icon-loading-small');
  481. var inputClass = '#passwordField-' + this.cid + '-' + shareId;
  482. var passwordField = $(inputClass);
  483. var state = element.prop('checked');
  484. var passwordByTalkElement = $('#passwordByTalk-' + this.cid + '-' + shareId);
  485. var passwordByTalkState = passwordByTalkElement.prop('checked');
  486. if (!state && !passwordByTalkState) {
  487. this.model.updateShare(shareId, {password: '', sendPasswordByTalk: false});
  488. passwordField.attr('value', '');
  489. passwordField.removeClass('error');
  490. passwordField.tooltip('hide');
  491. loading.addClass('hidden');
  492. passwordField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE);
  493. // We first need to reset the password field before we hide it
  494. passwordContainer.toggleClass('hidden', !state);
  495. } else if (state) {
  496. if (passwordByTalkState) {
  497. // Switching from sending the password by Talk to sending
  498. // the password by mail can be done keeping the previous
  499. // password sent by Talk.
  500. this.model.updateShare(shareId, {sendPasswordByTalk: false});
  501. var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId;
  502. var passwordByTalkContainer = $(passwordByTalkContainerClass);
  503. passwordByTalkContainer.addClass('hidden');
  504. passwordByTalkElement.prop('checked', false);
  505. }
  506. passwordContainer.toggleClass('hidden', !state);
  507. passwordField = '#passwordField-' + this.cid + '-' + shareId;
  508. this.$(passwordField).focus();
  509. }
  510. },
  511. onMailSharePasswordProtectByTalkChange: function(event) {
  512. var element = $(event.target);
  513. var li = element.closest('li[data-share-id]');
  514. var shareId = li.data('share-id');
  515. var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId;
  516. var passwordByTalkContainer = $(passwordByTalkContainerClass);
  517. var loading = this.$el.find(passwordByTalkContainerClass + ' .icon-loading-small');
  518. var inputClass = '#passwordByTalkField-' + this.cid + '-' + shareId;
  519. var passwordByTalkField = $(inputClass);
  520. var state = element.prop('checked');
  521. var passwordElement = $('#password-' + this.cid + '-' + shareId);
  522. var passwordState = passwordElement.prop('checked');
  523. if (!state) {
  524. this.model.updateShare(shareId, {password: '', sendPasswordByTalk: false});
  525. passwordByTalkField.attr('value', '');
  526. passwordByTalkField.removeClass('error');
  527. passwordByTalkField.tooltip('hide');
  528. loading.addClass('hidden');
  529. passwordByTalkField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE);
  530. // We first need to reset the password field before we hide it
  531. passwordByTalkContainer.toggleClass('hidden', !state);
  532. } else if (state) {
  533. if (passwordState) {
  534. // Enabling sending the password by Talk requires a new
  535. // password to be given (the one sent by mail is not reused,
  536. // as it would defeat the purpose of checking the identity
  537. // of the sharee by Talk if it was already sent by mail), so
  538. // the share is not updated until the user explicitly gives
  539. // the new password.
  540. var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId;
  541. var passwordContainer = $(passwordContainerClass);
  542. passwordContainer.addClass('hidden');
  543. passwordElement.prop('checked', false);
  544. }
  545. passwordByTalkContainer.toggleClass('hidden', !state);
  546. passwordByTalkField = '#passwordByTalkField-' + this.cid + '-' + shareId;
  547. this.$(passwordByTalkField).focus();
  548. }
  549. },
  550. onMailSharePasswordKeyUp: function(event) {
  551. if(event.keyCode === 13) {
  552. this.onMailSharePasswordEntered(event);
  553. }
  554. },
  555. onMailSharePasswordEntered: function(event) {
  556. var passwordField = $(event.target);
  557. var li = passwordField.closest('li[data-share-id]');
  558. var shareId = li.data('share-id');
  559. var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId;
  560. var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId;
  561. var sendPasswordByTalk = passwordField.attr('id').startsWith('passwordByTalk');
  562. var loading;
  563. if (sendPasswordByTalk) {
  564. loading = this.$el.find(passwordByTalkContainerClass + ' .icon-loading-small');
  565. } else {
  566. loading = this.$el.find(passwordContainerClass + ' .icon-loading-small');
  567. }
  568. if (!loading.hasClass('hidden')) {
  569. // still in process
  570. return;
  571. }
  572. passwordField.removeClass('error');
  573. var password = passwordField.val();
  574. // in IE9 the password might be the placeholder due to bugs in the placeholders polyfill
  575. if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) {
  576. return;
  577. }
  578. loading
  579. .removeClass('hidden')
  580. .addClass('inlineblock');
  581. this.model.updateShare(shareId, {
  582. password: password,
  583. sendPasswordByTalk: sendPasswordByTalk
  584. }, {
  585. error: function(model, msg) {
  586. // destroy old tooltips
  587. passwordField.tooltip('destroy');
  588. loading.removeClass('inlineblock').addClass('hidden');
  589. passwordField.addClass('error');
  590. passwordField.attr('title', msg);
  591. passwordField.tooltip({placement: 'bottom', trigger: 'manual'});
  592. passwordField.tooltip('show');
  593. },
  594. success: function(model, msg) {
  595. passwordField.blur();
  596. passwordField.attr('value', '');
  597. passwordField.attr('placeholder', PASSWORD_PLACEHOLDER);
  598. loading.removeClass('inlineblock').addClass('hidden');
  599. }
  600. });
  601. },
  602. onPermissionChange: function(event) {
  603. event.preventDefault();
  604. event.stopPropagation();
  605. var $element = $(event.target);
  606. var $li = $element.closest('li[data-share-id]');
  607. var shareId = $li.data('share-id');
  608. var permissions = OC.PERMISSION_READ;
  609. if (this.model.isFolder()) {
  610. // adjust checkbox states
  611. var $checkboxes = $('.permissions', $li).not('input[name="edit"]').not('input[name="share"]');
  612. var checked;
  613. if ($element.attr('name') === 'edit') {
  614. checked = $element.is(':checked');
  615. // Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck
  616. $($checkboxes).prop('checked', checked);
  617. if (checked) {
  618. permissions |= OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE;
  619. }
  620. } else {
  621. var numberChecked = $checkboxes.filter(':checked').length;
  622. checked = numberChecked === $checkboxes.length;
  623. var $editCb = $('input[name="edit"]', $li);
  624. $editCb.prop('checked', checked);
  625. $editCb.prop('indeterminate', !checked && numberChecked > 0);
  626. }
  627. } else {
  628. if ($element.attr('name') === 'edit' && $element.is(':checked')) {
  629. permissions |= OC.PERMISSION_UPDATE;
  630. }
  631. }
  632. $('.permissions', $li).not('input[name="edit"]').filter(':checked').each(function(index, checkbox) {
  633. permissions |= $(checkbox).data('permissions');
  634. });
  635. /** disable checkboxes during save operation to avoid race conditions **/
  636. $li.find('input[type=checkbox]').prop('disabled', true);
  637. var enableCb = function() {
  638. $li.find('input[type=checkbox]').prop('disabled', false);
  639. };
  640. var errorCb = function(elem, msg) {
  641. OC.dialogs.alert(msg, t('core', 'Error while sharing'));
  642. enableCb();
  643. };
  644. this.model.updateShare(shareId, {permissions: permissions}, {error: errorCb, success: enableCb});
  645. this._renderPermissionChange = shareId;
  646. },
  647. onSecureDropChange: function(event) {
  648. event.preventDefault();
  649. event.stopPropagation();
  650. var $element = $(event.target);
  651. var $li = $element.closest('li[data-share-id]');
  652. var shareId = $li.data('share-id');
  653. var permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE | OC.PERMISSION_READ;
  654. if ($element.is(':checked')) {
  655. permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE;
  656. }
  657. /** disable checkboxes during save operation to avoid race conditions **/
  658. $li.find('input[type=checkbox]').prop('disabled', true);
  659. var enableCb = function() {
  660. $li.find('input[type=checkbox]').prop('disabled', false);
  661. };
  662. var errorCb = function(elem, msg) {
  663. OC.dialogs.alert(msg, t('core', 'Error while sharing'));
  664. enableCb();
  665. };
  666. this.model.updateShare(shareId, {permissions: permissions}, {error: errorCb, success: enableCb});
  667. this._renderPermissionChange = shareId;
  668. }
  669. });
  670. OC.Share.ShareDialogShareeListView = ShareDialogShareeListView;
  671. })();