So a few days ago I posted a question on the BlogCFC blog about whether I should add "crash protection" to the BlogCFC entry editor. What did I mean by that? As much as I love Firefox, it isn't the most stable beast in the world. That wouldn't matter so much except that I tend to get a bit worried when writing a particularly long blog entry. In the past I've used Word to write long entries since I know Word will automatically save my text even if I forget to. While BlogCFC does support XML-RPC clients, none of them have really caught on with my writing style so I haven't been using them.
Over on cf-talk, Jacob Munson mentioned that Blogger (I think that's some other blogware product - anyone heard of it? ;) will automatically save a draft of your blog entry every few seconds. I decided to take a look at how hard this would be to add to BlogCFC. Turns out it wasn't hard at all. I figured other people may benefit from this as a possible feature they could add to their own products.
So first off - let me talk a bit about the technique. The idea is simple: Every N seconds store the contents of the blog entry to a JavaScript cookie. Add code on the server side to check for this cookie when first displaying the form. Also add code to clean up the cookies when an entry has been stored.
For BlogCFC I decided to be lazy and only store two things - the title and body of the blog entry. Here is the JavaScript code I ended up using:
//used to save your form info (title/body) in case your browser crashes function saveText() { var titleField = $("title"); var bodyField = $("body"); var expire = new Date(); expire.setDate(expire.getDate()+7);//write title to cookie var cookieString = 'savedtitle=' + escape(titleField.value)+'; expires=' + expire.toGMTString() + '; path=/'; document.cookie = cookieString; cookieString = 'savedbody=' + escape(bodyField.value) + '; expires=' + expire.toGMTString() + '; path=/'; document.cookie = cookieString; window.setTimeout('saveText()',5000); }
window.setTimeout('saveText()',5000);
The first thing I want to point out are these two lines:
var titleField = $("title"); var bodyField = $("body");
These are Spry (copied from Prototype) shorthands for:
var titleField = document.getElementById("title"); var bodyField = document.getElementById("body");
Outside of that - everything else is vanilla JavaScript. JavaScript lets you add cookies by simply setting the document.cookie value. Also note that document.cookie isn't a simple string. If you run document.cookie = something multiple times, you end up with multiple cookies.
The format for a cookie string is name=value; expires=DATE; path=PATH. In my case I simply used a cookie that expires in 7 days and a path of /.
Lastly I have a window.setTimeout, both outside of the function and inside, that will run this code every 5 seconds. Any duration is fine really.
To restore the values, I used this set of code on the server side:
<cfif not structKeyExists(form, "title") and structKeyExists(cookie, "savedtitle")> <cfset form.title = cookie.savedtitle> </cfif> <cfif not structKeyExists(form, "body") and structKeyExists(cookie, "savedbody")> <cfset form.body = cookie.savedbody> </cfif>
Basically I simply ensure that I'm not already posting and see if I have anything in the cookie. I clear out the cookies using this code:
<cfcookie name="savedtitle" value="" expires="now"> <cfcookie name="savedbody" value="" expires="now">
As you can see, this is a pretty trivial implementation. Anyone else using something like this in their applications?
Archived Comments
I've got something like this at the job, but instead of cookies, I copy the form data to a hidden copy of the form and do a XHR post to the final page with a "draft" parameter. A manual post doesn't send the draft param, so that version becomes the final version.
We had problems with cookie length and people screwing with the cookie settings on their browser, so when I implemented the feature, cookies weren't even an option.
As an FYI, I tested Word 2007's XML-RPC with BlogCFC and it works great.
@Ray:
Just remember that there's a 4k cookie limit per domain (the total size of the document.cookie can not be more than 4k.) So, your techinique is going to cause problems for large blog entries.
I'd actually recommend just posting the data back to the server (either via AJAX or an IFRAME) and then have the server store the data. You could do this in the db or just use a persistant variable.
As soon as user actually saves the entry, delete the auto-save copy from the server.
You are right on the limit - but I don't know. I think 4k would hold a LOT of text. As it stands - I was considering a Spry version as well. :)
For a brief moment, when I scanned the page and saw, $("title") I had a vision of Ray using Prototype. But instead it was simply Spry's use of the Prototype shorthand.
The universe is back in order! ;)
@Ray:
Just remember that any embedded HTML in the blog entry will quickly add to the total size of the cookie. Also, remember that the 4K limit isn't per cookie, it's for all the cookies on the domain.
I'm not sure how much markup you have in this blog entry, but the text alone is about 3.7K. While I'm sure most posts are well under 4K, any extensive blog entry w/lots of source code or examples is probably going to break the 4K limit.
It's really not that much work to simply post the data back to the server using AJAX or an IFRAME. It's actually really easy w/an IFRAME. Just add an IFRAME and setTimeout() to your page to automatically submit the form to the IFRAME every XX seconds or so.
Good point. As I'm using Spry, I will just do that. As it stands, I needed to switch to Spry tabs anyway.
Ray,
If you're gonna make it with Spry, I'd like it if you put an option in the admin to set the time limit, and possible to turn it off. The management at my job are a bunch of Nazis when it comes to Internet usage, and I'd prefer to just turn that feature off if I'm at work (every http hit adds 3 min to my 'bill'). :)
It's probably obvious, but when I said 'time limit' in my last comment, I mean save frequency.
Jacob - I most likely will not do that as I find that I have a lot of options already and I'm worried about overwhelming folks. But - you at least know about it and know where to comment it out. ;)
Ray, will you post your Spry solution here when it's done? I'd like to do more with Spry, and one of the basic things I haven't seen many examples on (not that I've looked very hard) is XHR calls (non-dataset stuff.)
I will - but it may be a little while - getting swamped. I did find out how to POST with Spry (thanks to another user), so that was my main stumbling block.
It's over a year since this blog was posted. One technique to achieve the same thing (auto save) is using AJAX as mentioned, Sam Farmer has implemented it, and I've learned this technique from him as well, it seems to be quite elegant.