Fiori launchpad: handle logout event with custom backend call without `attachLogoutEvent` (UI5 < 1.81) - sapui5

So, I found that the launchpad Container API provides an option to register a logout event with returning a promise (https://ui5.sap.com/#/api/sap.ushell.services.Container%23methods/attachLogoutEvent).
Unfortunately, after the implementation I found out that the UI5 version must be 1.81 or higher for parameter bAsync to work. In my project, we're at 1.78, so no promises for me.
What's the problem?
I want to make a backend call in the said logout event. This doesn't work, since, as far as I understood my debugging, the launchpad destroys everything just after my logout event has "finished" (= every line of code in the event has been gone through, ignoring sub-functions). Timeouts etc. don't work, because their calls would also be after code progressing has already finished, meaning the calls are deleted.
What have I tried?
Instant backend call without sub-functions → didn't work for the same reason as above.
Infinite while-looping until the backend call is processed → stack overflow.
While-looping with timeout/await → await not allowed in strict mode, timeout didn't work because of the above issue.
What do I think might work?
Stall code progression until the backend call has been finished.
Using a completely different method to get my logic into handling the logout (e.g. full custom logout).
Ask here for further ideas.
Does anyone have an idea on how to solve the issue with UI5 1.78?

Alright, I have found a solution to this. It's probably not the technically nicest, but it works and the result looks clean enough. This is from a S4/HANA system, so it might not be a universal solution (e.g. it doesn't consider logging off within the left-side pane which doesn't exist in my launchpad).
What did I do?
Instead of attaching my individual logic to the Fiori logout-event, I created a custom logout button with my individual logic, followed by calling the SICF logout node.
How did I do it?
Create a Launchpad plugin
In Component.js, add a new header item with custom logout function
// ushellLib required from "sap/ushell/library"
var oRenderer = ushellLib.Container.getRenderer("fiori2");
oRenderer.addHeaderEndItem("sap.ushell.ui.shell.ShellHeadItem", {
id: "logoutButton",
icon: "sap-icon://log",
// ...
press: [this._logout, this],
}, true, false);
_logout: function() {
this._callMyStuff();
window.location.href = "/sap/public/bc/icf/logoff";
},
In style.css, hide the original logout button (logoutBtn) in desktop (__list0...) and mobile (__list1...) to prevent skipping my logic by logging off via default logout.
#__list0-7-logoutBtn {
display: none;
}
#__list1-7-logoutBtn {
display: none;
}

Related

How to integrate Google Identity Toolkit with a single webpage app (e.g. GWT)

