Strategies for preserving form data ( on tab/browser close ) - forms

I have an issue with a task management application where occasionally users close their browsers/tabs and the information which they type goes away because they accidentally close a browser/tab, resulting in the loss of the text which they've entered ( and some can spend half an hour entering in text ).
So I have to provide a solution, I have a couple ideas but wanted input on the best to go with, or if you have a better solution let me hear ya.
Option 1:
On the window.onunload or possibly window.onbeforeunload event invoke a confirm() dialog and first test whether the task logging area has any text in it and is not blank. If it's not blank, invoke window.confirm() and ask whether the user wants to close the tab/window without saving a log.
My concern with option #1 is that it may be user intrusive.
Option 2:
On the same event, don't invoke any confirm() but instead forcefully save the text in the task logging area in a cookie. Then possibly offer a button that tries to restore any saved task information from the cookie on the same page, so hitting that button would make it parse the cookies and retrieve the information.

The window.onbeforeunload event works a little strangely. If you define a handler for it, the browser will display a generic message about losing data by navigating away from the page, with the string you return from the handler function inserted into the middle of the message. See here:
alt text http://img291.imageshack.us/img291/8724/windowonbeforeunload.png
So what we do: when we know something on the page is unsaved, we set:
window.onbeforeunload = function(){
return "[SOME CUSTOM MESSAGE FROM THE APP]";
}
and once the user saves, and we know we don't need to show them the message, we set:
window.onbeforeunload = null;
It is a little intrusive, but it's better than your users losing data accidentally.

If the user is daft enough to navigate away before submitting what they have been doing, then they shouldn't mind an intrusion to ask if they mean to do something that is apparently stupid.
Also, SO uses a confirmation dialog on navigating away, and most (some) users here are pretty smart.
This is the easiest to use, and will probably help the users more.
If someone writes a long piece of text, then closes the browser without submitting it, they might be more pleased to sort the problem there and then rather than finding out the next morning they didn't do it...

I would research AJAX frameworks for the particular web server/languages you are using. AJAX would allow you to save form data as it is typed (for example, this is how Google Docs works).

Related

In Facebook messenger API, how to prevent button postback payload text from being logged to chat window on click?

TLDR; # bottom
I asked the following question in the Facebook bugs section
NOTE: This is more of a platform design suggestion than a bug, as I failed to find a Chat API feedback portal
Currently I'm building a Chat bot that allows the user to track a goal. It will say something like "Did you go for a walk on July 12, 2016 ?" and have Yes/NO buttons below.
Currently in order to pass the intent, the day and the achievement boolean I need to template a string like this "==GOAL== achieved? <<<{goal_achieved}>>>, date tracked [[[{date_tracked}]]]" and use regex to capture the delimited variables. This is prone to parsing error in other cases where the templated strings in the payload are user-input variables i.e. if the '{goal_achieved}' were replaced with the goal variable '>>meditated" then the regex that captures the templated variable could fail.
One could use the postback payload to store a JSON-encoded string but the problem with this is that the payload string gets logged into the user output and JSON strings are a bit ugly and confusing. The challenges I face could be easily remedied if the payload was not logged to the user Instead log the text for the button to help the user confirm the button was clicked.
If that is not possible, is there any other advice for encoding data into button payload ?
The following answer was offered (Mark Wiltse)
Hi Justin,
Unfortunately at this time our payload structure does not support the functionality that you are trying to implement. From my understanding you want to use the Payload to inform your backend if the user accomplished their 'goal' on that specific date. I would suggest that you create your payload response for the button on your end before passing it to us, which is basically the JSON idea that you had initially.
I know this is a bit cumbersome to handle but the payload response passed back is independent of the text that was provided with the messenger thread.
I would suggest that you also attempt to sanitize your strings if you are worried a user has previously provided you would cause an issue with your regex. You should be able to implement this functionality if the prior user data is sanitized to avoid any issues with regex/json parsing.
Since this is an implementation question I will have to close this report as Invalid. If you are still looking for additional insights and concrete tips for implementing this flow please post to our stack overflow where we have Facebook Engineers and a wide range of community members who also contribute.
http://facebook.stackoverflow.com/
Take care and best wishes with your messenger bot.
Mark
This sentence was particularly unclear:
I know this is a bit cumbersome to handle but the payload response
passed back is independent of the text that was provided with the
messenger thread.
TLDR;
Can anyone inform me of how to prevent the button from logging the payload string so that I can use it to pass JSON to my app without the user seeing it ?
Make sure to comment out sendTextMessage() in your receivedPostback() call :
function receivedPostback(event){
sendTextMessage(senderID, event.postback.payload);
}
From my understanding you're saying that when you press a button the PAYLOAD instead of the button's text is showing up.
Are you defining your buttons like this?
{
type: "postback",
title: "View Details",
payload: "details:12345"
}
I'd recommend removing any special characters that would mess with the parsing of your payload. As long as the special characters are not crucial to the user experience this is probably a fine solution.
If this doesn't solve your issue:
Can you add a screenshot showing the button you are pressing, and the log message you are talking about? From my understanding you're saying that when you press a button the PAYLOAD instead of the button's text is showing up. That's not the case for me, my buttons text shows up when I press a button.

