Elbrus — Change Email Address Patch

To update an Elbrus 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
  , login
  , order
  , session
  , Application
  , environmentParameters
  ; 

        
With
            var SiteSettings
  , parameters
  , siteType
  , Environment
  , Language
  , Currency
  , Error
  , login
  , order
  , session
  , Application
  , environmentParameters
  , 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'
  , 'Header.Simplified.View'
  , 'Footer.Simplified.View'

  , 'Backbone'
  , 'underscore'
  , 'Utils'
  ]
, function (
    login_register_tpl

  , ProfileModel
  , LoginView
  , RegisterView
  , CheckoutAsGuestView
  , BackboneCompositeView
  , Configuration
  , HeaderSimplifiedView
  , FooterSimplifiedView

  , Backbone
  , _
  , Utils
  ) 

        
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'
  , 'Header.Simplified.View'
  , 'Footer.Simplified.View'

  , 'Backbone'
  , 'underscore'
  , 'Utils'
  ]
, function (
    login_register_tpl

  , GlobalViewsMessageView
  , ProfileModel
  , LoginView
  , RegisterView
  , CheckoutAsGuestView
  , BackboneCompositeView
  , Configuration
  , HeaderSimplifiedView
  , FooterSimplifiedView

  , Backbone
  , _
  , Utils
  ) 

        
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
            {
    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>

<div data-view="Messages"></div> 

        

Modify MyAccountApplication/SuiteScript/my_account.ssp

Replace
          <%
  var SiteSettings
  , siteType
  , Environment
  , Language
  , Currency
  , Error
  , Application
  , environmentParameters
  , session
  ,   parameters
  , external_payment
  ;

  try
  {
    SiteSettings = require('SiteSettings.Model').get();
    parameters = request.getAllParameters();
    session = require('SC.Models.Init').session;

    // 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);
    }

    Application = require('Application');
    Environment = Application.getEnvironment(request);
    environmentParameters = [];
    siteType = SiteSettings.sitetype;

    Language = Environment.currentLanguage && Environment.currentLanguage.locale || '';
    Currency = Environment.currencyCodeSpecifiedOnUrl;

    environmentParameters.push('lang=' + Language);
    environmentParameters.push('cur=' + Currency);
    environmentParameters.push('X-SC-Touchpoint=myaccount');

    _.each(require('ExternalPayment.Model').getParametersFromRequest(request), function(value, key) {
      environmentParameters.push(key.concat('=', value));
    });
  }
  catch (e)
  {
    Error = Application.processError(e);
  }

%> 

        
With
          <%
  var SiteSettings
  , siteType
  , Environment
  , Language
  , Currency
  , Error
  , Application
  , environmentParameters
  , session
  ,   parameters
  , external_payment
  , email_change_verification
  ;

  try
  {
    Application = require('Application');
    Environment = Application.getEnvironment(request);
    environmentParameters = [];
    SiteSettings = require('SiteSettings.Model').get();
    parameters = request.getAllParameters();
    session = require('SC.Models.Init').session;

    // 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;
      }
    }

    siteType = SiteSettings.sitetype;

    Language = Environment.currentLanguage && Environment.currentLanguage.locale || '';
    Currency = Environment.currencyCodeSpecifiedOnUrl;

    environmentParameters.push('lang=' + Language);
    environmentParameters.push('cur=' + Currency);
    environmentParameters.push('X-SC-Touchpoint=myaccount');

    _.each(require('ExternalPayment.Model').getParametersFromRequest(request), function(value, key) {
      environmentParameters.push(key.concat('=', value));
    });
  }
  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.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 () 

        

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
          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)
{
  ModelsInit.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">{{email}}</p> 

        
With
          <p class="profile-information-input-email" id="email">{{email}} | <a class="profile-information-change-email-address" data-action="change-email">{{translate 'Change Address'}}</a></p> 

        

General Notices