I have a Grails application with two webflows:
One for Purchasing
Another for getting in Contact with us
The issue:
It can happen that a user has a number of steps taken in a purchase wizard (via a webflow), he/she then decides they have a question and try to contact us (also via a webflow). So the user clicks on the 'contact-us' link on our navigation menu but they get brought back to the last step they were on in the purchase flow. Personally I assume it's got to do with the 'execution' key in the session which every link gets postfixed with i.e.
...../contact_us/index?execution=e1s1
The solution I would like:
I would like (in the above case), if the user decides himself to swap from the 'purchase flow' to the 'contact-us flow' that the 'purchase flow' gets invalidated/removed and the user begins from scratch in the 'contact-us flow'.
Grails version 2.2.3
Webflow plugin: 2.0.8.1
Attempted Solution
The best solution I could come up with (without doing something too ugly) was to create a filter that intercepts when a link has been clicked to start a new flow and attempts to remove the other one. However, in this example it surprisingly fails to find the flow in the flow repository (nullpointer).
FlowExecutionLock flowExecutionLock = null;
SessionBindingConversationManager conversationManager = applicationContext.getBean('conversationManager');
DefaultFlowExecutionRepository flowExecutionRepository = applicationContext.getBean("flowExecutionRepository");
try {
String executionKey = params.get("execution");// executionKey is something like 'e1s1'
FlowExecutionKey flowExecutionKey = flowExecutionRepository.parseFlowExecutionKey(executionKey);
flowExecutionLock = flowExecutionRepository.getLock(flowExecutionKey);
flowExecutionLock.lock();
FlowExecution execution = flowExecutionRepository.getFlowExecution(flowExecutionKey);
flowExecutionRepository.removeFlowExecution(execution);
} catch (FlowExecutionRepositoryException e) {
log.warn("Could not find flow in repository with id ${executionKey} " + e.getMessage());
} catch (NullPointerException e) {
log.warn("Could not find flow in repository with id ${executionKey} " + e.getMessage());
} finally {
if (flowExecutionLock != null) {
flowExecutionLock.unlock();
}
}
Ideas?
Has anybody got any ideas how I could do this. Swapping between two flows (where one is incomplete) in a session should be possible right? I'd like a clean solution to this rather than tweaking the flows themselves.
I seem to have found a workaround, in the g:link tag (where I create the link to the flow) I don't always make the habit of adding the action "index" as in Grails it goes there by default but it seems when I added it, I was able to swap between flows seamlessly.
<g:link absolute="true" controller="contact_us" action="index">
I am not sure if the old flows are removed from the flow repository. I have tried clicking between the two flows 70-80 times and nothing seems to be appearing in the log. If I do get any issues I'll come back and update the answer.
Related
So, I wrote a Nuget that allows dev in my company to quickly setup authorization using OIDC. Part of that, of course, is handing off the authentication step to a browser so I'm simply invoking Browse.OpenAsync with LaunchMode = BrowserLaunchMode.SystemPreferred. I also set up a HTTP listener that awaits the ahtority's authorization code callback. All works find but the browser window is of course still sitting at the top, hiding the initiating app.
I do realise I can use deep linking to simply "call up" the app again but that has two implications I would like to avoid: The need to register custom URI schema with the mobile OS and, above all, the need to register that (custom) callback URI with my company's API management system (which doesn't like anything but "http" or "https" URI schemes). Those requirements makes it more complicated for our devs to get started and, like I mentioned, it doesn't work very well with our API management system, so I'm looking for a simpler solution.
For iOS I can simply fetch the key window's view controller and dismiss it, like so:
async Task<Outcome> IPlatformService.TryCloseTopWindowAsync(bool isModalWindow, bool animated)
{
var window = UIApplication.SharedApplication.KeyWindow;
var viewController = window?.RootViewController;
if (viewController is null)
return Outcome.Fail("Couldn't obtain top window view controller");
try
{
if (isModalWindow)
{
viewController.DismissModalViewController(animated);
return Outcome.Success();
}
await viewController.DismissViewControllerAsync(animated);
return Outcome.Success();
}
catch (Exception ex)
{
return Outcome.Fail(ex);
}
}
Unfortunately, I'm not very good with Android's (Java based) APIs so I'm wondering if it's possible to do something similar here? Can I get the top app (activated by my own app) and dismiss it? If so; how?
When user visits the website first time, popup window has to be shown to user suggesting to register to the newsletter and so on... I imagine that it is done with a cookie detection. What is the proper way of doing that with Grails? We use Spring Security Core plugin, but didn't find how it could help us.
It probably makes sense to use a filter: http://grails.org/doc/latest/guide/theWebLayer.html#filters
If you need to show the popup to all users(not just registered members) then the cookie/session is probably your only solution.
In case that the popup is only displayed to logged in members you might use a filter similar to :
showPopupOnFirstLogin(controller:'*', action:'*') {
before = {
try{
User user = springSecurityService.currentUser
if (user?.mustGetNotification && !request.xhr){
//we ignore ajax requests
redirect(controller:"home", action:"showPopup")
return false
}
}catch (Exception e){
log.error "Failed to redirect", e
}
}
}
I have app with ZK and Spring Security. I logged in application through spring security mechanism. On index page I have some components which are created dynamically. "A" component has click listener;
***point1**
userComponent = new A("link");
userComponent.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
public void onEvent(Event event) throws Exception {
**point2**
//do something with current user
}
});
In point1 I try to get current user with Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); It's work perfect and I receive logged user. But in point2 SecurityContextHolder.getContext().getAuthentication() returns null. How to get current user inside on click event?
Maybe stack traces can help. I see no SecurityFilters in stacktrace to point2
This is stack trace to point1 http://pastebin.com/raw.php?i=k4EhXrAi
Stack trace to point2 http://pastebin.com/raw.php?i=v1mbfbwi
It looks like you have defined an empty filter chain for whatever URL matches the event event handler (I can tell this from the fact that this line is included in your second stack trace). So you are correct - the request is bypassing Spring Security and thus doesn't pick up the current user information. It should be obvious from the debug log since you will see a message along the lines of:
has an empty filter list or has no matching filters
Without seeing your configuration it's hard to say exactly what it is (I don't know anything about zk), but you should easily be able to debug the requests coming from your browser and match them to the debug log output which explains in detail how they are handled.
If you post the configuration and the URL which the Ajax call uses then it should be easier to give an exact answer.
I have question regarding redirection in Grails web-flow.
I am in a view state which will allow users to enter the answers for a question. On 2 wrong attempts I should be able to re-direct the user to view page from different controller. What i mean is
challengeQuestionOne{
onRender() {
//Display question
}
on('next') {BuildQuestion command ->
bindData(flow.recovery,command)
[return the model to flow]
if(command.hasErrors()) {
flow.command = command
return error()
}
if(check for status. If doesnot pass){
flash.message=message(code:'loginForm.account.locked', default: 'Please Contact Admin.')
redirect(controller : "login",action: "login")//how to redirect from here to diff controller
}
if (//compare answer entered) {
}
else{
//set status not active
}
}.to("challengeQuestionTwo")
on(Exception).to("error")
on('cancel').to('finish')
}
I have tried to redirect from onRender . It was redirecting to the page. But how do I display the error msg on the redirected page. How can i forward the error message from one controller to other??
Ivo Houbrechts wrote an excelent tutorial about grails webflow:
Webflow defines its own flash scope. Although it has the same semantics as the standard grails flash scope (the main purpose is to store objects only until after the next request), it is a different scope. This means that objects stored in webflow's flash scope are not visible in standard grails actions.
import org.springframework.web.context.request.RequestContextHolder
....
RequestContextHolder.currentRequestAttributes().flashScope.message = "YourMessage"
You can read more here:
http://livesnippets.cloudfoundry.com/docs/guide/
Flash scope will not work in this case as expected.
Try to use another approach to show error. E.g. you can pass parameters with redirect. Or you can throw some exception and check it on rendered page as follow:
<g:if test="${flowExecutionException}">
<div class="error"><g:message code="loginForm.account.locked"/></div>
</g:if>
We are currently working on the finishing touches of an application which uses Phonegap and have hit some issues with the Blackberry port.
So far, we've been reviewing the content available online and can't find a really finale answer to this. Seems like the "right" way to make and oauth authentication process for either Twitter, Facebook or Foursquare would be to use the ChildBrowser plugin, instantiate a window and then use that to handle the process.
Rightly so, there seems to be a lack of a ChildBrowser plugin for Blackberry. We've been looking so far at a couple of private projects on Github that look like they build/use that capability but we are not sure on how to control the created window.
Most (or all?) of those plugins refer to invoking the native Blackberry browser to handle the URLS, but then how would be manage to work on the callbacks, get the tokens and close the windows since it's another process.
For example, we have this concept code:
function openWindow() {
if (typeof blackberry !== 'undefined') {
app_id = SOMETHING_HERE;
redirect = 'http://www.facebook.com/connect/login_success.html';
url = 'https://graph.facebook.com/oauth/authorizeclient_id='+app_id+'&redirect_uri='+redirect+'&display=touch&scope=publish_stream';
var args = new blackberry.invoke.BrowserArguments(url);
blackberry.invoke.invoke(blackberry.invoke.APP_BROWSER, args);
}
}
Which works for opening the URL, but that's it. Is there a way to get a handle on the window and inject some listener to events? What should be our correct approach?
Thanks!
I am not PhoneGap user, but we did have to handle a very similar scenario - native app invokes the mobile browser to prompt the oAuth flow and then be able to handle a callback to the aative app.
This is possible on the BlackBerry using the BrowserContentProviderRegistry API. You can register your app to be invoked whenever a particular MIME type is returned to the browser. Sounds complicated but its fairly straightforward when all the pieces are in play.
Here is the rough flow -
Native app invokes browser to the oAuth page. This is part is easy and seems like you got this part.
The oAuth redirect needs to go to a URL that you can control. Something like http://mycompany.com/oAuthRedirectHandler.asp.
The oAuthRedirectorHandler.asp has simple code like this (we chose classic ASP but this can be done in PHP or any language, you can also ignore the Android block below) -
<html><body>
<h1>Redirect page</h1>
If you are not re-directed, please open the application manually.
<% strUA = Request.ServerVariables("HTTP_USER_AGENT")
if (InStr(strUA, "BlackBerry")) then
Response.Write("Opening appplication on BlackBerry")
Response.ContentType="application/x-MyCustomApp"
elseif (InStr(strUA, "Android")) then
Response.Write("Opening appplication on Android")
Response.Redirect("MyCustomApp://mycompany.com")
end if %>
</body> </html>
In your BlackBerry code you want a new BrowserContentProvider like this -
final class CustomBrowserProvider extends BrowserContentProvider{
String[] ACCEPT = new String[]{"application/x-MyCustomApp};
String appName;
CustomBrowserProvider(String appName){
this.appName = ApplicationDescriptor.currentApplicationDescriptor().getModuleName();
//cache this appName from the constructor in the invocation code below.
}
public String[] getSupportedMimeTypes() { return ACCEPT;}
public String[] getAccept(RenderingOptions context){return ACCEPT;}
public BrowserContent getBrowserContent( BrowserContentProviderContext context) throws RenderingException {
//this is where the callback happens
//this is happening in a separate process, raise your main app here using the appName that got passed in
//I dont have a sanitized ready to go sample to post here on how to do this, but not too complicated
//as a hint use the ApplicationDescriptor and CodeModuleManager classes
return null;
}
}
Now, in your application initialization, register this new BrowserPlugin like this -
BrowserContentProviderRegistry converterRegistry = BrowserContentProviderRegistry.getInstance();
converterRegistry.register(new CustomBrowserProvider());
Hope this helps. This has worked pretty well for us. The one downside we've had here is that when the user returns to the browser app, they are left with an empty page and there is no good way to close that in the BB.