I need to integrate Google's identitytoolkit (Google's identitytoolkit) with my Google webtoolkit (GWT) application.
However rendering the gitkit signInButton or widget is already not straightforward because the way to do it is linked to "window.onload". And I need to render the widget at a later moment.
I managed to do it (see below), but I am not happy with this and I wonder if someone else found a better way of integration.
private native void showGitKitWidget() /*-{
$wnd.google.identitytoolkit.signInButton(
'#gitkitDivId', // accepts any CSS selector
{
widgetUrl: "//127.0.0.1:8888/gitkit/signin.html",
signOutUrl: "/gitkit/signout",
popupMode: true
}
);
var evt = $doc.createEvent('Event');
evt.initEvent('load', false, false);
$wnd.dispatchEvent(evt);
}-*/;
Update
Actually using the popupMode parameter for the signInButton makes a seamless integration even closer. This lets the widget popup in a browser window and leaving the GWT window unchanged. Then in the widget instead of redirecting to the successUrl I use the JS callback to trigger an AJAX call instead.
var config = {
idps: ["googleplus"],
signInSuccessUrl: '//127.0.0.1:8888/gwt/servlet/gitkit/signedin',
callbacks: {
signInSuccess: function(tokenString, accountInfo,
opt_signInSuccessUrl) {
/* !!! Tell GWT parent window that we are ready...
I believe using a cookie for which the parent is
regularly looking is the way to go, because it
will work in mobile browsers too.
*/
return false; // prevents redirect to signInSuccessUrl
}
}
Update Finally the GWT parent window will wait for the result cookie and if found make the AJAX call to the signInSuccessUrl. It will also have to render the signInButton again, which will then show the signed in user.
So the only ugly workaround is how the signInButton is rendered using the onload method call.
It would be very helpful if there would be a way to render the signInButton dynamically when needed, for instance if there were a "$wnd.google.identitytoolkit.update()" method. This could be called any time for the first time and should also be able to handle signin-status change!
In conclusion, I have answered my own question, which might be helpful to others, but also I would still like to ask if there would be a better way, which I missed.
As you've noted, the Identity Toolkit widget currently needs to be triggered by page load. Single-page applications (like those built with GWT) should place the widget on a separate page. Then you can redirect the user there - or render via popup, as you've noted - to sign in the user in.
If rendering the sign-in button is a problem, there is documentation on how to load the widget directly.

protractor - how to enforce protractor to wait for REST call to return information to complete building page elements

New to testing with protractor, actually new to test automation in general so hopefully this is posted in the right place. Apologies if not, please advise.
I am trying to test elements on a pages which are only displayed after a response to REST call. The page in question has two TABS, I click on the non-active TAB, it goes active, loads the TAB framework, then a few seconds (depending on the information returned) later a Graphical timeline is built in the TAB based upon the information from the REST call.
My problem is how to make protractor wait for the REST call to be completed and the Timeline built before continuing to test the elements in the Timeline.
From various searches I am using the following code. I am just picking a single element on the Timeline to check for isPresent. I have also tried using browser.wait as opposed to ptor.getInstance however have the same problem that the wait is not waiting for the timeline to load fully. Any guidance would be much appreciated.
it('XXXXXX', function() {
var ptor = protractor.getInstance();
// Define element to wait for in the Timeline Graphic
var waitForElement = by.css('.timeline-time');
// Load the page
jobManagerPage.go();
// Click the inactive TAB to make active
jobManagerPage.eleDataVisTabLiTag.click();
// This is where I am expecting to wait for the element in the Timeline
// to be present before continuing
browser.wait(function() { return ptor.isElementPresent(waitForElement);
}, 8000);
// Test for the Timeline element to be present - this keeps failing !!!!!
expect(ptor.isElementPresent(waitForElement)).toBeTruthy();
});
One solution (though far from ideal) is to add an element to the DOM when you're loading, and remove it when done (or add/remove a CSS class, etc...). You may or may not have the opportunity to do that, depending on how your AJAX calls are being made.
I found this approach in an answer to this SO question: AngularJS + Protractor wait for all ajax calls to end / full page load, before running the tests

Real time model events in Sails.js 0.10-rc5

I've been playing around with building some realtime functionality using Sails.js version 0.10-rc5 (currently the #beta release).
To accomplish anything, i've been following the sweet SailsCast tutorial on this subject (sailsCast link)
It talks about subscribing to a model via a 'subscribe' action within the model's controller. Then listening to it at the client side, waiting for the server to emit messages. Quite straightforward, although I do not seem to receive any messages.
I'm trying to do this to get real-time updates on anything that changes in my User models, or if new ones get created.. So I can display login status etc. in real time. Pretty much exactly the stuff that's explained in the sailsCast.
In my terminal i'll get two things worth noticing, of which the first is the following:
debug: Deprecated: `Model.subscribe(socket, null, ...)`
debug: See http://links.sailsjs.org/docs/config/pubsub
debug: (⌘ + double-click to open link from terminal)
debug: Please use instance rooms instead (or raw sails.sockets.*() methods.)
It seems like the 'subscribe' method has been deprecated. Could anybody tell me if that's correct, and tell me how to fix this? I've been checking out the reference to the documentation in the debug message, although it just points me to the global documentation page. I've been searching for an answer elsewhere, but haven't found anything useful.
The second message I'm getting is:
warn: You are trying to render a view (_session/new), but Sails doesn't support rendering views over Socket.io... yet!
You might consider serving your HTML view normally, then fetching data with sockets in your client-side JavaScript.
If you didn't intend to serve a view here, you might look into content-negotiation
to handle AJAX/socket requests explictly, instead of `res.redirect()`/`res.view()`.
Now, i'm quite sure this is because I have an 'isAuthenticated' policy added to all of my controllers and actions. When a user is not authenticated, it'll redirect to a session/new page. Somebody must log in to be able to use the application. When I remove the 'isAuthenticated' policy from the 'subscribed' action, the warnings disappear. Although that means anyone will get updates via sockets (when I get it to work), even when they're logged out. - I don't really feel like people just sitting at the login screen, fishing out the real time messages which are intended only for users who are logged in.
Can anyone help me getting the real time updates to work? I'd really appreciate!
As far as the socket messages not being received, the issue is that you're following a tutorial for v0.9.x, but you're using a beta version of Sails in which PubSub has gone through some changes. That's covered in this answer about the "create" events not being received.
Your second issue isn't about sockets at all; you'll just need to reconsider your architecture a bit. If you want to to use socket requests to sign users in, then you'll have to be more careful about redirecting them because, as the message states, you can't render a view over a socket. Technically you could send a bunch of HTML back to the client over a socket, and replace your current page with it, but that's not very good practice. What you can do instead is, in your isAuthenticated policy, check whether the request is happening via sockets (using req.isSocket) and if so, send back a message that the front end can interpret to mean, "you should redirect to the login page now". Something like:
module.exports = function (req, res, next) {
if ([your auth logic here]) {
return next();
}
else {
if (req.isSocket) {
return res.json({status: 403, redirectTo: "/session/new"});
} else {
return res.redirect("/session/new");
}
}
}

Pop-up Scope to contain current tab URL on open

I've been using Crossrider's great API for some time now, however I have reached a great block in my progress.
I have been able to message the popup scope with the current domain through the tab change interrupts and the change of url interrupts. However, when I click the popup browser button, data about the current tab URL is supposed to be shown. Instead, such data is black, and when debugging - it was found that the reason for it being blank is either:
The current URL is being set to the popup extension internal URL
Some sort of bug with the popup logic
I am really stumped, and require such functionality to progress further. My Crossrider ID is 52909.
Thanks!
First and foremost, when posting questions to this forum please include code snippets for the benefit of others so that they can try an assist you. As-is they have no access to the extension code you provided.
Looking at your popup.html code, I can see that you are using the following Crossrider API methods that are not supported in the popup scope: appAPI.resources.includeJS, appAPI.webRequest.onRequest. For more information about API supported in the popup scope, see appAPI.browserAction.setPopup.
As alternatives/workarounds:
For appAPI.resources.includeJS, you can use the jquery to load resources scripts, as follows: $.globalEval(appAPI.resources.get('script.js'));
For appAPI.webRequest.onRequest, implement it in the background scope and use messaging to pass the data to the popup scope, something like:
background.js:
appAPI.ready(fucntion($) {
appAPI.webRequest.onRequest.addListener(function(details, opaqueData) {
appAPI.message.toPopup(details);
}, []);
});
crossriderMain in popup.html:
function crossriderMain($) {
appAPI.message.addListener(function(details) {
// Do something with the details from webRequest
});
}
[Disclosure: I am a Crossrider employee]

Greasemonkey script not executed when unusual content loading is being used

I'm trying to write a Greasemonkey script for Facebook and having some trouble with the funky page/content loading that they do (I don't quite understand this - a lot of the links are actually just changing the GET, but I think they do some kind of server redirect to make the URL look the same to the browser too?). Essentially the only test required is putting a GM_log() on its own in the script. If you click around Facebook, even with facebook.com/* as the pattern, it is often not executed. Is there anything I can do, or is the idea of a "page load" fixed in Greasemonkey, and FB is "tricking" it into not running by using a single URL?
If I try to do some basic content manipulation like this:
GM.log("starting");
var GM_FB=new Object;
GM_FB.birthdays = document.evaluate("//div[#class='UIUpcoming_Item']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = GM_FB.birthdays.snapshotLength - 1; i >= 0; i--) {
if (GM_FB.birthdayRegex.test(GM_FB.birthdays.snapshotItem(i).innerHTML)) {
GM_FB.birthdays.snapshotItem(i).setAttribute('style','font-weight: bold; background: #fffe88');
}
}
The result is that sometimes only a manual page refresh will make it work. Pulling up the Firebug console and forcing the code to run works fine. Note that this isn't due to late loading of certain parts of the DOM: I have adding some code later to wait for the relevant elements and, crucially, the message never gets logged for certain transitions. For example, when I switch from Messages to News Feed and back.
Aren't they using ajax to load content in a div? You can find the element which is being updated by using Firebug for example.
When you click something and the URL changes, but with a # on the URL and after this some text, it means the text is not a path, it's a parameter, the browser won't change the page you are, so since GreaseMonkey inject the script on the page loads it won't inject again, because the page is not reloading.
As in your example the URL facebook.com/#!/sk=messages is not navigating away from facebook.com/ it will not fire window.load event.
So you need to find which element is being changed and add an event listener to that element, you can do is using Firebug as I mentioned before.
After you find out what element is getting the content, you have to add an event listener to that element and not the page (GreaseMonkey adds only on the window load event).
So in you GM script you would have ("air code")
document.getElement('dynamic_div').addEvent('load', /*your script*/);