Grails: calling an action that uses withForm

I have a situation in which I need to reuse an action that has its functionality wrapped in a withForm closure.
Everything works well when submitting the form but when I try to reuse that action in another way I get redirect errors from my browser. Specifically, I need to redirect another action to it, possibly call it with chain, and I also want to call it from a hyperlink.
I'd really like to avoid creating a redundant action or having the invalidToken closure execute the same code. I've tried to find some more details about how withForm works and find out what happens if no token is passed to the closure but the Googles have let me down.
Is this possible? Am I trying to make it do something it can't?
More info:
I have a user edit controller action. It is wrapped with the withForm closure. There are three different cases in which I need to call this controller to render the user edit page:
An admin enters the user's id into an input and clicks the form
submit button (this form uses useToken). This needs to be secured
and protected from duplicate form submission.
An admin selects a user to edit from a list of employees by clicking
on the user's name (a hyperlink). Its possible I could turn this into a form submission with useToken and do some CSS styling to make it look like a link.
An admin creates a new user. When the user is successfully created
the create controller redirects (or uses chain) to the edit
controller. I can't find a work around for this, except to create a redundant controller.
If your code is used in more than one place a controller action isn't the best place to put it. I suggest you to move that piece of code to a service and call it from both actions.
Here is my solution. If anyone has some insight into other methods of solving this please contribute. I'm sure I'm not the only one that has had this problem.
The answer is due, in large part to #Sergio's response. It was far more simple than what I was thinking it would be. I created my edit action without withFormthen call it from another action that wraps the edit action in the withForm.
def editWT(Long uid, Long pid){
withForm{
edit(uid, pid)
}
}
def edit(Long uid, Long pid){
// Do lots of stuff to prep the data for rendering the view
}
This answer isn't innovative or ground-breaking but it works. I hope this helps someone else.

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");
}
}
}

Having trouble AFTER form submission with zombie.js

I have the following setup on my site:
You enter credentials on a login page and that takes you to a second page (which normally produces no screen output) which validates the user and redirects them to the appropriate homepage.
My step definitions consist of three steps:
Load up initial login page.
Enter credentials and submit.
Verify (by checking page title) that I made it in the homepage.
My first step passes with flying colors.
My second step claims to pass.
My third step fails.
Upon review, I found that it's because the second step, while officially it didn't fail, didn't do what it was supposed to do. Zombie got stuck on the validation page. At first I thought it was just missing the redirect, but it seems that it doesn't execute ANYTHING on the validation page. I even commented out the entire page and simply put an output of "Hello" at the top of the page. If my browser.html(); can be believed, it doesn't even see that. I know I make it to the second page because I have
console.log("\n" + browser.location.href);
which shows me the URL of the second page.
I then have
console.log(browser.html());
which is empty.
I even have a:
browser.wait(10000,callback);
beforehand to give it some processing time but to no avail.
Some information that might be relevant:
This is a ColdFusion site. I know zombie's handling the concept of CF since it's loading the login page initially, although there's not much actual CF processing going on there.
There's DB access happening. If zombie is accessing like a regular browser, it shouldn't make a difference, but it's there. Although even when I comment everything out, it still doesn't work, so I doubt that's actually relevant.
This is my script portion for the login step. Please advise if I'm approaching this the wrong way.
this.When(/^I input my credentials$/, function(callback) {
browser.fill("login", "myusername").fill("password", "mypassword");
browser.document.forms[0].submit();
// Put in here to account for redirect time it will take to get past validation page to actual home page
browser.wait(10000,callback);
callback();
});
If you need any other information, please let me know. I would appreciate any help whatsoever in being able to make this work!
I am not sure I fully understand your problem, but it looks like this may be an issue with the way you are handling asynchronous calls. At any rate, you should not need to browser.wait for something like this at all. Try something like the following:
this.When(/^I input my credentials$/, function(callback) {
browser
.fill("login", "myusername")
.fill("password", "mypassword")
.pressButton("#selectorForYourButton", function (err) {
// Check for errors or any other behaviour this test is actually about
callback();
});
});
First, the pressButton method is preferable because it gets closer to testing actual browser interaction. But more importantly, callback() is only executed after all the events fired off by pressing the button have been resolved.

