EP Snippet - Portal user idle timeout for logoff - custom javascript

HOWTO handle portal user idle timeout

Author: Arnaud Leymet
Submitted: 09 May 2008

Abstract

The question has been asked many times. Some solutions came out in the forums, but a few matched correctly the problem.

I'd like to share my solution which works great. This solution is a javascript one, which is located in the banner of the portal (masthead).

Solution

Simply edit the banner par file (com.sap.portal.navigation.masthead.par.bak) by applying the following :

  1. Create a javascript/ directory in the par structure.
  2. Add the file customtimeout.js in this directory : this is the customized timeout-handling javascript file.
  3. Edit the file PORTAL-INF/jsp/HeaderiView.jsp in order to call the timeout handler.

Source

Do not forget
Remove all 'REMOVEME' occurrences in these two files (I had troubles publishing this page on the wiki)!


javascript/customtimeout.js
/**
* Custom Timeout Handler
* @param warn_delay Delay until the warning message (in minutes)
* @param kick_delay Delay until the "session timed out" message (in minutes)
*/
var customTimeoutHandler = function(warn_delay, kick_delay) {
  return {
    debug_mode:         false,                  // Switches the display of the timer (function _displayTimer)
    timeout_wait:       1000 * 10,              // Delay for the 'setTimeout' native function
    timeout_warn_delay: 1000 * 60 * warn_delay, // Delay until the warning message
    timeout_kick_delay: 1000 * 60 * kick_delay, // Delay until the timeout message and the effective timeout
    /**
    * Initializes the custom portail timeout (called at first time)
    **/
    initialize: function() {
      this.updateTimer();
      REMOVEMEsetTimeout("timeout_h.checkTimeout()", this.timeout_wait);
    },
    /**
    * Updates the custom portail timeout (called whenever the user navigates in the portal)
    **/
    updateTimer: function() {
      var dt = new Date();
      writeCookie('custom_timeout', dt.getTime());
      if(this.debug_mode) {
        this._displayTimer();
      }
    },
    /**
    * Checks wether the timeout has occured or not ( repeatedly called)
    **/
    checkTimeout: function() {
      var now = new Date();
      var diff = now.getTime() - this._getTimerStart();
      if(diff > this.timeout_warn_delay) { // Action : unactive
        var date_timeout = new Date();
        date_timeout.setTime(this._getTimerStart().getTime() + this.timeout_kick_delay);
        var date_t_hours = date_timeout.getHours() + "";
        if(date_t_hours.length == 1) date_t_hours = "0" + date_t_hours;
        var date_t_min = date_timeout.getMinutes() + "";
        if(date_t_min.length == 1) date_t_min = "0" + date_t_min;
        var date_timeout_display = date_t_hours + ":" + date_t_min;
        REMOVEMEalert('The page was idle since '+warn_delay+' minutes.\n'+
          'Clicking on the OK button before  '+date_timeout_display+' will reactivate your session.');
        now = new Date();
        diff = now.getTime() - this._getTimerStart();
        if(diff > this.timeout_kick_delay) { // Action : kick (= time out)
          logoff();
        } else {
          this._refreshPage();
          this.updateTimer();
          REMOVEMEsetTimeout("timeout_h.checkTimeout()", this.timeout_wait);
        }
      } else {
        REMOVEMEsetTimeout("timeout_h.checkTimeout()", this.timeout_wait);
      }
      if(this.debug_mode) {
        this._displayTimer( diff + " > " + this.timeout_kick_delay + " ? " + (diff > this.timeout_kick_delay) );
      }
    },
    /**
    *
    */
    _getTimerStart: function() {
      var timeout_start = parseInt(readCookie('custom_timeout'));
      return new Date(timeout_start);
    },
    /**
    * Displays the timer informations in the window status bar
    **/
    _displayTimer: function(message) {
      var dt = this._getTimerStart();
      var timer_start_string = dt.getHours() + ":" + dt.getMinutes() + ":" + dt.getSeconds();
      if(message && message!=null && message!="") {
        window.status = timer_start_string + " - " + message;
      } else {
        window.status = timer_start_string;
      }
    },
    /**
    * Refreshes the portal page content
    **/
    _refreshPage: function() {
      try {
        frameworkSupport.refreshContentArea();
      } catch(e) {
        document.location.reload()
      }
    },
    /**
    * Subscribes to an EPCM event
    **/
    addListener: function(urn, action) {
      EPCM.subscribeEvent(urn, action, this.updateTimer);
    }
  }
}


// cookies functions

function writeCookie(name, value) {
  document.cookie = name + "=" + escape(value);
}
function readCookie(name) {
  var arg = name+'=';
  var alen = arg.length;
  var clen = document.cookie.length;
  var i=0;
  while (i < clen){
    var j = i + alen;
    if (document.cookie.substring(i, j) == arg) return getCookieVal(j);
    i=document.cookie.indexOf(' ', i) + 1;
    if (i == 0) break;
  }
  return null;
}
function getCookieVal(offset){
  var endstr = document.cookie.indexOf (';', offset);
  if (endstr == -1) endstr = document.cookie.length;
  return unescape(document.cookie.substring(offset, endstr));
}
PORTAL-INF/jsp/HeaderiView.jsp
// ...
<REMOVEMEscript type="text/javaREMOVEMEscript"
    src="/irj/portalapps/{par_file_name}/javascript/customtimeout.js"    >
</script>
<REMOVEMEscript type="text/javaREMOVEMEscript">
  var timeout_h = customTimeoutHandler(30, 40);
  timeout_h.initialize();
  function pop() { timeout_h.updateTimer(); }
  EPCM.subscribeEvent("urn:com.sapportals:navigation", "Navigate", pop);
  //EPCM.subscribeEvent("urn:com.foo.bar.myapp", "myEvent", pop);
  // Subscribe to your events here
</script>
// ...

Don't forget to change the 'par_file_name' string in the latter file and remove all 'REMOVEME' occurrences in these two files (I had troubles publishing this page on the wiki).

Known limitations

This solution has some limitations.
Among them, the most embarassing one is the fact that the timeout timer is only reseted by subscribed events.
Unchanged, it only resets the timer on navigation changes:

EPCM.subscribeEvent("urn:com.sapportals:navigation", "Navigate", pop););

If you want to add events for resetting the timer, you'll have to add this type of lines:

EPCM.subscribeEvent("urn:com.company.my:myservice1", "OnChange", pop););
EPCM.subscribeEvent("urn:com.company.my:myservice2", "OnRequest", pop););
//etc.

Expected result

After being idle on the portal for some time, you shoud obtain a popup like the following:

Hope you'll find it usable!

Labels

snippet snippet Delete
ep ep Delete
ep_snippet ep_snippet Delete
idle idle Delete
timeout timeout Delete
logoff logoff Delete
portal portal Delete
inactivity inactivity Delete
logout logout Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.