Discussion

38
Views
Richard Marsot (RichardMarsot)
PEGA
Director, Front-end Development
Pegasystems Inc.
US
RichardMarsot Member since 2011 27 posts
PEGA
Posted: 3 weeks 2 days ago
Last activity: 3 weeks 2 days ago
Posted: 6 Jul 2021 11:41 EDT
Last activity: 6 Jul 2021 12:31 EDT

How to auto-logoff the window for cases open in a new browser tab using Theme-Cosmos

One of the critical feature in Theme-Cosmos is the ability to open work objects in separate browser tabs.

browser tabs

Any link that renders the 'preview' button on hover will generate a valid 'href' link that the browser can use to open the case in a new browser tab. To open the case in a new tab, you can use 'right click -> open in new tab' or 'Cmd click'

One of the issue is that these tabs becomes independent. Each tab runs on a separate thread with a uniquely generated thread name. There is no communication between these tabs. As such if the user logs out from one tab, the other tabs will continue to be visible even if the session is no longer valid. Doing any action or a refresh will automatically bring back the user to the login page.

In this post, we are going to look at ways to bring some communication between these tabs. The main focus is the logoff use case, but other message could be sent to.

The method used in this post is based on the broadcast channel API - this API is not available on Safari or IE11. Another option is to use localstorage to send message. For more details - see https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API 

Note: The code and example were tested on Pega 8.6 and Theme-Cosmos 3.0. Some changes might be required with older version of the Pega Platform.

1/ Simple synchronization between tabs for logoff

To automatically reload the other browser tabs when a logout has been executed, you can add the following code to a JS file attached to your portal harness. The window.location.reload will trigger a browser reload on the other tabs and should bring back each tab to the login page.

var PegaChannel = new BroadcastChannel('pega-data');

pega.ui.EventsEmitter.subscribe("BeforeLogOff", function() {
     PegaChannel.postMessage('logoff');
}, null, null, null);
PegaChannel.addEventListener('message', function(event) {
    if (event.data === 'logoff') {
        setTimeout(function() { window.location.reload(); }, 1000);
    }
});

2/ Using pxSessionTimer

If you want to auto-logoff a user after a certain period of inactivity, you can use the pxSessionTimer. Include the non-auto section into your portal header (UserHeader). One of the challenge using pxSessionTimer is that each tab will run independently and if one of the tab is not used for a certain period of time, it will trigger automatically a logout even if the other tabs are being actively used.

pxsessionTimer demo

We will extend the code above to provide more synchronization between each tab and use the broadcast channel to use for both the logoff event as well as to reset the timer.

Here is the code to add to your portal header

var PegaChannel = new BroadcastChannel('pega-data');
var skipResetTimeoutWarning = false;

function closeLogoffTimer() {
    for (var i = 0; i < window.frames.length; i++) {
        if (window.frames[i].frameElement && window.frames[i].frameElement.src && window.frames[i].frameElement.src.indexOf(
                "ShowLogoffTimer") !== -1 && typeof window.frames[i].window.closeModal === "function") {
            try {
                window.frames[i].window.closeModal();
            } catch (e) {}
        }
    }
}

pega.ui.EventsEmitter.subscribe("BeforeLogOff", function() {
    PegaChannel.postMessage('logoff');
}, null, null, null);
PegaChannel.addEventListener('message', function(event) {
    console.log("PegaChannel event", event);
    if (event.data === 'logoff') {
        closeLogoffTimer();
        setTimeout(function() { window.location.reload(); }, 1000);
    } else if (event.data === 'restartTimeoutWarning') {
        closeLogoffTimer();
        desktop_restartTimeoutWarningTimer(true);
    }
});

function sendRestartTimeoutWarningMessage() {
    PegaChannel.postMessage('restartTimeoutWarning');
}

function desktop_restartTimeoutWarningTimer(skipBroadcast) {
    if (!skipBroadcast && skipResetTimeoutWarning) {
        skipResetTimeoutWarning = false;
        return;
    }
    if (pega.desktop.TimeoutTime && pega.desktop.TimeoutTime > 0) {
        var nTimeoutWarningTime = (pega.desktop.TimeoutTime - pega.desktop.TimeoutWarningWindow) * 60000;
        console.log("desktop_restartTimeoutWarningTimer - clear timer");
        clearTimeout(pega.desktop.TimeoutWarningCountdown);
        if (!skipBroadcast) sendRestartTimeoutWarningMessage();
        if (nTimeoutWarningTime >= 0) {
            pega.desktop.TimeoutWarningCountdown = self.setTimeout("desktop_showTimeoutWarning('" + pega.desktop.TimeoutWarningWindow +
                "')", nTimeoutWarningTime);
        }
    }
}

function desktop_showTimeoutWarning(strTime) {
    var iTime = parseInt(strTime);
    iTime = iTime * 60000;
    var oSafeURL = new SafeURL("@baseclass.ShowLogoffTimer");
    oSafeURL.put("time", iTime);
    skipResetTimeoutWarning = true;
    pega.u.d.convertToRunActivityAction(oSafeURL);
    pega.openUrlInModal.showModalDialog(oSafeURL, iTime, 236, 620, function(ret) {
        if (!ret || ret === null || ret === "ok") {
            desktop_restartTimeoutWarningTimer();
            sendRestartTimeoutWarningMessage();
        } else {
            pega.ui.HarnessContextMgr.set("gDirtyOverride", false);
            PegaChannel.postMessage('logoff');
            pega.u.d.replace('pyActivity=LogOff&pzPrimaryPageName=pyDisplayHarness', null);
        }
    });
}

Note: the console.log are for debug and troubleshooting purpose - they should be removed in production code. The video below should explain how this code works and how to test the implementation and different use cases

The attached RAP contains the JS file and the UserHeader changes with these changes.

Pega Platform 8.6 User Experience Developer Knowledge Share