sharedialoglinkshareview.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /*
  2. * Copyright (c) 2015
  3. *
  4. * This file is licensed under the Affero General Public License version 3
  5. * or later.
  6. *
  7. * See the COPYING-README file.
  8. *
  9. */
  10. /* globals Clipboard, Handlebars */
  11. (function() {
  12. if (!OC.Share) {
  13. OC.Share = {};
  14. }
  15. var PASSWORD_PLACEHOLDER = '**********';
  16. var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the public link');
  17. var TEMPLATE =
  18. '{{#if shareAllowed}}' +
  19. '<span class="icon-loading-small hidden"></span>' +
  20. '<input type="checkbox" name="linkCheckbox" id="linkCheckbox-{{cid}}" class="checkbox linkCheckbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} />' +
  21. '<label for="linkCheckbox-{{cid}}">{{linkShareLabel}}</label>' +
  22. '<br />' +
  23. '<div class="oneline">' +
  24. '<label for="linkText-{{cid}}" class="hidden-visually">{{urlLabel}}</label>' +
  25. '<input id="linkText-{{cid}}" class="linkText {{#unless isLinkShare}}hidden{{/unless}}" type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
  26. '<a class="{{#unless isLinkShare}}hidden-visually{{/unless}} clipboardButton icon icon-clippy" data-clipboard-target="#linkText-{{cid}}"></a>' +
  27. '</div>' +
  28. ' {{#if publicUpload}}' +
  29. '<div id="allowPublicUploadWrapper">' +
  30. ' <span class="icon-loading-small hidden"></span>' +
  31. ' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload-{{cid}}" class="checkbox publicUploadCheckbox" {{{publicUploadChecked}}} />' +
  32. '<label for="sharingDialogAllowPublicUpload-{{cid}}">{{publicUploadLabel}}</label>' +
  33. '</div>' +
  34. ' {{#if hideFileList}}' +
  35. '<div id="hideFileListWrapper">' +
  36. ' <span class="icon-loading-small hidden"></span>' +
  37. ' <input type="checkbox" value="1" name="hideFileList" id="sharingDialogHideFileList-{{cid}}" class="checkbox hideFileListCheckbox" {{{hideFileListChecked}}} />' +
  38. '<label for="sharingDialogHideFileList-{{cid}}">{{hideFileListLabel}}</label>' +
  39. '</div>' +
  40. ' {{/if}}' +
  41. ' {{/if}}' +
  42. ' {{#if publicEditing}}' +
  43. '<div id="allowPublicEditingWrapper">' +
  44. ' <span class="icon-loading-small hidden"></span>' +
  45. ' <input type="checkbox" value="1" name="allowPublicEditing" id="sharingDialogAllowPublicEditing-{{cid}}" class="checkbox publicEditingCheckbox" {{{publicEditingChecked}}} />' +
  46. '<label for="sharingDialogAllowPublicEditing-{{cid}}">{{publicEditingLabel}}</label>' +
  47. '</div>' +
  48. ' {{/if}}' +
  49. ' {{#if showPasswordCheckBox}}' +
  50. '<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
  51. '<label for="showPassword-{{cid}}">{{enablePasswordLabel}}</label>' +
  52. ' {{/if}}' +
  53. '<div id="linkPass" class="linkPass {{#unless isPasswordSet}}hidden{{/unless}}">' +
  54. ' <label for="linkPassText-{{cid}}" class="hidden-visually">{{passwordLabel}}</label>' +
  55. ' <input id="linkPassText-{{cid}}" class="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" />' +
  56. ' <span class="icon-loading-small hidden"></span>' +
  57. '</div>' +
  58. '{{else}}' +
  59. // FIXME: this doesn't belong in this view
  60. '{{#if noSharingPlaceholder}}<input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>{{/if}}' +
  61. '{{/if}}'
  62. ;
  63. /**
  64. * @class OCA.Share.ShareDialogLinkShareView
  65. * @member {OC.Share.ShareItemModel} model
  66. * @member {jQuery} $el
  67. * @memberof OCA.Sharing
  68. * @classdesc
  69. *
  70. * Represents the GUI of the share dialogue
  71. *
  72. */
  73. var ShareDialogLinkShareView = OC.Backbone.View.extend({
  74. /** @type {string} **/
  75. id: 'shareDialogLinkShare',
  76. /** @type {OC.Share.ShareConfigModel} **/
  77. configModel: undefined,
  78. /** @type {Function} **/
  79. _template: undefined,
  80. /** @type {boolean} **/
  81. showLink: true,
  82. events: {
  83. 'focusout input.linkPassText': 'onPasswordEntered',
  84. 'keyup input.linkPassText': 'onPasswordKeyUp',
  85. 'click .linkCheckbox': 'onLinkCheckBoxChange',
  86. 'click .linkText': 'onLinkTextClick',
  87. 'change .publicUploadCheckbox': 'onAllowPublicUploadChange',
  88. 'change .publicEditingCheckbox': 'onAllowPublicEditingChange',
  89. 'change .hideFileListCheckbox': 'onHideFileListChange',
  90. 'click .showPasswordCheckbox': 'onShowPasswordClick'
  91. },
  92. initialize: function(options) {
  93. var view = this;
  94. this.model.on('change:permissions', function() {
  95. view.render();
  96. });
  97. this.model.on('change:itemType', function() {
  98. view.render();
  99. });
  100. this.model.on('change:allowPublicUploadStatus', function() {
  101. view.render();
  102. });
  103. this.model.on('change:hideFileListStatus', function() {
  104. view.render();
  105. });
  106. this.model.on('change:linkShare', function() {
  107. view.render();
  108. });
  109. if(!_.isUndefined(options.configModel)) {
  110. this.configModel = options.configModel;
  111. } else {
  112. throw 'missing OC.Share.ShareConfigModel';
  113. }
  114. _.bindAll(
  115. this,
  116. 'onLinkCheckBoxChange',
  117. 'onPasswordEntered',
  118. 'onPasswordKeyUp',
  119. 'onLinkTextClick',
  120. 'onShowPasswordClick',
  121. 'onHideFileListChange',
  122. 'onAllowPublicUploadChange',
  123. 'onAllowPublicEditingChange'
  124. );
  125. var clipboard = new Clipboard('.clipboardButton');
  126. clipboard.on('success', function(e) {
  127. var $input = $(e.trigger);
  128. $input.tooltip('hide')
  129. .attr('data-original-title', t('core', 'Copied!'))
  130. .tooltip('fixTitle')
  131. .tooltip({placement: 'bottom', trigger: 'manual'})
  132. .tooltip('show');
  133. _.delay(function() {
  134. $input.tooltip('hide')
  135. .attr('data-original-title', t('core', 'Copy'))
  136. .tooltip('fixTitle');
  137. }, 3000);
  138. });
  139. clipboard.on('error', function (e) {
  140. var $input = $(e.trigger);
  141. var actionMsg = '';
  142. if (/iPhone|iPad/i.test(navigator.userAgent)) {
  143. actionMsg = t('core', 'Not supported!');
  144. } else if (/Mac/i.test(navigator.userAgent)) {
  145. actionMsg = t('core', 'Press ⌘-C to copy.');
  146. } else {
  147. actionMsg = t('core', 'Press Ctrl-C to copy.');
  148. }
  149. $input.tooltip('hide')
  150. .attr('data-original-title', actionMsg)
  151. .tooltip('fixTitle')
  152. .tooltip({placement: 'bottom', trigger: 'manual'})
  153. .tooltip('show');
  154. _.delay(function () {
  155. $input.tooltip('hide')
  156. .attr('data-original-title', t('core', 'Copy'))
  157. .tooltip('fixTitle');
  158. }, 3000);
  159. });
  160. },
  161. onLinkCheckBoxChange: function() {
  162. var $checkBox = this.$el.find('.linkCheckbox');
  163. var $loading = $checkBox.siblings('.icon-loading-small');
  164. if(!$loading.hasClass('hidden')) {
  165. return false;
  166. }
  167. if($checkBox.is(':checked')) {
  168. if(this.configModel.get('enforcePasswordForPublicLink') === false) {
  169. $loading.removeClass('hidden');
  170. // this will create it
  171. this.model.saveLinkShare();
  172. } else {
  173. this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
  174. this.$el.find('.linkPassText').focus();
  175. }
  176. } else {
  177. if (this.model.get('linkShare').isLinkShare) {
  178. $loading.removeClass('hidden');
  179. this.model.removeLinkShare();
  180. } else {
  181. this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
  182. }
  183. }
  184. },
  185. onLinkTextClick: function() {
  186. var $el = this.$el.find('.linkText');
  187. $el.focus();
  188. $el.select();
  189. },
  190. onShowPasswordClick: function() {
  191. this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
  192. if(!this.$el.find('.showPasswordCheckbox').is(':checked')) {
  193. this.model.saveLinkShare({
  194. password: ''
  195. });
  196. } else {
  197. this.$el.find('.linkPassText').focus();
  198. }
  199. },
  200. onPasswordKeyUp: function(event) {
  201. if(event.keyCode === 13) {
  202. this.onPasswordEntered();
  203. }
  204. },
  205. onPasswordEntered: function() {
  206. var $loading = this.$el.find('.linkPass .icon-loading-small');
  207. if (!$loading.hasClass('hidden')) {
  208. // still in process
  209. return;
  210. }
  211. var $input = this.$el.find('.linkPassText');
  212. $input.removeClass('error');
  213. var password = $input.val();
  214. // in IE9 the password might be the placeholder due to bugs in the placeholders polyfill
  215. if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) {
  216. return;
  217. }
  218. $loading
  219. .removeClass('hidden')
  220. .addClass('inlineblock');
  221. this.model.saveLinkShare({
  222. password: password
  223. }, {
  224. error: function(model, msg) {
  225. // destroy old tooltips
  226. $input.tooltip('destroy');
  227. $loading.removeClass('inlineblock').addClass('hidden');
  228. $input.addClass('error');
  229. $input.attr('title', msg);
  230. $input.tooltip({placement: 'bottom', trigger: 'manual'});
  231. $input.tooltip('show');
  232. }
  233. });
  234. },
  235. onAllowPublicUploadChange: function() {
  236. var $checkbox = this.$('.publicUploadCheckbox');
  237. $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
  238. var permissions = OC.PERMISSION_READ;
  239. if($checkbox.is(':checked')) {
  240. permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ | OC.PERMISSION_DELETE;
  241. }
  242. this.model.saveLinkShare({
  243. permissions: permissions
  244. });
  245. },
  246. onAllowPublicEditingChange: function() {
  247. var $checkbox = this.$('.publicEditingCheckbox');
  248. $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
  249. var permissions = OC.PERMISSION_READ;
  250. if($checkbox.is(':checked')) {
  251. permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_READ;
  252. }
  253. this.model.saveLinkShare({
  254. permissions: permissions
  255. });
  256. },
  257. onHideFileListChange: function () {
  258. var $checkbox = this.$('.hideFileListCheckbox');
  259. $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
  260. var permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ | OC.PERMISSION_DELETE;
  261. if ($checkbox.is(':checked')) {
  262. permissions = OC.PERMISSION_CREATE;
  263. }
  264. this.model.saveLinkShare({
  265. permissions: permissions
  266. });
  267. },
  268. render: function() {
  269. var linkShareTemplate = this.template();
  270. var resharingAllowed = this.model.sharePermissionPossible();
  271. if(!resharingAllowed
  272. || !this.showLink
  273. || !this.configModel.isShareWithLinkAllowed())
  274. {
  275. var templateData = {shareAllowed: false};
  276. if (!resharingAllowed) {
  277. // add message
  278. templateData.noSharingPlaceholder = t('core', 'Resharing is not allowed');
  279. }
  280. this.$el.html(linkShareTemplate(templateData));
  281. return this;
  282. }
  283. var publicUpload =
  284. this.model.isFolder()
  285. && this.model.createPermissionPossible()
  286. && this.configModel.isPublicUploadEnabled();
  287. var publicUploadChecked = '';
  288. if(this.model.isPublicUploadAllowed()) {
  289. publicUploadChecked = 'checked="checked"';
  290. }
  291. var publicEditingChecked = '';
  292. if(this.model.isPublicEditingAllowed()) {
  293. publicEditingChecked = 'checked="checked"';
  294. }
  295. var hideFileList = publicUploadChecked;
  296. var hideFileListChecked = '';
  297. if(this.model.isHideFileListSet()) {
  298. hideFileListChecked = 'checked="checked"';
  299. }
  300. var isLinkShare = this.model.get('linkShare').isLinkShare;
  301. var isPasswordSet = !!this.model.get('linkShare').password;
  302. var showPasswordCheckBox = isLinkShare
  303. && ( !this.configModel.get('enforcePasswordForPublicLink')
  304. || !this.model.get('linkShare').password);
  305. var publicEditable =
  306. !this.model.isFolder()
  307. && isLinkShare
  308. && this.model.updatePermissionPossible();
  309. this.$el.html(linkShareTemplate({
  310. cid: this.cid,
  311. shareAllowed: true,
  312. hideFileList: hideFileList,
  313. isLinkShare: isLinkShare,
  314. shareLinkURL: this.model.get('linkShare').link,
  315. linkShareLabel: t('core', 'Share link'),
  316. urlLabel: t('core', 'Link'),
  317. enablePasswordLabel: t('core', 'Password protect'),
  318. passwordLabel: t('core', 'Password'),
  319. passwordPlaceholder: isPasswordSet ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
  320. isPasswordSet: isPasswordSet,
  321. showPasswordCheckBox: showPasswordCheckBox,
  322. publicUpload: publicUpload && isLinkShare,
  323. publicUploadChecked: publicUploadChecked,
  324. hideFileListChecked: hideFileListChecked,
  325. publicUploadLabel: t('core', 'Allow upload and editing'),
  326. publicEditing: publicEditable,
  327. publicEditingChecked: publicEditingChecked,
  328. publicEditingLabel: t('core', 'Allow editing'),
  329. hideFileListLabel: t('core', 'File drop (upload only)'),
  330. mailPrivatePlaceholder: t('core', 'Email link to person'),
  331. mailButtonText: t('core', 'Send')
  332. }));
  333. this.$el.find('.clipboardButton').tooltip({placement: 'bottom', title: t('core', 'Copy'), trigger: 'hover'});
  334. this.delegateEvents();
  335. return this;
  336. },
  337. /**
  338. * @returns {Function} from Handlebars
  339. * @private
  340. */
  341. template: function () {
  342. if (!this._template) {
  343. this._template = Handlebars.compile(TEMPLATE);
  344. }
  345. return this._template;
  346. }
  347. });
  348. OC.Share.ShareDialogLinkShareView = ShareDialogLinkShareView;
  349. })();