summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/js/share/sharedialoglinkshareview_popover_menu.handlebars10
-rw-r--r--core/js/sharedialoglinkshareview.js64
-rw-r--r--core/js/shareitemmodel.js5
-rw-r--r--core/js/sharetemplates.js29
-rw-r--r--core/js/tests/specs/sharedialoglinkshareview.js113
-rw-r--r--core/js/tests/specs/shareitemmodelSpec.js13
6 files changed, 222 insertions, 12 deletions
diff --git a/core/js/share/sharedialoglinkshareview_popover_menu.handlebars b/core/js/share/sharedialoglinkshareview_popover_menu.handlebars
index cc951ce047d..59312bc70b0 100644
--- a/core/js/share/sharedialoglinkshareview_popover_menu.handlebars
+++ b/core/js/share/sharedialoglinkshareview_popover_menu.handlebars
@@ -62,6 +62,16 @@
</span>
</li>
{{/if}}
+ {{#if showPasswordByTalkCheckBox}}
+ <li>
+ <span class="shareOption menuitem">
+ <span class="icon-loading-small hidden"></span>
+ <input type="checkbox" name="passwordByTalk" id="passwordByTalk-{{cid}}" class="checkbox passwordByTalkCheckbox"
+ {{#if isPasswordByTalkSet}}checked="checked"{{/if}} />
+ <label for="passwordByTalk-{{cid}}">{{passwordByTalkLabel}}</label>
+ </span>
+ </li>
+ {{/if}}
<li>
<span class="menuitem">
<input id="expireDate-{{cid}}" type="checkbox" name="expirationDate" class="expireDate checkbox"
diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js
index 4ea8c0fa153..e5af4ad1f17 100644
--- a/core/js/sharedialoglinkshareview.js
+++ b/core/js/sharedialoglinkshareview.js
@@ -54,6 +54,7 @@
'focusout input.linkPassText': 'onPasswordEntered',
'keyup input.linkPassText': 'onPasswordKeyUp',
'change .showPasswordCheckbox': 'onShowPasswordClick',
+ 'change .passwordByTalkCheckbox': 'onPasswordByTalkChange',
'change .publicEditingCheckbox': 'onAllowPublicEditingChange',
// copy link url
'click .linkText': 'onLinkTextClick',
@@ -96,6 +97,37 @@
view.render();
});
+ this.model.on('change:linkShares', function(model, linkShares) {
+ // The "Password protect by Talk" item is shown only when there
+ // is a password. Unfortunately there is no fine grained
+ // rendering of items in the link shares, so the whole view
+ // needs to be rendered again when the password of a share
+ // changes.
+ // Note that this event handler is concerned only about password
+ // changes; other changes in the link shares does not trigger
+ // a rendering, so the view must be rendered again as needed in
+ // those cases (for example, when a link share is removed).
+
+ var previousLinkShares = model.previous('linkShares');
+ if (previousLinkShares.length !== linkShares.length) {
+ return;
+ }
+
+ var i;
+ for (i = 0; i < linkShares.length; i++) {
+ if (linkShares[i].id !== previousLinkShares[i].id) {
+ // A resorting should never happen, but just in case.
+ return;
+ }
+
+ if (linkShares[i].password !== previousLinkShares[i].password) {
+ view.render();
+
+ return;
+ }
+ }
+ });
+
if(!_.isUndefined(options.configModel)) {
this.configModel = options.configModel;
} else {
@@ -343,6 +375,32 @@
});
},
+ onPasswordByTalkChange: function(event) {
+ var $element = $(event.target);
+ var $li = $element.closest('li[data-share-id]');
+ var shareId = $li.data('share-id');
+ var $checkbox = $li.find('.passwordByTalkCheckbox');
+ $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
+
+ var sendPasswordByTalk = false;
+ if($checkbox.is(':checked')) {
+ sendPasswordByTalk = true;
+ }
+
+ this.model.saveLinkShare({
+ sendPasswordByTalk: sendPasswordByTalk,
+ cid: shareId
+ }, {
+ success: function() {
+ $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock');
+ },
+ error: function(obj, msg) {
+ OC.Notification.showTemporary(t('core', 'Unable to toggle this option'));
+ $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock');
+ }
+ });
+ },
+
onAllowPublicEditingChange: function(event) {
var $element = $(event.target);
var $li = $element.closest('li[data-share-id]');
@@ -790,6 +848,9 @@
expireDate = moment(share.expiration, 'YYYY-MM-DD').format('DD-MM-YYYY');
}
+ var isTalkEnabled = oc_appswebroots['spreed'] !== undefined;
+ var sendPasswordByTalk = share.sendPasswordByTalk;
+
var showHideDownloadCheckbox = !this.model.isFolder();
var hideDownload = share.hideDownload;
@@ -816,6 +877,9 @@
passwordPlaceholder: isPasswordSet ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
isPasswordSet: isPasswordSet || isPasswordEnabledByDefault || isPasswordEnforced,
showPasswordCheckBox: showPasswordCheckBox,
+ showPasswordByTalkCheckBox: isTalkEnabled && isPasswordSet,
+ passwordByTalkLabel: t('core', 'Password protect by Talk'),
+ isPasswordByTalkSet: sendPasswordByTalk,
publicUploadRWChecked: publicUploadRWChecked,
publicUploadRChecked: publicUploadRChecked,
publicUploadWChecked: publicUploadWChecked,
diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js
index 1bbdb2448ab..f4ac03e1c18 100644
--- a/core/js/shareitemmodel.js
+++ b/core/js/shareitemmodel.js
@@ -19,6 +19,7 @@
* @property {string} token
* @property {bool} hideDownload
* @property {string|null} password
+ * @property {bool} sendPasswordByTalk
* @property {number} permissions
* @property {Date} expiration
* @property {number} stime share time
@@ -141,6 +142,7 @@
hideDownload: false,
password: '',
passwordChanged: false,
+ sendPasswordByTalk: false,
permissions: OC.PERMISSION_READ,
expireDate: this.configModel.getDefaultExpirationDateString(),
shareType: OC.Share.SHARE_TYPE_LINK
@@ -873,7 +875,8 @@
// hide_download is returned as an int, so force it
// to a boolean
hideDownload: !!share.hide_download,
- password: share.share_with
+ password: share.share_with,
+ sendPasswordByTalk: share.send_password_by_talk
}));
return share;
diff --git a/core/js/sharetemplates.js b/core/js/sharetemplates.js
index bd9886e6afd..6469e264d75 100644
--- a/core/js/sharetemplates.js
+++ b/core/js/sharetemplates.js
@@ -158,18 +158,30 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
},"11":function(container,depth0,helpers,partials,data) {
return "hidden";
},"13":function(container,depth0,helpers,partials,data) {
- return "datepicker";
+ var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
+
+ return " <li>\n <span class=\"shareOption menuitem\">\n <span class=\"icon-loading-small hidden\"></span>\n <input type=\"checkbox\" name=\"passwordByTalk\" id=\"passwordByTalk-"
+ + alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
+ + "\" class=\"checkbox passwordByTalkCheckbox\"\n "
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPasswordByTalkSet : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + " />\n <label for=\"passwordByTalk-"
+ + alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
+ + "\">"
+ + alias4(((helper = (helper = helpers.passwordByTalkLabel || (depth0 != null ? depth0.passwordByTalkLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"passwordByTalkLabel","hash":{},"data":data}) : helper)))
+ + "</label>\n </span>\n </li>\n";
},"15":function(container,depth0,helpers,partials,data) {
+ return "datepicker";
+},"17":function(container,depth0,helpers,partials,data) {
var helper;
return container.escapeExpression(((helper = (helper = helpers.expireDate || (depth0 != null ? depth0.expireDate : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"expireDate","hash":{},"data":data}) : helper)));
-},"17":function(container,depth0,helpers,partials,data) {
+},"19":function(container,depth0,helpers,partials,data) {
var helper;
return container.escapeExpression(((helper = (helper = helpers.defaultExpireDate || (depth0 != null ? depth0.defaultExpireDate : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"defaultExpireDate","hash":{},"data":data}) : helper)));
-},"19":function(container,depth0,helpers,partials,data) {
- return "readonly";
},"21":function(container,depth0,helpers,partials,data) {
+ return "readonly";
+},"23":function(container,depth0,helpers,partials,data) {
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return " <li>\n <a href=\"#\" class=\"menuitem pop-up\" data-url=\""
@@ -193,6 +205,7 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.publicEditing : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showHideDownloadCheckbox : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showPasswordCheckBox : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showPasswordByTalkCheckBox : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " <li>\n <span class=\"menuitem\">\n <input id=\"expireDate-"
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
+ "\" type=\"checkbox\" name=\"expirationDate\" class=\"expireDate checkbox\"\n "
@@ -216,15 +229,15 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
+ "</label>\n <!-- do not use the datepicker if enforced -->\n <input id=\"expirationDatePicker-"
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
+ "\" class=\""
- + ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.isExpirationEnforced : depth0),{"name":"unless","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.isExpirationEnforced : depth0),{"name":"unless","hash":{},"fn":container.program(15, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\" type=\"text\"\n placeholder=\""
+ alias4(((helper = (helper = helpers.expirationDatePlaceholder || (depth0 != null ? depth0.expirationDatePlaceholder : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"expirationDatePlaceholder","hash":{},"data":data}) : helper)))
+ "\" value=\""
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"if","hash":{},"fn":container.program(15, data, 0),"inverse":container.program(17, data, 0),"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"if","hash":{},"fn":container.program(17, data, 0),"inverse":container.program(19, data, 0),"data":data})) != null ? stack1 : "")
+ "\"\n data-max-date=\""
+ alias4(((helper = (helper = helpers.maxDate || (depth0 != null ? depth0.maxDate : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"maxDate","hash":{},"data":data}) : helper)))
+ "\" "
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isExpirationEnforced : depth0),{"name":"if","hash":{},"fn":container.program(19, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isExpirationEnforced : depth0),{"name":"if","hash":{},"fn":container.program(21, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " />\n </span>\n </li>\n <li>\n <a href=\"#\" class=\"share-add\">\n <span class=\"icon-loading-small hidden\"></span>\n <span class=\"icon icon-edit\"></span>\n <span>"
+ alias4(((helper = (helper = helpers.addNoteLabel || (depth0 != null ? depth0.addNoteLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"addNoteLabel","hash":{},"data":data}) : helper)))
+ "</span>\n <input type=\"button\" class=\"share-note-delete icon-delete "
@@ -236,7 +249,7 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
+ "</textarea>\n <input type=\"submit\" class=\"icon-confirm share-note-submit\" value=\"\" id=\"add-note-"
+ alias4(((helper = (helper = helpers.shareId || (depth0 != null ? depth0.shareId : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"shareId","hash":{},"data":data}) : helper)))
+ "\" />\n </span>\n </li>\n"
- + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.social : depth0),{"name":"each","hash":{},"fn":container.program(21, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.social : depth0),{"name":"each","hash":{},"fn":container.program(23, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " <li>\n <a href=\"#\" class=\"unshare\"><span class=\"icon-loading-small hidden\"></span><span class=\"icon icon-delete\"></span><span>"
+ alias4(((helper = (helper = helpers.unshareLinkLabel || (depth0 != null ? depth0.unshareLinkLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"unshareLinkLabel","hash":{},"data":data}) : helper)))
+ "</span></a>\n </li>\n <li>\n <a href=\"#\" class=\"new-share\">\n <span class=\"icon-loading-small hidden\"></span>\n <span class=\"icon icon-add\"></span>\n <span>"
diff --git a/core/js/tests/specs/sharedialoglinkshareview.js b/core/js/tests/specs/sharedialoglinkshareview.js
index f5fe8725c03..c2d84fd2e87 100644
--- a/core/js/tests/specs/sharedialoglinkshareview.js
+++ b/core/js/tests/specs/sharedialoglinkshareview.js
@@ -235,4 +235,117 @@ describe('OC.Share.ShareDialogLinkShareView', function () {
});
+ describe('protect password by Talk', function () {
+
+ var $passwordByTalkCheckbox;
+ var $workingIcon;
+
+ beforeEach(function () {
+ // Needed to render the view
+ configModel.isShareWithLinkAllowed.returns(true);
+
+ // "Enable" Talk
+ window.oc_appswebroots['spreed'] = window.oc_webroot + '/apps/files/';
+
+ shareModel.set({
+ linkShares: [{
+ id: 123,
+ password: 'password'
+ }]
+ });
+ view.render();
+
+ $passwordByTalkCheckbox = view.$el.find('.passwordByTalkCheckbox');
+ $workingIcon = $passwordByTalkCheckbox.prev('.icon-loading-small');
+
+ sinon.stub(shareModel, 'saveLinkShare');
+
+ expect($workingIcon.hasClass('hidden')).toBeTruthy();
+ });
+
+ afterEach(function () {
+ shareModel.saveLinkShare.restore();
+ });
+
+ it('is shown if Talk is enabled and there is a password set', function() {
+ expect($passwordByTalkCheckbox.length).toBeTruthy();
+ });
+
+ it('is not shown if Talk is enabled but there is no password set', function() {
+ // Changing the password value also triggers the rendering
+ shareModel.set({
+ linkShares: [{
+ id: 123
+ }]
+ });
+
+ $passwordByTalkCheckbox = view.$el.find('.passwordByTalkCheckbox');
+
+ expect($passwordByTalkCheckbox.length).toBeFalsy();
+ });
+
+ it('is not shown if there is a password set but Talk is not enabled', function() {
+ // "Disable" Talk
+ delete window.oc_appswebroots['spreed'];
+
+ view.render();
+
+ $passwordByTalkCheckbox = view.$el.find('.passwordByTalkCheckbox');
+
+ expect($passwordByTalkCheckbox.length).toBeFalsy();
+ });
+
+ it('checkbox is checked when the setting is enabled', function () {
+ shareModel.set({
+ linkShares: [{
+ id: 123,
+ password: 'password',
+ sendPasswordByTalk: true
+ }]
+ });
+ view.render();
+
+ $passwordByTalkCheckbox = view.$el.find('.passwordByTalkCheckbox');
+
+ expect($passwordByTalkCheckbox.is(':checked')).toEqual(true);
+ });
+
+ it('checkbox is not checked when the setting is disabled', function () {
+ expect($passwordByTalkCheckbox.is(':checked')).toEqual(false);
+ });
+
+ it('enables the setting if clicked when unchecked', function () {
+ // Simulate the click by checking the checkbox and then triggering
+ // the "change" event.
+ $passwordByTalkCheckbox.prop('checked', true);
+ $passwordByTalkCheckbox.change();
+
+ expect($workingIcon.hasClass('hidden')).toBeFalsy();
+ expect(shareModel.saveLinkShare.withArgs({ sendPasswordByTalk: true, cid: 123 }).calledOnce).toBeTruthy();
+ });
+
+ it('disables the setting if clicked when checked', function () {
+ shareModel.set({
+ linkShares: [{
+ id: 123,
+ password: 'password',
+ sendPasswordByTalk: true
+ }]
+ });
+ view.render();
+
+ $passwordByTalkCheckbox = view.$el.find('.passwordByTalkCheckbox');
+ $workingIcon = $passwordByTalkCheckbox.prev('.icon-loading-small');
+
+ // Simulate the click by unchecking the checkbox and then triggering
+ // the "change" event.
+ $passwordByTalkCheckbox.prop('checked', false);
+ $passwordByTalkCheckbox.change();
+
+ expect($workingIcon.hasClass('hidden')).toBeFalsy();
+ expect(shareModel.saveLinkShare.withArgs({ sendPasswordByTalk: false, cid: 123 }).calledOnce).toBeTruthy();
+ });
+
+ });
+
});
diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js
index 3b4dc5a960f..e8016950094 100644
--- a/core/js/tests/specs/shareitemmodelSpec.js
+++ b/core/js/tests/specs/shareitemmodelSpec.js
@@ -169,7 +169,8 @@ describe('OC.Share.ShareItemModel', function() {
storage: 1,
token: 'tehtoken',
uid_owner: 'root',
- hide_download: 1
+ hide_download: 1,
+ send_password_by_talk: true
}
]));
@@ -189,6 +190,7 @@ describe('OC.Share.ShareItemModel', function() {
expect(linkShares.length).toEqual(1);
var linkShare = linkShares[0];
expect(linkShare.hideDownload).toEqual(true);
+ expect(linkShare.sendPasswordByTalk).toEqual(true);
// TODO: check more attributes
});
@@ -293,7 +295,8 @@ describe('OC.Share.ShareItemModel', function() {
storage: 1,
token: 'tehtoken',
uid_owner: 'root',
- hide_download: 0
+ hide_download: 0,
+ send_password_by_talk: false
}, {
displayname_owner: 'root',
expiration: '2015-10-15 00:00:00',
@@ -312,7 +315,8 @@ describe('OC.Share.ShareItemModel', function() {
storage: 1,
token: 'anothertoken',
uid_owner: 'root',
- hide_download: 1
+ hide_download: 1,
+ send_password_by_talk: true
}]
));
OC.currentUser = 'root';
@@ -327,6 +331,7 @@ describe('OC.Share.ShareItemModel', function() {
var linkShare = linkShares[0];
expect(linkShare.token).toEqual('tehtoken');
expect(linkShare.hideDownload).toEqual(false);
+ expect(linkShare.sendPasswordByTalk).toEqual(false);
// TODO: check child too
});
@@ -588,6 +593,7 @@ describe('OC.Share.ShareItemModel', function() {
hideDownload: false,
password: '',
passwordChanged: false,
+ sendPasswordByTalk: false,
permissions: OC.PERMISSION_READ,
expireDate: '',
shareType: OC.Share.SHARE_TYPE_LINK
@@ -612,6 +618,7 @@ describe('OC.Share.ShareItemModel', function() {
hideDownload: false,
password: '',
passwordChanged: false,
+ sendPasswordByTalk: false,
permissions: OC.PERMISSION_READ,
expireDate: '2015-07-24 00:00:00',
shareType: OC.Share.SHARE_TYPE_LINK