Denali — Change Email Address Patch
To update a Denali implementation to give users the ability to change their email address, you’ll need to modify and add the files as detailed below.
Important:
This addition requires changes to templates, views, JavaScript, and SSP application files. Test changes thoroughly against your existing customizations before deploying to your published domain.
Modify CheckoutApplication/SuiteScript/checkout.ssp
Replace
var SiteSettings
, parameters
, siteType
, Environment
, Language
, Currency
, Error
, cart_bootstrap
, login
, Application;
With
var SiteSettings
, parameters
, siteType
, Environment
, Language
, Currency
, Error
, cart_bootstrap
, login
, Application
, password_reset_expired;
Replace
if (session.isChangePasswordRequest())
{
parameters.fragment = 'reset-password';
login = true;
}
With
if (parameters.passwdret)
{
try
{
if (session.isChangePasswordRequest())
{
parameters.fragment = 'reset-password';
login = true;
}
}
catch (e)
{
password_reset_expired = true;
}
}
Replace
<% if (Error) { %>
<script>SC.ENVIRONMENT.contextError = <%= JSON.stringify(Error) %>;</script>
<% } %>
With
<% if (Error) { %>
<script>SC.ENVIRONMENT.contextError = <%= JSON.stringify(Error) %>;</script>
<% } %>
<% if (parameters.key) { %>
<script>SC.ENVIRONMENT.email_verification_error = true;</script>
<% } else if (password_reset_expired) { %>
<script>SC.ENVIRONMENT.password_reset_expired_error = true;</script>
<% } else if (parameters.passwdret && parameters.fragment !== 'reset-password') { %>
<script>SC.ENVIRONMENT.password_reset_invalid_error = true;</script>
<% } %>
Modify LoginRegister/Javascript/LoginRegister.View.js
Replace
define('LoginRegister.View'
, [ 'login_register.tpl'
, 'Profile.Model'
, 'LoginRegister.Login.View'
, 'LoginRegister.Register.View'
, 'LoginRegister.CheckoutAsGuest.View'
, 'Backbone.CompositeView'
, 'SC.Configuration'
, 'Backbone'
, 'underscore'
, 'Utils'
]
, function (
login_register_tpl
, ProfileModel
, LoginView
, RegisterView
, CheckoutAsGuestView
, BackboneCompositeView
, Configuration
, Backbone
, _
)
With
define('LoginRegister.View'
, [ 'login_register.tpl'
, 'GlobalViews.Message.View'
, 'Profile.Model'
, 'LoginRegister.Login.View'
, 'LoginRegister.Register.View'
, 'LoginRegister.CheckoutAsGuest.View'
, 'Backbone.CompositeView'
, 'SC.Configuration'
, 'Backbone'
, 'underscore'
, 'Utils'
]
, function (
login_register_tpl
, GlobalViewsMessageView
, ProfileModel
, LoginView
, RegisterView
, CheckoutAsGuestView
, BackboneCompositeView
, Configuration
, Backbone
, _
)
Replace
this.enableCheckoutAsGuest = is_checking_out && profile_model.get('isLoggedIn') === 'F' &&
(Configuration.getRegistrationType() === 'optional' || Configuration.getRegistrationType() === 'disabled');
BackboneCompositeView.add(this);
With
this.enableCheckoutAsGuest = is_checking_out && profile_model.get('isLoggedIn') === 'F' &&
(Configuration.getRegistrationType() === 'optional' || Configuration.getRegistrationType() === 'disabled');
if (SC.ENVIRONMENT.email_verification_error)
{
this.message = _('The validation process has failed. Please login into your account and click on the validation link again.').translate();
delete SC.ENVIRONMENT.email_verification_error;
}
else if (SC.ENVIRONMENT.password_reset_invalid_error)
{
this.message = _('Your reset password link is invalid. Request a new one using the Forgot Password link.').translate();
delete SC.ENVIRONMENT.password_reset_invalid_error;
}
else if (SC.ENVIRONMENT.password_reset_expired_error)
{
this.message = _('Your reset password link has expired. Request a new one using the Forgot Password link.').translate();
delete SC.ENVIRONMENT.password_reset_expired_error;
}
BackboneCompositeView.add(this);
Replace
, 'Register': function ()
{
return new RegisterView(this.child_view_options);
}
}
With
, 'Register': function ()
{
return new RegisterView(this.child_view_options);
}
, 'Messages': function ()
{
if (this.message)
{
return new GlobalViewsMessageView({
message: this.message
, type: 'error'
, closable: true
});
}
}
}
Modify LoginRegister/Templates/login_register.tpl
Replace
<header class="login-register-header">
{{#if showRegister}}
<h1 class="login-register-title"> {{translate 'Log in | Register'}}</h1>
{{else}}
<h1 class="login-register-title"> {{translate 'Log in'}}</h1>
{{/if}}
</header>
With
<header class="login-register-header">
{{#if showRegister}}
<h1 class="login-register-title"> {{translate 'Log in | Register'}}</h1>
{{else}}
<h1 class="login-register-title"> {{translate 'Log in'}}</h1>
{{/if}}
</header>
Modify MyAccountApplication/SuiteScript/my_account.ssp
Replace
<%
var SiteSettings
, siteType
, Environment
, Language
, Currency
, Error
, Application;
try
{
Application = require('Application');
SiteSettings = require('SiteSettings.Model').get();
siteType = SiteSettings.sitetype;
Environment = Application.getEnvironment(session, request);
Language = Environment.currentLanguage && Environment.currentLanguage.locale || '';
Currency = Environment.currencyCodeSpecifiedOnUrl;
// Access control, if you are not loged this will send you to the log in page
if (!session.isLoggedIn() || session.getCustomer().isGuest())
{
var parameters = request.getAllParameters();
delete parameters.sitepath;
parameters.origin = 'customercenter';
if (parameters.fragment)
{
parameters.origin_hash = parameters.fragment;
delete parameters.fragment;
}
return nlapiSetRedirectURL('EXTERNAL', SiteSettings.touchpoints.login, null, false, parameters);
}
} catch (e) {
Error = Application.processError(e);
}
%>
With
<%
var SiteSettings
, siteType
, Environment
, Language
, Currency
, Error
, Application
, parameters
, email_change_verification
;
try
{
Application = require('Application');
SiteSettings = require('SiteSettings.Model').get();
parameters = request.getAllParameters();
siteType = SiteSettings.sitetype;
Environment = Application.getEnvironment(session, request);
Language = Environment.currentLanguage && Environment.currentLanguage.locale || '';
Currency = Environment.currencyCodeSpecifiedOnUrl;
// Access control, if you are not loged this will send you to the log in page
if (!session.isLoggedIn2() || session.getCustomer().isGuest())
{
delete parameters.sitepath;
parameters.origin = 'customercenter';
if (parameters.fragment)
{
parameters.origin_hash = parameters.fragment;
delete parameters.fragment;
}
return nlapiSetRedirectURL('EXTERNAL', SiteSettings.touchpoints.login, null, false, parameters);
}
else if (session.isLoggedIn2() && parameters.key)
{
try
{
session.verifyEmailChange(parameters.key)
email_change_verification = true;
}
catch (e)
{
email_change_verification = e.details;
}
}
} catch (e) {
Error = Application.processError(e);
}
%>
Replace
<% if (Error) { %>
<script>SC.ENVIRONMENT.contextError = <%= JSON.stringify(Error) %>;</script>
<% } %>
With
<% if (Error) { %>
<script>SC.ENVIRONMENT.contextError = <%= JSON.stringify(Error) %>;</script>
<% } %>
<% if (email_change_verification) { %>
<script>SC.SESSION.email_change_verification = '<%= email_change_verification %>';</script>
<% } %>
Modify Overview/Javascript/Overview.Home.View.js
Replace
define('Overview.Home.View'
, [
'SC.Configuration'
, 'Overview.Banner.View'
, 'Overview.Profile.View'
, 'Overview.Payment.View'
, 'Overview.Shipping.View'
, 'Backbone.CollectionView'
, 'OrderHistory.List.Tracking.Number.View'
, 'RecordViews.View'
, 'Handlebars'
, 'overview_home.tpl'
, 'Backbone'
, 'Backbone.CompositeView'
, 'underscore'
, 'Utils'
]
, function(
Configuration
, OverviewBannerView
, OverviewProfileView
, OverviewPaymentView
, OverviewShippingView
, BackboneCollectionView
, OrderHistoryListTrackingNumberView
, RecordViewsView
, Handlebars
, overview_home_tpl
, Backbone
, BackboneCompositeView
, _
)
With
define('Overview.Home.View'
, [
'SC.Configuration'
, 'GlobalViews.Message.View'
, 'Overview.Banner.View'
, 'Overview.Profile.View'
, 'Overview.Payment.View'
, 'Overview.Shipping.View'
, 'Backbone.CollectionView'
, 'OrderHistory.List.Tracking.Number.View'
, 'RecordViews.View'
, 'Handlebars'
, 'overview_home.tpl'
, 'Backbone'
, 'Backbone.CompositeView'
, 'underscore'
, 'Utils'
]
, function(
Configuration
, GlobalViewsMessageView
, OverviewBannerView
, OverviewProfileView
, OverviewPaymentView
, OverviewShippingView
, BackboneCollectionView
, OrderHistoryListTrackingNumberView
, RecordViewsView
, Handlebars
, overview_home_tpl
, Backbone
, BackboneCompositeView
, _
)
Replace
this.creditcards.on('reset destroy change add', this.showContent, this);
}
With
this.creditcards.on('reset destroy change add', this.showContent, this);
if (SC.SESSION.email_change_verification)
{
this.email_change_verification = SC.SESSION.email_change_verification;
delete SC.SESSION.email_change_verification;
}
}
Replace
, 'Overview.Shipping': function()
{
return new OverviewShippingView({ model: this.defaultShippingAddress });
}
With
, 'Overview.Shipping': function()
{
return new OverviewShippingView({ model: this.defaultShippingAddress });
}
, 'Overview.Messages': function ()
{
if (this.email_change_verification)
{
return new GlobalViewsMessageView({
message: this.email_change_verification === 'true' ? _('Your email has been changed successfully to <strong>').translate() + this.model.get('email') + '</strong>' : this.email_change_verification
, type: this.email_change_verification === 'true' ? 'success' : 'error'
, closable: true
});
}
}
Modify Overview/Templates/overview_home.tpl
Replace
<section class="overview-home">
<div class="overview-home-orders" data-permissions="{{purchasesPermissions}}">
With
<section class="overview-home">
<div data-view="Overview.Messages"></div>
<div class="overview-home-orders" data-permissions="{{purchasesPermissions}}">
Add Profile/Javascript/Profile.ChangeEmailAddress.Model.js
// Profile.ChangeEmailAddress.Model.js
// ===================================--
// View Model for changing user's email
// @module Profile
define(
'Profile.ChangeEmailAddress.Model'
, [
'Backbone'
, 'underscore'
, 'Utils'
]
, function (
Backbone
, _
)
{
'use strict';
// @class Profile.ChangeEmailAddress.Model @extends Backbone.Model
return Backbone.Model.extend(
{
urlRoot: 'services/Profile.Service.ss'
, validation: {
current_password: { required: true, msg: _('Current password is required').translate() }
, confirm_email: [
{ required: true, msg: _('Confirm Email is required').translate() }
, { equalTo: 'new_email', msg: _('New Email and Confirm New Email do not match').translate() }
]
, new_email: { required: true, msg: _('New Email is required').translate() }
}
});
});
Add Profile/Javascript/Profile.ChangeEmailAddress.View.js
// @module Profile
define(
'Profile.ChangeEmailAddress.View'
, [
'GlobalViews.Message.View'
, 'Backbone.FormView'
, 'SC.Configuration'
, 'profile_change_email.tpl'
, 'Backbone'
, 'underscore'
, 'Utils'
]
, function (
GlobalViewsMessageView
, BackboneFormView
, Configuration
, profile_change_email_tpl
, Backbone
, _
)
{
'use strict';
// @class Profile.ChangeEmailAddress.View @extends Backbone.View
return Backbone.View.extend({
template: profile_change_email_tpl
, page_header: _('Change Email').translate()
, title: _('Change Email').translate()
, events: {
'submit form': 'saveFormCustom'
}
, bindings: {
'[name="current_password"]': 'current_password'
, '[name="new_email"]': 'new_email'
, '[name="confirm_email"]': 'confirm_email'
}
, initialize: function()
{
Backbone.View.prototype.initialize.apply(this, arguments);
BackboneFormView.add(this);
}
, saveFormCustom: function ()
{
this.new_email = this.$('[name="new_email"]').val();
BackboneFormView.saveForm.apply(this, arguments);
}
, showSuccess: function (placeholder)
{
var global_view_message = new GlobalViewsMessageView({
message: _('A confirmation email has been sent to <strong>').translate() + this.new_email + '</strong>'
, type: 'success'
, closable: true
});
placeholder.html(global_view_message.render().$el.html());
}
});
});
Modify Profile/Javascript/Profile.Information.View.js
Replace
define(
'Profile.Information.View'
, [
'SC.Configuration'
, 'GlobalViews.Message.View'
, 'Backbone.FormView'
, 'profile_information.tpl'
, 'Backbone'
, 'underscore'
, 'jQuery'
, 'Utils'
]
, function (
Configuration
, GlobalViewsMessageView
, BackboneFormView
, profile_information_tpl
, Backbone
, _
, jQuery
)
{
'use strict';
// @class Profile.Information.View @extends Backbone.View
return Backbone.View.extend({
template: profile_information_tpl
, page_header: _('Profile Information').translate()
, title: _('Profile Information').translate()
, attributes: {'class': 'ProfileInformationView'}
, events: {
'submit form': 'saveForm'
, 'change input[data-type="phone"]': 'formatPhone'
}
, bindings: {
'[name="firstname"]': 'firstname'
, '[name="lastname"]': 'lastname'
, '[name="companyname"]': 'companyname'
, '[name="phone"]': 'phone'
}
, initialize: function()
{
BackboneFormView.add(this);
}
, formatPhone: function (e)
{
var $target = jQuery(e.target);
$target.val(_($target.val()).formatPhone());
}
, showSuccess: function ()
With
define(
'Profile.Information.View'
, [
'SC.Configuration'
, 'GlobalViews.Message.View'
, 'Backbone.FormView'
, 'profile_information.tpl'
, 'Backbone'
, 'underscore'
, 'jQuery'
, 'Utils'
]
, function (
Configuration
, GlobalViewsMessageView
, BackboneFormView
, profile_information_tpl
, Backbone
, _
, jQuery
)
{
'use strict';
// @class Profile.Information.View @extends Backbone.View
return Backbone.View.extend({
template: profile_information_tpl
, page_header: _('Profile Information').translate()
, title: _('Profile Information').translate()
, attributes: {'class': 'ProfileInformationView'}
, events: {
'submit form': 'saveForm'
, 'change input[data-type="phone"]define(
'Profile.Information.View'
, [
'SC.Configuration'
, 'GlobalViews.Message.View'
, 'Backbone.FormView'
, 'Profile.ChangeEmailAddress.Model'
, 'Profile.ChangeEmailAddress.View'
, 'profile_information.tpl'
, 'Backbone'
, 'underscore'
, 'jQuery'
, 'Utils'
]
, function (
Configuration
, GlobalViewsMessageView
, BackboneFormView
, ProfileChangeEmailModel
, ProfileChangeEmailView
, profile_information_tpl
, Backbone
, _
, jQuery
)
{
'use strict';
// @class Profile.Information.View @extends Backbone.View
return Backbone.View.extend({
template: profile_information_tpl
, page_header: _('Profile Information').translate()
, title: _('Profile Information').translate()
, attributes: {'class': 'ProfileInformationView'}
, events: {
'submit form': 'saveForm'
, 'change input[data-type="phone"]': 'formatPhone'
, 'click [data-action="change-email"]': 'changeEmail'
}
, bindings: {
'[name="firstname"]': 'firstname'
, '[name="lastname"]': 'lastname'
, '[name="companyname"]': 'companyname'
, '[name="phone"]': 'phone'
}
, initialize: function(options)
{
BackboneFormView.add(this);
this.application = options.application;
}
, formatPhone: function (e)
{
var $target = jQuery(e.target);
$target.val(_($target.val()).formatPhone());
}
, changeEmail: function ()
{
var model = new ProfileChangeEmailModel(this.model.attributes);
var view = new ProfileChangeEmailView({
application: this.application
, model: model
});
var self = this;
model.on('save', function () {
view.showSuccess(self.$('[data-type="alert-placeholder"]'));
});
view.useLayoutError = true;
this.application.getLayout().showInModal(view);
}
, showSuccess: function ()
': 'formatPhone'
}
, bindings: {
'[name="firstname"]': 'firstname'
, '[name="lastname"]': 'lastname'
, '[name="companyname"]': 'companyname'
, '[name="phone"]': 'phone'
}
, initialize: function()
{
BackboneFormView.add(this);
}
, formatPhone: function (e)
{
var $target = jQuery(e.target);
$target.val(_($target.val()).formatPhone());
}
, showSuccess: function ()
Add Profile/Sass/_profile-change-email.scss
.profile-change-email-button-back {
@extend .button-back;
}
.profile-change-email-button-back-icon {
@extend .button-back-icon;
}
.profile-change-email-form-label {
display: inline-block;
}
.profile-change-email-form-group-label-required {
@extend .input-required;
}
.profile-change-email {
@extend .address-edit;
}
.profile-change-email-form-title {}
.profile-change-email-form-area {}
.profile-change-email-form {
margin-top: $sc-base-margin * 3;
}
.profile-change-email-form-group,
.profile-change-email-form-actions {
@extend .control-group;
}
.profile-change-email-form-group-label {
@extend .input-label
}
.profile-change-email-form-group-label-required {
}
.profile-change-email-form-group-input {
@extend .input-large
}
.profile-change-email-form-actions-change {
@extend .button-primary;
@extend .button-medium;
}
.profile-change-email-group-form-controls{}
.profile-change-email-form-info-block{}
Modify Profile/SuiteScript/Profile.Model.js
Replace
define(
'Profile.Model'
, ['SC.Model', 'Utils']
, function (SCModel, Utils)
With
define(
'Profile.Model'
, ['SC.Model', 'Models.Init', 'Utils']
, function (SCModel, ModelsInit, Utils)
Replace
if (data.email && data.email !== this.currentSettings.email && data.email === data.confirm_email)
{
if(data.isGuest === 'T')
{
customerUpdate.email = data.email;
}
else
{
login.changeEmail(data.current_password, data.email, true);
}
}
With
if (data.email && data.email !== this.currentSettings.email && data.email === data.confirm_email && data.isGuest === 'T')
{
customerUpdate.email = data.email;
}
else if (data.new_email && data.new_email === data.confirm_email && data.new_email !== this.currentSettings.email)
{
session.login({
email: data.email
, password: data.current_password
});
login.changeEmail(data.current_password, data.new_email, true);
}
Add Profile/Templates/profile_change_email.tpl
<section class="profile-change-email">
<div data-type="alert-placeholder"></div>
<div class="profile-change-email-form-area">
<form class="profile-change-email-form">
<fieldset>
<small class="profile-change-email-form-label">{{translate 'Required'}} <span class="profile-change-email-form-group-label-required">*</span></small>
<div class="profile-change-email-form-group" data-input="new_email" data-validation="control-group">
<label class="profile-change-email-form-group-label" for="new_email">{{translate 'New Email'}} <span class="profile-change-email-form-group-label-required">*</span></label>
<div class="profile-change-email-group-form-controls" data-validation="control">
<input type="email" class="profile-change-email-form-group-input" id="new_email" name="new_email" value="" placeholder="{{translate 'your@example.com'}}">
</div>
</div>
<div class="profile-change-email-form-group" data-input="confirm_email" data-validation="control-group">
<label class="profile-change-email-form-group-label" for="confirm_email">{{translate 'Confirm New Email'}} <span class="profile-change-email-form-group-label-required">*</span></label>
<div class="profile-change-email-group-form-controls" data-validation="control">
<input type="email" class="profile-change-email-form-group-input" id="confirm_email" name="confirm_email" value="" placeholder="{{translate 'your@example.com'}}">
</div>
</div>
<div class="profile-change-email-form-group" data-input="current_email" data-validation="control-group">
<label class="profile-change-email-form-group-label" for="current_password">{{translate 'Password'}} <span class="profile-change-email-form-group-label-required">*</span></label>
<div class="profile-change-email-group-form-controls" data-validation="control">
<input type="password" class="profile-change-email-form-group-input" id="current_password" name="current_password" value="">
</div>
</div>
</fieldset>
<p class="profile-change-email-form-info-block"><small> {{translate 'You will still be able to login with your current email address and password until your new email address is verified.'}} </small></p>
<div class="profile-change-email-form-actions">
<button type="submit" class="profile-change-email-form-actions-change">{{translate 'Send Verification Email'}}</button>
</div>
</form>
</div>
</section>
Modify Profile/Templates/profile_information.tpl
Replace
<p class="profile-information-input-email" id="email" name="email" >{{email}}</p>
With
<p class="profile-information-input-email" id="email" name="email" >{{email}} | <a class="profile-information-change-email-address" data-action="change-email">{{translate 'Change Address'}}</a></p>