What's the best action persistence technique for a Catalyst application?

I'm writing a Catalyst application that's required to have a fairly short session expiration (15 minutes). I'm using the standard Catalyst framework authentication modules, so the user data is stored in the session -- i.e., when your session expires, you get logged out.
Many of the uses of this application will require >15 minutes to complete, so users will frequently submit a form only to find their session state is gone and they're required to log back in.
If this happens I want to preserve the original form submission, and if they log in successfully, continue on and carry out the form submission just as if the session had not expired.
I've got the authentication stuff being handled by an auto() method in the controller -- if you request an action that requires authentication and you're not currently logged in, you get redirected to the login() method, which displays the login form and then processes it once it's submitted. It seems like it should be possible to store the request and any form parameters when the auto method redirects to the login(), and then pull them back out if the login() succeeds -- but I'm not entirely sure of the best way to grab or store this information in a generic/standard/reusable way. (I'm figuring on storing it in the session and then deleting it once it's pulled back out; if that seems like a bad idea, that's something else to address.)
Is there a standard "best practices" or cookbook way to do this?
(One wrinkle: these forms are being submitted via POST.)
I can't help thinking that there's a fundamental flaw in mandating a 15 minute timeout in an app that routinely requires >15 minutes between actions.
Be that as it may, I would look at over-riding the Catalyst::Plugin::Session->delete_session method so that any contents of $c->request->body_parameters are serialised and saved (presumably to the database) for later recovery. You would probably want some rudimentary check of the POST arguments to ensure they're what you're expecting.
Similarly, create_session needs to take responsibility for pulling this data back out of the database and making it available to the original form action.
It does seem like a messy situation, and I'm inclined to repeat my first sentence...
UPDATE:
Whether you use delete_session or auto, the paradoxical issue remains: you can't store this info in the session because the time-out event will destroy the session. You've got to store it somewhere more permanent so it survives the session re-initialization. Catalyst::Plugin::Session itself is using Storable, and you should be able to with something along these lines:
use Storable;
...
sub auto {
...
unless (...) { #ie don't do this if processing the login action
my $formitems = freeze $c->request->body_parameters;
my $freezer = $rs->update_or_create(
{user => $c->user, formitems => $formitems} );
# Don't quote me on the exact syntax, I don't use DBIx::Class
}
...
my $formitems = $c->request->body_parameters
|| thaw $rs->find({$user => $c->user})->formitems
|| {} ;
# use formitems instead of $c->request->body_parameters from here on in
The underlying table probably has (user CHAR(x), formitems TEXT) or similar. Perhaps a timestamp so that nothing too stale gets recovered. You might also want to store the action you were processing, to be sure the retrieved form items belong to the right form. You know the issues for your app better than me.
I would store the form data as some sort of per user data in the model.
Catalyst::Plugin::Session::PerUser is one way of doing that (albeit somewhat hackishly). I would reccomend using the session plugin only for authentication and storing all the state info in the model that stores your user data instead.
And I totally agree with RET's opinion that the 15 minute limit seems really counter productive in this context.
I came across this whilst searching CPAN for something entirely unrelated.
Catalyst::Plugin::Wizard purports to do exactly what you need. The documentation suggests it can redirect to a login page whilst retaining the state of the previous action.
NB: I haven't used it, so can't vouch for its effectiveness.
In the end, we ended up grabbing the pending request (URL+params) in the auto(), serializing and encrypting it, and passing it via a hidden form element on the login page. If we got a login request with the hidden element populated, we decrypted and deserialized it and then redirected appropriately (making sure to pass through the standard "can this user do this thing" code paths).
You could always have some javascript on the client that keeps the session from expiring by making a small request every few minutes.
Or you could have AJAX check for an active session before posting the form and presenting the user with a new login box at that time if needed.