Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loading+error state for privacy dialog #2484

Merged
merged 7 commits into from
Feb 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* Remove tmp and unused files [#2328](https://github.com/CartoDB/cartodb/pull/2328)
* Open privacy dialog directly from items [#2442](https://github.com/CartoDB/cartodb/pull/2442)
* Fixes error handling when adding an erroneous WMS URL.
* Add loading+error state for privacy dialog [#2484](https://github.com/CartoDB/cartodb/pull/2484)

Bugfixes:
* When being in any configuration page remove the arrow from the breadcrumb [#2312](https://github.com/CartoDB/cartodb/pull/2312)
Expand Down
2 changes: 1 addition & 1 deletion config/frontend.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#This file defines the version of the frontend code that is going to be loaded.
3.7.5
3.7.6
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
<%= itemName %> privacy
</p>
<p class="DefaultSecondary">
Although we believe in the power of open data, you can also protected your tables.
Although we believe in the power of open data, you can also protect your <%= itemType %>.
</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ module.exports = cdb.core.View.extend({
'click .js-back' : '_onClickBack'
},

initialize: function(args) {
this._org = args.organization;
this._permission = args.permission;
this._canChangeWriteAccess = args.canChangeWriteAccess;
this._tableMetadata = args.tableMetadata;
initialize: function() {
if (!this.options.viewModel) {
throw new Error('viewModel is compulsory');
}
this._viewModel = this.options.viewModel;
this.add_related_model(this._viewModel);

this._template = cdb.templates.getTemplate('new_dashboard/dialogs/change_privacy/share_view/template');
},

render: function() {
this.clearSubViews();

var usersCount = this._org.users.length;
var usersCount = this._organization().users.length;

this.$el.html(
this._template({
Expand All @@ -42,27 +43,31 @@ module.exports = cdb.core.View.extend({
return this;
},

_organization: function() {
return this._viewModel.get('user').organization;
},

_renderOrganizationPermissionView: function() {
this._appendPermissionView(
new PermissionView({
model: this._org,
permission: this._permission,
canChangeWriteAccess: this._canChangeWriteAccess,
model: this._organization(),
permission: this._viewModel.get('permission'),
canChangeWriteAccess: this._viewModel.canChangeWriteAccess(),
title: 'Default settings for your Organization',
desc: 'New users will have this permission'
})
);
},

_renderUserPermissionViews: function() {
var usersUsingVis = this._usersUsingVis();
var usersUsingVis = this._viewModel.usersUsingVis();

this._org.users.each(function(user) {
this._organization().users.each(function(user) {
this._appendPermissionView(
new PermissionView({
model: user,
permission: this._permission,
canChangeWriteAccess: this._canChangeWriteAccess,
permission: this._viewModel.get('permission'),
canChangeWriteAccess: this._viewModel.canChangeWriteAccess(),
title: user.get('username'),
desc: user.get('name'),
avatarUrl: user.get('avatar_url'),
Expand All @@ -72,30 +77,18 @@ module.exports = cdb.core.View.extend({
}, this);
},

_usersUsingVis: function() {
return _.chain(_.union(
this._tableMetadata.get('dependent_visualizations'),
this._tableMetadata.get('non_dependent_visualizations')
))
.compact()
.map(function(visData) {
return visData.permission.owner;
})
.value();
},

_appendPermissionView: function(view) {
this.$('.js-permissions').append(view.render().el);
this.addView(view);
},

_onClickBack: function(ev) {
this.killEvent(ev);
this.trigger('click:back');
this._viewModel.changeState('Start');
},

_onClickSave: function(ev) {
this.killEvent(ev);
this.trigger('click:save');
this._viewModel.save();
}
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var cdb = require('cartodb.js');
var _ = require('underscore');
var $ = require('jquery');

var DISABLED_SAVE_CLASS_NAME = 'is-disabled';

Expand All @@ -13,45 +14,48 @@ module.exports = cdb.core.View.extend({
'click .js-save' : '_onClickSave',
'click .js-option' : '_selectOption',
'click .js-share' : '_onClickShare',
'keyup .js-password-input' : '_updatePassword',
'blur .js-password-input' : 'render' // make sure the view is consistently rendered after writing password
'keyup .js-password-input' : '_updatePassword'
},

initialize: function(args) {
this.vis = args.vis; // of model cdb.admin.Visualization
this.user = args.user;
this.upgradeUrl = args.upgradeUrl;
this.privacyOptions = args.privacyOptions;
initialize: function() {
if (!this.options.viewModel) {
throw new Error('viewModel is compulsory');
}
this._viewModel = this.options.viewModel;
this._viewModel.get('privacyOptions').bind('change', this.render, this);
this.add_related_model(this._viewModel);

this.template = cdb.templates.getTemplate('new_dashboard/dialogs/change_privacy/start_view_template');

this.privacyOptions.bind('change', this.render, this);
this.add_related_model(this.privacyOptions);
},

render: function() {
var pwdOption = this.privacyOptions.passwordOption();
var selectedOption = this.privacyOptions.selectedOption();
var org = this.user.organization;
this.clearSubViews();

var privacyOptions = this._viewModel.get('privacyOptions');
var pwdOption = privacyOptions.passwordOption();
var selectedOption = privacyOptions.selectedOption();
var upgradeUrl = this._viewModel.get('upgradeUrl');

this.$el.html(
this.template({
itemName: this.vis.get('name'),
options: this.privacyOptions,
itemName: this._viewModel.get('vis').get('name'),
options: privacyOptions,
showPasswordInput: pwdOption === selectedOption,
pwdOption: pwdOption,
saveBtnClassNames: selectedOption.canSave() ? '' : DISABLED_SAVE_CLASS_NAME,
showUpgradeBanner: !!this.upgradeUrl && this.privacyOptions.any(function(option) { return !!option.get('disabled'); }),
upgradeUrl: this.upgradeUrl,
showShareBanner: !!org
showUpgradeBanner: upgradeUrl && privacyOptions.any(function(o) { return !!o.get('disabled'); }),
upgradeUrl: upgradeUrl,
showShareBanner: this._viewModel.shouldShowShareBanner()
})
);

return this;
},

_selectOption: function(ev) {
var option = this.privacyOptions.at( $(ev.target).closest('.js-option').attr('data-index') );
var i = $(ev.target).closest('.js-option').data('index');
var option = this._viewModel.get('privacyOptions').at(i);

if (!option.get('disabled')) {
option.set('selected', true);
}
Expand All @@ -60,19 +64,19 @@ module.exports = cdb.core.View.extend({
_updatePassword: function(ev) {
// Reflect state directly in DOM instead of re-rendering to avoid loosing the focus on input
var pwd = ev.target.value;
this.privacyOptions.passwordOption().set({ password: pwd }, { silent: true });
this._viewModel.get('privacyOptions').passwordOption().set({ password: pwd }, { silent: true });
this.$('.js-save')[ _.isEmpty(pwd) ? 'addClass' : 'removeClass' ](DISABLED_SAVE_CLASS_NAME);
},

_onClickShare: function(ev) {
if (this.privacyOptions.selectedOption().canSave()) {
if (this._viewModel.canSave()) {
this.killEvent(ev);
this.trigger('click:share');
this._viewModel.changeState('Share');
}
},

_onClickSave: function(ev) {
this.killEvent(ev);
this.trigger('click:save');
this._viewModel.save();
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
var cdb = require('cartodb.js');
var _ = require('underscore');
var PrivacyOptions = require('./options_collection');

/**
* View model for a change privacy dialog
*/
module.exports = cdb.core.Model.extend({

defaults: {
vis: undefined,
user: undefined,
upgradeUrl: '',
state: 'Start',
hasPermissionsChanged: false,
privacyOptions: undefined
},

initialize: function(attrs) {
if (!attrs.vis) {
throw new Error('vis is required');
}
if (!attrs.user) {
throw new Error('user is required');
}
this.set('privacyOptions', PrivacyOptions.byVisAndUser(attrs.vis, attrs.user));

if (attrs.user.organization) {
this._setupSharePrerequisities();
}
},

changeState: function(newState) {
// Force a change event
this.set('state', undefined, { silent: true });
this.set('state', newState);
},

canShare: function() {
return !!this.get('permission');
},

usersUsingVis: function() {
var metadata = this.get('vis').tableMetadata();
return _.chain(_.union(
metadata.get('dependent_visualizations'),
metadata.get('non_dependent_visualizations')
))
.compact()
.map(function(d) {
return d.permission.owner;
})
.value();
},

shouldShowShareBanner: function() {
return this.get('user').organization;
},

shouldRenderDialogWithExpandedLayout: function() {
return this.get('state') === 'Share';
},

canChangeWriteAccess: function() {
return !this.get('vis').isVisualization();
},

canSave: function() {
return this.get('privacyOptions').selectedOption().canSave();
},

save: function() {
var selectedOption = this.get('privacyOptions').selectedOption();
if (selectedOption.canSave()) {
this.changeState('Saving');
var self = this;
selectedOption.saveToVis(this.get('vis'))
.done(function() {
self.get('hasPermissionsChanged') ? self._savePermissionChanges() : self._saveDone();
})
.fail(this._saveFail.bind(this));
}
},

_setupSharePrerequisities: function() {
var vis = this.get('vis');
this.set('permission', vis.permission.clone());
this.get('permission').acl.bind('all', function() {
this.set('hasPermissionsChanged', true);
}, this);

if (!vis.isVisualization()) {
var self = this;
vis.tableMetadata().fetch({
silent: true,
success: function() {
if (self.get('state') === 'Share') {
self.changeState('Share');
}
}
});
}
},

_savePermissionChanges: function() {
var originalPermission = this.get('vis').permission;
originalPermission.overwriteAcl(this.get('permission'));
originalPermission.save()
.done(this._saveDone.bind(this))
.fail(this._saveFail.bind(this));
},

_saveDone: function() {
this.changeState('SaveDone');
},

_saveFail: function() {
this.changeState('SaveFail');
}

});
Loading