Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,117 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const CoreView = require('backbone/core-view');
const PagesSubheader = require('dashboard/components/pages-subheader/pages-subheader.js');
const AccountFormView = require('./account-form-view');
const ModalsServiceModel = require('builder/components/modals/modals-service-model');
const template = require('./account-content.tpl');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'userModel',
'configModel',
'flashMessageModel',
'client'
];
module.exports = CoreView.extend({
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._initModels();
this._initBinds();
},
_initBinds: function () {
this.listenTo(this.model, 'change:isLoading change:errors', this.render);
},
render: function () {
this.clearSubViews();
this.$el.html(template());
this._initViews();
return this;
},
_initModels: function () {
this.model = new Backbone.Model();
this.modals = new ModalsServiceModel();
},
_initViews: function () {
const pagesSubheader = new PagesSubheader({
userModel: this._userModel,
configModel: this._configModel
});
this.$('.js-SideMenu').append(pagesSubheader.render().el);
this.addView(pagesSubheader);
const accountFormView = new AccountFormView({
userModel: this._userModel,
renderModel: this.model,
configModel: this._configModel,
modals: this.modals,
setLoading: this._setLoading.bind(this),
onSuccess: this._showSuccess.bind(this),
onError: this._showErrors.bind(this),
client: this._client,
errors: this.model.get('errors')
});
this.$('.js-AccountContent').append(accountFormView.render().el);
this.addView(accountFormView);
},
_setLoading: function (message) {
this._flashMessageModel.hide();
this.model.set({
isLoading: !!message,
loadingText: message,
errors: []
});
},
_setFlashMessage: function (data, message, type) {
this._setLoading('');
const jsonData = data && data.responseJSON || {};
const errors = jsonData.errors;
let flashMessage = jsonData.message;
if (errors) {
this.model.set({ errors });
}
if (!flashMessage) {
flashMessage = message;
}
this._flashMessageModel.show(flashMessage, type);
},
_showSuccess: function (data) {
$(window).scrollTop(0);
_.extend(
this._userModel.attributes,
data.user_data
);
this._setFlashMessage(data, _t('account.flash_messages.save_changes.success'), 'success');
if (data.mfa_required) {
this._goToMultifactorAuthentication();
}
},
_goToMultifactorAuthentication: function () {
window.location = '/multifactor_authentication';
},
_showErrors: function (data) {
$(window).scrollTop(0);
this._setFlashMessage(data, _t('account.flash_messages.save_changes.error'), 'error');
}
});
@@ -0,0 +1,13 @@
<div class="CDB-Text FormAccount-Section u-inner">
<div class="SideMenu CDB-Text CDB-Size-medium js-SideMenu"></div>
<div class="FormAccount-Content">
<div class="FormAccount-title">
<p class="FormAccount-titleText"><%= _t('account.views.content.form_title') %></p>
</div>
<span class="FormAccount-separator"></span>
<div class="js-AccountContent"></div>
</div>
</div>
@@ -0,0 +1,211 @@
const _ = require('underscore');
const Backbone = require('backbone');
const CoreView = require('backbone/core-view');
const randomQuote = require('builder/components/loading/random-quote');
const ServiceItem = require('dashboard/components/service-item/service-item-view');
const template = require('./account-form.tpl');
const loadingTemplate = require('builder/components/loading/loading.tpl');
const DeleteAccountView = require('dashboard/components/delete-account/delete-account-view');
const PasswordValidatedForm = require('dashboard/helpers/password-validated-form');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'userModel',
'renderModel',
'configModel',
'modals',
'setLoading',
'onSuccess',
'onError',
'client'
];
module.exports = CoreView.extend({
events: {
'click .js-save': '_onClickSave',
'submit form': '_onClickSave',
'change .js-toggle-mfa': '_onToggleMfa'
},
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._initModels();
this._initBinds();
},
_initModels: function () {
this._errors = this.options.errors || {};
this.add_related_model(this._renderModel);
},
_initBinds: function () {
this._renderModel.bind('change:isLoading', this.render, this);
},
render: function () {
this.clearSubViews();
if (this._renderModel.get('isLoading')) {
this.$el.html(
loadingTemplate({
title: this._renderModel.get('loadingText'),
descHTML: randomQuote()
})
);
} else {
this.$el.html(template({
isCartoDBHosted: this._configModel.get('cartodb_com_hosted'),
formAction: this._configModel.prefixUrl() + '/account',
username: this._getField('username'),
errors: this._errors,
isInsideOrg: this._userModel.isInsideOrg(),
isAuthUsernamePasswordEnabled: this._getField('auth_username_password_enabled'),
hidePasswordFields: this._userModel.isInsideOrg() && !this._getField('auth_username_password_enabled'),
canChangePassword: this._getField('can_change_password'),
isOrgOwner: this._userModel.isOrgOwner(),
planName: this._getField('plan_name'),
planUrl: this._getField('plan_url'),
cantBeDeletedReason: this._getField('cant_be_deleted_reason'),
services: this._getField('services') || [],
mfaEnabled: this._getField('mfa_configured')
}));
this._initViews();
}
return this;
},
_setDeleteAccountView: function (event) {
this._modals.create(modalModel =>
new DeleteAccountView({
userModel: this._userModel,
modalModel,
onError: this._onError,
client: this._client
})
);
},
_initViews: function () {
const services = this._getField('services');
this.$('.js-deleteAccount').click(event => {
event && event.preventDefault();
this._setDeleteAccountView(event);
});
if (services && services.length) {
_.each(services, function (service) {
const serviceItem = new ServiceItem({
model: new Backbone.Model(_.extend({ state: 'idle' }, service)),
configModel: this._configModel
});
this.$('.js-datasourcesContent').after(serviceItem.render().el);
this.addView(serviceItem);
}, this);
}
},
_getField: function (field) {
return this._userModel.get(field);
},
_onClickSave: function (event) {
this.killEvent(event);
const origin = this._getUserFields();
const destination = this._getDestinationValues();
const destinationKeys = _.keys(destination);
const differenceKeys = _.filter(destinationKeys, key =>
origin[key] !== destination[key]
);
const user = _.pick(destination, differenceKeys);
if (!this._userModel.get('needs_password_confirmation')) {
return this._updateUser(user);
}
PasswordValidatedForm.showPasswordModal({
modalService: this._modals,
onPasswordTyped: (password) => this._updateUser(user, password),
updatePassword: destination.new_password !== '' && destination.confirm_password !== ''
});
},
_onToggleMfa: function (event) {
this.killEvent(event);
const newLabel = this._mfaStatus() ? _t('account.views.form.mfa_enabled') : _t('account.views.form.mfa_disabled');
this._mfaLabel().html(newLabel);
},
_updateUser: function (user, password) {
this._setLoading('Saving changes');
const userParams = { user: { ...user, password_confirmation: password } };
this._client.putConfig(userParams, (errors, response, data) => {
if (errors) {
this.options.onError(data, errors);
this.render();
} else {
this._getUser();
}
});
},
_getUser: function () {
this._client.getConfig((errors, response, data) => {
if (errors) {
this.options.onError(data, response, errors);
} else {
this.options.onSuccess(data);
}
this.render();
});
},
_getUserFields: function () {
return {
username: this._getField('username')
};
},
_getDestinationValues: function () {
return {
username: this._username(),
new_password: this._newPassword(),
confirm_password: this._confirmPassword(),
mfa: this._mfaStatus()
};
},
_username: function () {
return this.$('#user_username').val();
},
_newPassword: function () {
return this.$('#user_new_password').val();
},
_confirmPassword: function () {
return this.$('#confirm_password').val();
},
_mfaStatus: function () {
if (this.$('.js-toggle-mfa').length === 0) {
return false;
}
return this.$('.js-toggle-mfa')[0].checked;
},
_mfaLabel: function () {
return this.$('.js-mfa-label');
}
});
@@ -0,0 +1,127 @@
<form accept-charset="UTF-8">
<div class="FormAccount-row">
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor"><%= _t('account.views.form.username') %></label>
</div>
<div class="FormAccount-rowData">
<input class="CDB-InputText CDB-Text FormAccount-input FormAccount-input--med is-disabled" id="user_username" name="user[username]" readonly="readonly" size="30" type="text" value="<%= username %>">
<div class="FormAccount-rowInfo FormAccount-rowInfo--marginLeft">
<p class="CDB-Text CDB-Size-small u-altTextColor"><%= _t('account.views.form.subdomain_info') %></p>
</div>
</div>
</div>
<% if (!hidePasswordFields) { %>
<div class="VerticalAligned--FormRow">
<div class="FormAccount-row">
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor"><%= _t('account.views.form.new_password') %></label>
</div>
<div class="FormAccount-rowData">
<input class="CDB-InputText CDB-Text FormAccount-input FormAccount-input--med <% if (errors['new_password']) { %>has-error<% } %> <% if (!canChangePassword) { %>is-disabled<% } %>" id="user_new_password" name="user[new_password]" size="30" type="password" <% if (!canChangePassword) { %>readonly="readonly"<% } %>>
</div>
<div class="FormAccount-rowInfo">
<% if (errors['new_password']) { %>
<p class="FormAccount-rowInfoText FormAccount-rowInfoText--error u-tSpace"><%= errors['new_password'][0] %></p>
<% } %>
</div>
</div>
<div class="FormAccount-row js-confirmPassword">
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor"><%= _t('account.views.form.confirm_password') %></label>
</div>
<div class="FormAccount-rowData">
<input class="CDB-InputText CDB-Text FormAccount-input FormAccount-input--med <% if (!canChangePassword) { %>is-disabled<% } %>" id="confirm_password" name="user[confirm_password]" size="30" type="password" <% if (!canChangePassword) { %>readonly="readonly"<% } %>>
</div>
</div>
</div>
<% } %>
<div class="FormAccount-row">
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor">
<%= _t('account.views.form.multifactor_authentication') %>
</label>
</div>
<div class="FormAccount-rowData u-tspace-s u-vspace-s">
<div class="Toggler">
<input name="user[mfa]" type="hidden" value="0">
<input class="js-toggle-mfa" id="mfa" name="user[mfa]" type="checkbox" value="1" <% if (mfaEnabled) { %>checked="checked"<% } %>>
<label for="mfa"></label>
</div>
<div class="FormAccount-rowInfo u-lSpace--xl">
<p class="CDB-Text CDB-Size-medium js-mfa-label">
<%= mfaEnabled ? _t('account.views.form.mfa_enabled') : _t('account.views.form.mfa_disabled') %>
</p>
</div>
</div>
<div class="FormAccount-rowData u-tspace-xs">
<p class="CDB-Text CDB-Size-small u-altTextColor"><%= _t('account.views.form.mfa_description') %></p>
</div>
</div>
<% if ((!isInsideOrg || isOrgOwner) && !isCartoDBHosted) { %>
<div class="FormAccount-title">
<p class="FormAccount-titleText"><%= _t('account.views.form.account_type') %></p>
</div>
<span class="FormAccount-separator"></span>
<div class="FormAccount-row">
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor"><%= _t('account.views.form.billing_plan') %></label>
</div>
<div class="FormAccount-rowData">
<div class="FormAccount-planTag CDB-Size-medium"><%= planName %></div>
<div class="FormAccount-rowInfo FormAccount-rowInfo--marginLeft">
<p class="FormAccount-rowInfoText CDB-Size-medium"><a href="<%= planUrl %>" class="FormAccount-link"><%= _t('account.views.form.view_details') %></a></p>
</div>
</div>
</div>
<% } %>
<% if (services.length > 0) { %>
<div class="FormAccount-title">
<p class="FormAccount-titleText"><%= _t('account.views.form.connect_external_datasources') %></p>
</div>
<span class="FormAccount-separator"></span>
<div class="js-datasourcesContent"></div>
<% } %>
<div class="FormAccount-footer <% if (cantBeDeletedReason) { %>FormAccount-footer--noMarginBottom<% } %>">
<% if (cantBeDeletedReason) { %>
<p class="FormAccount-footerText">
<i class="CDB-IconFont CDB-IconFont-info FormAccount-footerIcon"></i>
<span><%= cantBeDeletedReason %></span>
</p>
<% } else { %>
<p class="FormAccount-footerText"></p>
<% } %>
<button type="submit" class="CDB-Button CDB-Button--primary">
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-small u-upperCase"><%= _t('account.views.form.save_changes') %></span>
</button>
</div>
<% if (!cantBeDeletedReason) { %>
<div class="FormAccount-title">
<p class="FormAccount-titleText"><%= _t('account.views.form.delete_account') %></p>
</div>
<span class="FormAccount-separator"></span>
<div class="FormAccount-row FormAccount-row--wideMarginBottom">
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor"><%= _t('account.views.form.confirm') %></label>
</div>
<div class="FormAccount-rowData">
<span class="FormAccount-button--deleteAccount CDB-Size-medium js-deleteAccount"><%= _t('account.views.form.delete_all') %></span>
</div>
</div>
<% } %>
</form>
@@ -0,0 +1,77 @@
const CoreView = require('backbone/core-view');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const TrialNotificationView = require('dashboard/components/trial-notification/trial-notification-view');
const AccountContentView = require('./account-content-view');
const UpgradeMessage = require('dashboard/components/upgrade-message-view.js');
const VendorScriptsView = require('dashboard/components/vendor-scripts/vendor-scripts-view');
const FlashMessageModel = require('dashboard/data/flash-message-model');
const FlashMessageView = require('dashboard/components/flash-message/flash-message-view');
var moment = require('moment');
const TRIAL_ACCOUNTS = ['PERSONAL30', 'Individual'];
const REQUIRED_OPTS = [
'userModel',
'configModel',
'client',
'assetsVersion',
'organizationNotifications'
];
module.exports = CoreView.extend({
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._initViews();
},
_initViews: function () {
const $app = this.$('#app');
const accountType = this._userModel.get('account_type');
if (TRIAL_ACCOUNTS.indexOf(accountType) > -1) {
const trialEndsAt = moment(this._userModel.get('trial_ends_at'));
const now = moment();
const trialNotificationView = new TrialNotificationView({
userModel: this._userModel,
upgradeUrl: this._configModel.get('upgrade_url'),
trialDays: Math.round(trialEndsAt.diff(now, 'days', true))
});
$app.append(trialNotificationView.render().el);
this.addView(trialNotificationView);
}
const flashMessageModel = new FlashMessageModel();
const flashMessageView = new FlashMessageView({
model: flashMessageModel
});
$app.prepend(flashMessageView.render().el);
this.addView(flashMessageView);
const accountContentView = new AccountContentView({
userModel: this._userModel,
configModel: this._configModel,
flashMessageModel,
client: this._client
});
$app.append(accountContentView.render().el);
this.addView(accountContentView);
const upgradeMessage = new UpgradeMessage({
userModel: this._userModel,
configModel: this._configModel
});
$app.prepend(upgradeMessage.render().el);
this.addView(upgradeMessage);
const vendorScriptsView = new VendorScriptsView({
configModel: this._configModel,
assetsVersion: this._assetsVersion,
userModel: this._userModel
});
this.$el.append(vendorScriptsView.render().el);
this.addView(vendorScriptsView);
return this;
}
});
@@ -0,0 +1,17 @@
const Backbone = require('backbone');
/**
* Header view model to handle state for dashboard header view.
*/
module.exports = Backbone.Model.extend({
breadcrumbTitle: () => 'Configuration',
isBreadcrumbDropdownEnabled: () => false,
isDisplayingDatasets: () => false,
isDisplayingMaps: () => false,
isDisplayingLockedItems: () => false
});
@@ -0,0 +1,87 @@
const CoreView = require('backbone/core-view');
const randomQuote = require('builder/components/loading/random-quote');
const ServiceInvalidate = require('dashboard/data/service-invalidate-model');
const loadingTemplate = require('builder/components/loading/loading.tpl');
const template = require('./service-disconnect-dialog.tpl');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'serviceModel',
'modalModel',
'configModel'
];
/**
* Disconnect service or help user to disconnect it
*
* - It needs the service model
*/
module.exports = CoreView.extend({
events: {
'click .js-revoke': '_revokeAccess',
'click .js-cancel': '_closeDialog'
},
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
CoreView.prototype.initialize.apply(this);
this._initBinds();
},
render: function () {
if (this._serviceModel.get('state') === 'loading') {
return this.$el.html(
loadingTemplate({
title: 'Revoking access',
descHTML: randomQuote()
})
);
}
return this.$el.html(template(this._serviceModel.attributes));
},
_initBinds: function () {
this.listenTo(this._serviceModel, 'change:state', this._maybeReplaceContent);
},
_maybeReplaceContent: function () {
if (this._serviceModel.get('state') !== 'error') {
this.render();
}
},
_revokeAccess: function () {
const invalidateModel = new ServiceInvalidate({ datasource: this._serviceModel.get('name') });
invalidateModel._configModel = this._configModel;
this._serviceModel.set('state', 'loading');
invalidateModel.destroy({
success: (model, response) => {
if (response.success) {
this._reloadWindow();
} else {
this._setErrorState();
}
},
error: () => this._setErrorState()
});
},
_setErrorState: function () {
this._serviceModel.set('state', 'error');
this._closeDialog();
},
_reloadWindow: function () {
window.location.reload();
},
_closeDialog: function () {
this._modalModel.destroy();
}
});
@@ -0,0 +1,41 @@
<div class="CDB-Text Dialog-header u-inner">
<div class="Dialog-headerIcon Dialog-headerIcon--negative">
<i class="CDB-IconFont CDB-IconFont-cloud"></i>
</div>
<p class="Dialog-headerTitle">
Disconnect your <%= title %> account
</p>
<p class="Dialog-headerText">
<% if (revoke_url) { %>
Revoke the access to CARTO
<% } else { %>
Are you sure you want to revoke the CARTO access to your <%= title %> account?
<% } %>
</p>
</div>
<% if (revoke_url) { %>
<div class="Dialog-body">
<p class="DefaultParagraph DefaultParagraph--short DefaultParagraph--centered DefaultParagraph--spaced">
We cant revoke the access for your <%= title %> account automatically.
</p>
<p class="DefaultParagraph DefaultParagraph--short DefaultParagraph--centered DefaultParagraph--spaced">
For your own security, we are unable to disconnect your <%= title %> account from CARTO. You can revoke access yourself by manually editing your <%= title %> authorized applications.
</p>
</div>
<% } %>
<div class="Dialog-footer u-inner">
<% if (revoke_url) { %>
<a href="<%- revoke_url%>" target="_blank" class="Button Button-inner Button--inline Button--secondary ">
<span>go to<%= title %></span>
</a>
<% } else { %>
<button class="CDB-Text Button Button--secondary Dialog-footerBtn Button--inline js-cancel">
<span>cancel</span>
</button>
<button class="CDB-Text js-revoke Button Button--negative Button--inline">
<span>Revoke access</span>
</button>
<% } %>
</div>