diff --git a/core/css/share.css b/core/css/share.css index bdf34e9a86..73bad5c500 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -126,6 +126,14 @@ a.unshare { margin-right: 0; } +.shareTabView .error { + color: #e9322d; + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + #link #showPassword img { padding-left:5px; width:12px; diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js index f244fe5654..c5d8c82965 100644 --- a/core/js/sharedialogexpirationview.js +++ b/core/js/sharedialogexpirationview.js @@ -19,13 +19,13 @@ // in the LinkShareView to ease reusing it in future. Then, // modifications (getting rid of IDs) are still necessary. '{{#if isLinkShare}}' + - '' + '' + - ' {{#if isExpirationSet}}' + - '' + - '' + - ' {{/if}}' + + '
' + + ' ' + + ' ' + + '
' + ' {{#if isExpirationEnforced}}' + // originally the expire message was shown when a default date was set, however it never had text '{{defaultExpireMessage}}' + @@ -58,6 +58,11 @@ className: 'hidden', + events: { + 'change .expirationCheckbox': '_onToggleExpiration', + 'change .datepicker': '_onChangeExpirationDate' + }, + initialize: function(options) { if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; @@ -79,6 +84,38 @@ }); }, + _onToggleExpiration: function(event) { + var $checkbox = $(event.target); + var state = $checkbox.prop('checked'); + // TODO: slide animation + this.$el.find('.expirationDateContainer').toggleClass('hidden', !state); + if (!state) { + // discard expiration date + this.model.setExpirationDate(''); + this.model.saveLinkShare(); + } + }, + + _onChangeExpirationDate: function(event) { + var $target = $(event.target); + $target.tooltip('hide'); + $target.removeClass('error'); + + this.model.setExpirationDate($target.val()); + this.model.saveLinkShare(null, { + error: function(model, message) { + if (!message) { + $target.attr('title', t('core', 'Error setting expiration date')); + } else { + $target.attr('title', message); + } + $target.tooltip({gravity: 'n'}); + $target.tooltip('show'); + $target.addClass('error'); + } + }); + }, + render: function() { var defaultExpireMessage = ''; var defaultExpireDays = this.configModel.get('defaultExpireDate'); @@ -136,6 +173,8 @@ this.$el.find('.datepicker').datepicker({dateFormat : 'dd-mm-yy'}); + this.delegateEvents(); + return this; }, diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index f49a73f291..cadec3071d 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -108,7 +108,8 @@ if($checkBox.is(':checked')) { if(this.configModel.get('enforcePasswordForPublicLink') === false) { $loading.removeClass('hidden'); - this.model.addLinkShare(); + // this will create it + this.model.saveLinkShare(); } else { this.$el.find('#linkPass').slideToggle(OC.menuSpeed); // TODO drop with IE8 drop @@ -131,7 +132,8 @@ onShowPasswordClick: function() { this.$el.find('#linkPass').slideToggle(OC.menuSpeed); if(!this.$el.find('#showPassword').is(':checked')) { - this.model.addLinkShare({password: ''}); + this.model.setPassword(''); + this.model.saveLinkShare(); } else { this.$el.find('#linkPassText').focus(); } @@ -147,13 +149,15 @@ .removeClass('hidden') .addClass('inlineblock'); - this.model.addLinkShare({password: password}); + this.model.setPassword(password); + this.model.saveLinkShare(); }, onAllowPublicUploadChange: function() { this.$el.find('#sharingDialogAllowPublicUpload') .siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock'); this.model.setPublicUpload(this.$el.find('#sharingDialogAllowPublicUpload').is(':checked')); + this.model.saveLinkShare(); }, render: function() { @@ -205,6 +209,7 @@ mailButtonText: t('core', 'Send') })); + // TODO: move this to delegate events instead this.$el.find('#linkCheckbox').change(this.onLinkCheckBoxChange); this.$el.find('#sharingDialogAllowPublicUpload').change(this.onAllowPublicUploadChange); this.$el.find('#linkText').click(this.onLinkTextClick); diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index eb44b73573..2a97bc8abd 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -70,6 +70,9 @@ * Represents the GUI of the share dialogue * * // FIXME: use OC Share API once #17143 is done + * + * // TODO: this really should be a collection of share item models instead, + * where the link share is one of them */ var ShareItemModel = OC.Backbone.Model.extend({ initialize: function(attributes, options) { @@ -90,47 +93,114 @@ linkShare: {} }, - addLinkShare: function(options) { + /** + * Saves the current link share information. + * + * This will trigger an ajax call and refetch the model afterwards. + * + * TODO: this should be a separate model + */ + saveLinkShare: function(attributes, options) { var model = this; - var expiration = this.configModel.getDefaultExpirationDateString(); var itemType = this.get('itemType'); var itemSource = this.get('itemSource'); - var options = options || {}; - var requiredOptions = [ + // TODO: use backbone's default value mechanism once this is a separate model + var requiredAttributes = [ { name: 'password', defaultValue: '' }, - { name: 'permissions', defaultValue: OC.PERMISSION_READ } + { name: 'permissions', defaultValue: OC.PERMISSION_READ }, + { name: 'expiration', defaultValue: this.configModel.getDefaultExpirationDateString() } ]; - _.each(requiredOptions, function(option) { + + attributes = attributes || {}; + + // get attributes from the model and fill in with default values + _.each(requiredAttributes, function(attribute) { // a provided options overrides a present value of the link // share. If neither is given, the default value is used. - if(_.isUndefined(options[option.name])) { - options[option.name] = option.defaultValue; - var currentValue = model.get('linkShare')[option.name]; + if(_.isUndefined(attribute[attribute.name])) { + attributes[attribute.name] = attribute.defaultValue; + var currentValue = model.get('linkShare')[attribute.name]; if(!_.isUndefined(currentValue)) { - options[option.name] = currentValue; + attributes[attribute.name] = currentValue; } } }); - OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, options.password, options.permissions, this.fileInfoModel.get('name'), expiration, function(data) { - model.fetch(); - //FIXME: updateIcon belongs to view - OC.Share.updateIcon(itemType, itemSource); - }); + OC.Share.share( + itemType, + itemSource, + OC.Share.SHARE_TYPE_LINK, + attributes.password, + attributes.permissions, + this.fileInfoModel.get('name'), + attributes.expiration, + function(result) { + if (!result || result.status !== 'success') { + model.fetch({ + success: function() { + if (options && _.isFunction(options.success)) { + options.success(model); + } + } + }); + } else { + if (options && _.isFunction(options.error)) { + options.error(model); + } + } + //FIXME: updateIcon belongs to view + OC.Share.updateIcon(itemType, itemSource); + }, + function(result) { + var msg = t('core', 'Error'); + if (result.data && result.data.message) { + msg = result.data.message; + } + + if (options && _.isFunction(options.error)) { + options.error(model, msg); + } else { + OC.dialogs.alert(msg, t('core', 'Error while sharing')); + } + } + ); }, removeLinkShare: function() { this.removeShare(OC.Share.SHARE_TYPE_LINK, ''); }, + /** + * Sets the public upload flag + * + * @param {bool} allow whether public upload is allowed + */ setPublicUpload: function(allow) { var permissions = OC.PERMISSION_READ; if(allow) { - permissions = OC.PERMISSION_UPDATE + OC.PERMISSION_CREATE + OC.PERMISSION_READ; + permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ; } - this.addLinkShare({permissions: permissions}); + this.get('linkShare').permissions = permissions; + }, + + /** + * Sets the expiration date of the public link + * + * @param {string} expiration expiration date + */ + setExpirationDate: function(expiration) { + this.get('linkShare').expiration = expiration; + }, + + /** + * Set password of the public link share + * + * @param {string} password + */ + setPassword: function(password) { + this.get('linkShare').password = password; }, addShare: function(event, selected, options) {