sharedialogshareelistview.js 27 KB

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