Source: src/js/views/SignInView.js

/*global define */

define(['jquery', 'underscore', 'backbone', 'text!templates/login.html',
        'text!templates/alert.html', 'text!templates/loginButtons.html',
        'text!templates/loginOptions.html', 'text!templates/login-ldap.html'],
  function($, _, Backbone, LoginTemplate, AlertTemplate, LoginButtonsTemplate, LoginOptionsTemplate, LdapLoginTemplate) {
  'use strict';

  /**
  * @class SignInView
  * @classcategory Views
  * @extends Backbone.View
  * @screenshot views/SignInView.png
  */
  var SignInView = Backbone.View.extend(
    /** @lends SignInView.prototype */{

    template: _.template(LoginTemplate),
    alertTemplate: _.template(AlertTemplate),
    buttonsTemplate: _.template(LoginButtonsTemplate),
    loginOptionsTemplate: _.template(LoginOptionsTemplate),
    ldapTemplate: _.template(LdapLoginTemplate),

    tagName: "div",
    className: "sign-in-btns",

    ldapError: false,

    /* Set to true to only show the LDAP login form */
    ldapOnly: false,

    /* Set to true if this SignInView is the only thing on the page */
    fullPage: false,

    /* Set to true if this SignInView is in a modal window */
    inPlace: false,

    /**
    * Set a query string that will be added to the redirect URL
    * when the user has logged-in and is returned back to MetacatUI.
    * This can be useful to return back to MetacatUI with additional information
    * about the state of the app when the user left to sign in.
    * @type {string}
    * @example
    * // This example may tell the view that the register citation modal was open when Sign In was clicked
    * "registerCitation=true"
    * @default ""
    * @since 2.15.0
    */
    redirectQueryString: "",

    /*A message to display at the top of the view */
    topMessage: "",

    initialize: function(options){
      if(typeof options !== "undefined"){
        this.inPlace = options.inPlace;
        this.topMessage = options.topMessage;
        this.fullPage = options.fullPage;
        this.closeButtons = options.closeButtons === false? false : true;
      }
    },

    render: function(){
      //Don't render a SignIn view if there are no Sign In URLs configured
      if(!MetacatUI.appModel.get("signInUrlOrcid"))
        return this;

      var view = this;

      if(this.inPlace){
        this.$el.addClass("hidden modal");
        this.$el.attr("data-backdrop", "static");

        //Add a message to the top, if supplied
        if(typeof this.topMessage == "string")
          this.$el.prepend('<p class="center container">' + this.topMessage + '</p>');
        else if(typeof this.topMessage == "object")
          this.$el.prepend(this.topMessage);

        //Copy/paste the contents of the sign-in popup
        var signInBtns = $.parseHTML($("#signinPopup").html().trim()),
          signInBtnsContainer = $(document.createElement("div")).addClass("center container").html(signInBtns);

        signInBtnsContainer.find("a.signin").each(function(i, a){
          var url = $(a).attr("href");

          var redirectUrl = decodeURIComponent(url.substring( url.indexOf("target=")+7 ));

          var urlWithoutHttp = redirectUrl.substring( redirectUrl.indexOf("://") +  3 );
          var routeName = urlWithoutHttp.substring( urlWithoutHttp.indexOf("/") + 1 );

          if( !routeName ){
            redirectUrl = redirectUrl + "signinsuccess";
          }
          else if( _.contains(MetacatUI.uiRouter.getRouteNames(), MetacatUI.uiRouter.getRouteName(routeName)) ){
            redirectUrl = redirectUrl.substring(0, redirectUrl.indexOf(routeName)) + "signinsuccess";
          }

          url = url.substring(0, url.indexOf("target=")+7) + encodeURIComponent(redirectUrl);
          $(a).attr("href", url);
        });

        signInBtnsContainer.find("h1, h2").remove();

        this.$el.append(signInBtnsContainer);

        //Remove the accordion widget from the ldap login so it gets displayed as a popup window instead
        if( this.$("#signinLdap").length ){
          this.$("[href='" + "#signinLdap']").addClass("signin");
          this.$(".accordion").removeClass("accordion");
        }

        this.$el.prepend($(document.createElement("div")).addClass("container").prepend(
            $(document.createElement("a")).text("Close").addClass("close").prepend(
            $(document.createElement("i")).addClass("icon icon-on-left icon-remove"))));

        //Listen for clicks
        this.$("a.signin").on("click", function(e){
          //Get the link URL and change the target to a special route
          e.preventDefault();

          var link = e.target;
          if(link.nodeName != "A") link = $(link).parents("a");

          var url = $(link).attr("href");

          //Open up a new small window with this URL to allow the user to login
          window.open(url, "Login", "height=600,width=700");

          //Listen for successful sign-in
          window.listenForSignIn = setInterval(function(){
            MetacatUI.appUserModel.checkToken(function(data){
              $(".modal.sign-in-btns").modal("hide");
              clearInterval(window.listenForSignIn);

              if(MetacatUI.appUserModel.get("checked"))
                MetacatUI.appUserModel.trigger("change:checked");
              else
                MetacatUI.appUserModel.set("checked", true);
            });
          }, 750);
        });

        if( this.closeButtons ){
          this.$("a.close").on("click", function(e){
            view.$el.modal("hide");
          });
        }
        else{
          this.$(".close").remove();

          //Create a sign out link
          var divider     = document.createElement("hr"),
              signOutLink = $(document.createElement("a"))
                              .addClass("error underline")
                              .text("Sign Out");

          //Add the Sign Out link to the view
          this.$(".modal-body").append(divider, signOutLink);

          //If we're on the EML211EditorView, then show a warning message that unsaved changes will be lost
          if( MetacatUI.appView.currentView && MetacatUI.appView.currentView.type == "EML211Editor" ){
            signOutLink.after( $(document.createElement("p"))
              .addClass("error")
              .text("   Warning! - All your unsaved changes will be lost."));
          }

          //When the sign out link is clicked, we can just refresh the page.
          signOutLink.on("click", function(e){
            window.location.reload();
          });

        }
      }
      else{

        //If it's a full-page sign-in view, then empty it first
        if(this.el == MetacatUI.appView.el || this.fullPage){
          this.$el.empty();
          var container = document.createElement("div");
          container.className = "container login";

          if( this.ldapOnly ){

            var redirectUrl = window.location.origin + window.location.pathname;
            redirectUrl = redirectUrl.substring(0, redirectUrl.lastIndexOf("/"));
            redirectUrl + "/signinSuccessLdap";

            $(container).append(this.ldapTemplate({
              redirectUrl:  redirectUrl
            }));
            this.$el.append(container);

            //Hide all the other page elements so it's just the login form
            $("#Navbar, #HeaderContainer, #Footer").hide();
          }
          else{
            $(container).append(this.buttonsTemplate({
              signInUrl: MetacatUI.appModel.get('signInUrlOrcid') + this.getRedirectURL()
            }));
            this.$el.append(container);
          }

        }
        else{

          if( this.ldapOnly ){

            var redirectUrl = MetacatUI.root + "/signinSuccessLdap";

            this.$el.append(this.ldapTemplate({
              redirectUrl: redirectUrl
            }));

          }
          else{
            let signInUrl = MetacatUI.appModel.get('signInUrlOrcid') + this.getRedirectURL();

            this.$el.append(this.buttonsTemplate({
              signInUrl: signInUrl
            }));
          }

        }

        //Insert the sign in popup screen once
        if(!$("#signinPopup").length){
          var target = this.getRedirectURL();
          var signInUrl = MetacatUI.appModel.get('signInUrl')? MetacatUI.appModel.get('signInUrl') + target : null;
          var signInUrlOrcid = MetacatUI.appModel.get('signInUrlOrcid') ? MetacatUI.appModel.get('signInUrlOrcid') + target : null;
          var signInUrlLdap = MetacatUI.appModel.get('signInUrlLdap') ? MetacatUI.appModel.get('signInUrlLdap') + target : null,
              redirectUrl = (window.location.href.indexOf("signinldaperror") > -1) ?
                  window.location.href.replace("signinldaperror", "") : window.location.href;

          $("body").append(this.template({
            signInUrl:  signInUrl,
            signInUrlOrcid:  signInUrlOrcid,
            signInUrlLdap:  signInUrlLdap,
            ldapLoginForm: this.ldapTemplate({
              redirectUrl: redirectUrl
            }),
            currentUrl: window.location.href,
            loginOptions: this.loginOptionsTemplate({ signInUrl: signInUrl }).trim(),
            collapseLdap: !MetacatUI.appUserModel.get("errorLogin"),
            redirectUrl: redirectUrl
          }));

          this.setUpPopup();
        }

        //If there is an error message in the URL, it means authentication has failed
        if(this.ldapError){
          MetacatUI.appUserModel.failedLdapLogin();
          this.failedLdapLogin();
        };
      }

      return this;
    },

    /*
     * This function is executed when LDAP authentication fails in the DataONE portal
     */
    failedLdapLogin: function(){
      //Insert an error message
      this.$("form").before(this.alertTemplate({
        classes: "alert-error",
        msg: "Incorrect username or password. Please try again."
      }));

      //If this is a full-page sign-in view, then take the form and insert it into the page
      if(this.$el.attr("id") == "Content" && !$("#Content form").length)
        $("#Content").html( $("#signinLdap").html() );
      //Else, just show the login in the modal window
      else if(!this.ldapOnly){
        $("#signinPopup").modal("show");
      }

      //Show the LDAP login form
      $('#signinLdap').removeClass("collapse").css("height", "auto");
    },

    setUpPopup: function(){
      var view = this;

      //Initialize the modal elements
      $("#signupPopup, #signinPopup").modal({
        show: false,
        shown: function(){

          //Update the sign-in URLs so we are redirected back to the previous page after authentication
          if( MetacatUI.appModel.get("enableCILogonSignIn") ){
            $("a.update-sign-in-url").attr("href", MetacatUI.appModel.get("signInUrl") + encodeURIComponent(window.location.href));
          }
          $("a.update-orcid-sign-in-url").attr("href", MetacatUI.appModel.get("signInUrlOrcid") + encodeURIComponent(window.location.href));

        }
      });
    },

    /**
    * Constructs and returns a string of the URL that the user should return to when they are done signing in.
    * This URL is sent to the DataONE portal service during login, via the `target` URL attribute. The DataONE
    * portal will redirect the user back to the `target` URL when sign in is complete.
    * @returns {string}
    */
    getRedirectURL: function(){
      let redirectURL = window.location.href;

      if( this.redirectQueryString && this.redirectQueryString.length ){
        let currentQueryString = window.location.search;

        //If there is a current query string in the window.location, concatenate the
        // new query string properly
        if( currentQueryString.length ){

          //Exclude the "?" character from the query string, if it is there already
          if( this.redirectQueryString.charAt(0) == "?" ){
            this.redirectQueryString = this.redirectQueryString.substring(1)
          }

          //Add the new query string parameters
          redirectURL += "&" + this.redirectQueryString;

        }
        else{
          redirectURL += "?" + this.redirectQueryString;
        }
      }

      return redirectURL;
    },

    onClose: function(){
      this.$el.empty();

      if(window.listenForSignIn)  clearInterval(window.listenForSignIn);
    }
  });
  return SignInView;

});