Autosave \ Manual Save with AJAX
General information:

The autosave plugin lets you save automatically the content created with CKEditor, what helps to protect you against losing your work.

The autosaving can be carried out in three ways:

• Change counter - Optional trigger which sends data to server only after specified amount changes was made in the editor.

• Interval - Optional trigger which sends data to server after specified amount of time.

• Toolbar button - sends data to server when user click on a button (This is not quite automatic ). As of version 1.0.2 it is possible to assign keystroke to button.

All communication between plugin and server is made with XML messages.

Plugin fires two events: "afterAutosave" (fired right after saving was finished) and "beforeAutosave" (fired right before AJAX request is made) which can be used to execute user specific functions while saving process takes place.

Important Update:
On the 8th of January 2012 AJAX autosave has been updated to version 1.0.1. It is now possible to use this plugin as simple manual AJAX save by switching off Interval and Change counter triggers. Special thanks to @lsdzung for bringing this up.

Licensed under the terms of any of the following licenses at your choice: GPL, LGPL and MPL.

Installation:

Unpack downloaded zip file and copy autosave folder to /ckeditor/plugins/ or /ckeditor/_source/plugins/ folder (It all depends whether you use ckeditor_source.js or ckeditor.js script on your page). If you are interested only in using the plugin then the first path is what you are looking for.

Next you have to make three things which are in fact providing values for three parameters:

• Register the plugin -
config.extraPlugins = 'autosave';

• Add new toolbar button -
config.toolbar = [['Source','Save','Preview','-', 'Autosave']];

• Specify URL for the data saving server-side script -
config.autosaveTargetUrl = 'http://example.com/path_to_script_that_saves_data';

There are three archives attached to this post. One containing the plugin (minified plus folders for making source version), second containing sample java application showing usage of the plugin and third containing sample PHP connector (Special thanks to Wiktor Walc for providing this one).

Autosave Plugin
Sample Java Application
Sample PHP Connector

As I was saying before the above archives contain two sample connectors, for Java and PHP. If you would like to attach sample connector for other programming language you are welcome.

If you have found a bug, have got an idea for a new feature for this plugin or have a general idea how this plugin could be improved, please leave a comment.
If idea is interesting and possible to implement I will try to include it in future releases.

• Version 1.0.1 - It is now possible to use this plugin as manual AJAX save by simply setting autosaveSensitivity and autosaveRefreshTime configuration properties to 0. Special thanks to @lsdzung for bringing this up.

• Version 1.0.2 - It is now possible to define user specific request parameters with configuration option autosaveRequestParams, assign key shortcut to autosave button with configuration option autosaveKeystroke and execute user specific functions on two events fired by the plugin (afterAutosave and beforeAutosave). Special thanks to @petr82 for bringing this up.
Thank you for great work.
Your solution is what I am looking for. But I develop in ASP.NET and C#.
Can you suggest a sample for ASP.NET and C# to use your plugin.
Can I use your plugin for many CKEditor instances in one page.

Hi here is my ASP.Net implementation based on PHP Connector.

Create the page AutoSaveCallBack_.aspx. Be careful, ValidateRequest must set to "false" (to accept non encoded html in post request).

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AutoSaveCallBack_.aspx.cs" Inherits="webservices_AutoSaveCallBack_" ValidateRequest="false" %>

the code behind :

using System;

/// <summary>
/// My implemention in ASP.Net C# is a translation of PHP Connector.
/// </summary>
public partial class webservices_AutoSaveCallBack_ : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// additionnal parameters like userid via querystring.
//
// In my case, on the "edit" page, i drive CKEditor programmaticly in code-behind via C#. (I use the "dll-wrapper" CKEditor.Net 3.6.4 in my \Bin directory.) :
//
// this.myEditor.ExtraPlugins = "autosave";
// this.myEditor.config.ExtraOptions["autosaveRefreshTime"] = "'30'";

// NOTE IMPORTANT :
// If your "edit" page (ie, the page containing CKEditor) is accessed in "https", the autosaveTargetUrl must have the same
// http scheme, so "https" too.

this.SaveContent();
}

private bool SaveContent()
{
int id_rubrique = 0;
string exceptionMessage = null;
bool result = false;

// Invalid requests
if (String.IsNullOrEmpty(Request.Form["content"]) || String.IsNullOrEmpty(Request.Form["ckeditorname"]) || String.IsNullOrEmpty(Request.Form["autosaveaction"]))
{
this.SendResponse(400, null);
return false;
}

if (!Request.Form["autosaveaction"].Equals("draft"))
{
this.SendResponse(400, null);
return false;
}

if ((String.IsNullOrEmpty(Request.QueryString["id_rubrique"])) || (!int.TryParse(Request.QueryString["id_rubrique"], out id_rubrique)))
{
this.SendResponse(400, null);
return false;
}

if (String.IsNullOrEmpty(Request.QueryString["id_rubrique"]))
{
this.SendResponse(400, null);
return false;
}

// Save datas here...
result = this.SaveContent(Request.Form["content"], out exceptionMessage);

if (result)
{
this.SendResponse(200, null);
return result;
}

this.SendResponse(403, null);
return result;
}

/// <summary>
/// Send the response to the ajax request initiated by CKEditor plugin.
/// </summary>
/// <param name="errorCode">http error code</param>
/// <param name="errorMessage">custom exception message (optionnal)</param>
protected void SendResponse(int errorCode, string errorMessage)
{

if (errorCode == 200)
{
Response.Write("<result status=\"ok\" />");
}
else
{
Response.Write("<error statuscode=\"" + errorCode + "\" ");

if (!String.IsNullOrEmpty(errorMessage))
{
Response.Write("message=\"" + Server.HtmlEncode(errorMessage) + "\" ");
}

Response.Write("/>");
}
}

/// <summary>
/// </summary>
{
string gmtDate = DateTime.Now.ToString("r");

// HTTP/1.1

// HTTP/1.0

// Set the response format.
}

/// <summary>
/// This is the function used to save user-data.
/// </summary>
/// <param name="content">html content provided by CKEditor</param>
/// <param name="exceptionMessage">exception message if you want to have clear errors messages.</param>
/// <returns>return true if operation succeeded.</returns>
protected bool SaveContent(string content, out string exceptionMessage)
{
// Build you own persistence function to store
// CKEditor content in database or filesystem.
bool result = false;
exceptionMessage = "";

try
{
result = true;
}
catch (Exception ex)
{
exceptionMessage = ex.Message;
result = false;
}

return result;
}

/// <summary>
/// Optionnal security function to check if user is allowed to call this page
/// via ajax request by CKEditor Autosave plugin.
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
protected bool IsUserAllowed(string userId)
{
// Build your owncode here to check

return false;
}
}

This pattern is what exactly I needed I've been looking for this kind of information and thanks be to you that you posted it here. I currently develop site using the ASP.net and any more suggestions in order this to working.
@lsdzung - yes you can use the plugin for more than one editor instance. The plugin uses editor name (http://docs.cksource.com/ckeditor_api/s ... .html#name) so that serverside could distuingish from which editor the request was sent.

ASP.NET and C# to use your plugin

As I was saying before I'm not a .NET developer so it will be hard for me to come up with anything.
But this shouldn't be a problem even for a beginner .NET developer . All that this server side connector should do is read request parameters and write the data to file/data base analyze it or do with it what ever you want. General rules are also written inside readme files. You can also have a look at sample PHP connector and Java Servlet - they are also the source of information how can it be done.

@roseholcomb143
I currently develop site using the ASP.net and any more suggestions in order this to working.

I'm not sure if I understand correctly but if you have any problems with your solution please write about them I will try to help.

@roseholcomb143 and @lsdzung - as I understand correctly you are .NET developers. If you are able to come up with a sample connector you are welcome to contribute. It is enough when this connector simply writes something to file or only reads request parameters and does nothing else (it can be used as a template for other developers).
Plugin has been updated to version 1.0.1.

@lsdzung thanks for idea on how to improve this plugin.
Hi Jakub, I was trying your plugin and it works well. What I didn't find is a way how to pass some custom parameters along with the actual editor content. I would need to dynamically pass at least some ID (to update the right content in DB) and a method name (in case ckeditor is used on different places for different purposes). Ideally if I can access the form containing the textarea or any other way. Many thanks!
Hi,

I thought this was being handled by assigning request params to URL. I thought that my plugin handles all the rest but it does handle it only for GET requests (which probably no one uses) and not for POST.

I think I will treat it as bug and will try to fix it this week. I'm also working on other change suggested by @robC user so I'm not yet sure If I will make two releases or just one.

Although I hate to bring another config option I think that this will be the only solution here. Of course I could tell user to simply add his params to URL in form of queryString but in case of POST request it does not seam right even if plugin could done all the queryString stripping in background.

@petr82 what do you think?
Thank you for the response. I have to say that I didn't think of the option of configuring the URL within the actual page and adding URL parameters to it dynamically. So that actually solves the problem for me.

I was testing the plugin a bit more and was wondering if there's a way to intercept when content has been saved, e.g. via an event handler. So i can display a message to the user (in addition to the tick symbol). Something on the lines of :
CKEDITOR.instances.editor_id.on('autosave', ...)
I also haven't found a way to fire the autosave plugin using a shortcut. For standard save I've been able to configure Ctrl+S this way:
config.keystrokes[config.keystrokes.length] = [ CKEDITOR.CTRL + 83, 'save' ];
But if a try to change 'save' for 'autosave' or 'Autosave' it doesn't work. Could you please try if this also doesn't work for you.
I've just found another thing. It seems that the autosave plugin breaks the checkDirty functionality. When using the plugin, the function always returns false even if there are changes. If I remove the plugin from config.extraPlugins then the checkDirty starts working properly again.
Thank you for the response. I have to say that I didn't think of the option of configuring the URL within the actual page and adding URL parameters to it dynamically. So that actually solves the problem for me.

There was no code to handle extra parameters so I think new config option is something that allows for adding new (even generated) parameters without modifying the code.

I was testing the plugin a bit more and was wondering if there's a way to intercept when

No, plugin does not fire it's own event. This is however something to consider. I'm not sure though when to fire such event. The content is being saved in asynchronous way - in callback function... it's to late. The first thing that comes to my mind is just before making AJAX request call.

So i can display a message

But plugin displays it's own messages in form of tooltip. Just hover the cross or tick and you will see. You can also change/add new messages.

I also haven't found a way to fire the autosave plugin using a shortcut.

Plugin does not have such option, yet again this is something to consider.

It seems that the autosave plugin breaks the checkDirty functionality.

It does not break it. It uses it. I guess that this is the main drawback of this plugin - checking and resetting dirty state. As I was saying in previous post I'm working on a way to change it but I'm not sure if this will work.
I'm not sure though when to fire such event. The content is being saved in asynchronous
Well there can be both events, although I think the more useful one is callback - that means the content has been actually saved. Like in Google Docs you have "All changes saved" message.

plugin displays it's own messages in form of tooltip
The tick / cross icons are really nice but a normal user might not understand that. I'd like to show my own messages to ensure the user the content has been saved successfully.

I also haven't found a way to fire the autosave plugin using a shortcut.
I thought that ckeditor itself is handling that. Anyway that would be very useful if shortcuts could trigger autosave. Many users (incl. me) have the habit of using Ctrl+S when writing longer documents:-)

It does not break it. It uses it. I guess that this is the main drawback of this plugin - checking and resetting dirty state.
I see you're resetting in order to count changes. Wouldn't it be enough to count key presses? Surely, you'll miss using mouse events changing the style for example but I think the main proportion of editing a document will still be key presses.

Thanks for your response and also thanks for the work on this plugin, it's one of the most useful ones in CKEditor!
Firing events: I have thought about it a while and come to the same solution. Plugin could fire two events - beforeAutosave (before sending request) and afterAutosave (when saving was done).

Messages: Before you implement your own messages pleas bear in mind that this plugin has it's own "messaging system". Please also have a look at manual as it IMO explains it well.
Just to give a quick summary - when contents were saved - message with time when it happened is displayed.
When error occurs either message from server or plugin predefined one is displayed.
To see the message you have to put mouse cursor over the icon.

Shotcut - Sorry it was quite late when I was reading and writing it:). Yes, this should be handled. I will check if there are any problems with it.

Wouldn't it be enough to count key presses?
- There are other events fired by dialog, non-dialog plugins, by undo/redo, fired in source-mode etc. Plugin registering only some changes and not all of them? Hmmm... I just don't see it, sorry.
Anyway I have started to think of other way to handle saves. Method would be more intelligent and eventually could let me drop changes counter, interval and using dirty mechanism but this part might take some time as this change is quite big and to be honest it's back to the drawing board for me
Anyway I have started to think of other way to handle saves
I guess one way would be to keep a version of the document and comparing the current version to it and detect changes. Although that might be expensive. A good compromise might be to store the document length from last check and check against it like this:

var currentLength = getCurrentLength(); // just a mock method
if ( lastLength != currentLength )
{
autosaveCounter++ ;
lastLength = currentLength;
// change Icon to saveDirty
autosaveChangeIcon( editor, autosaveIcons[1].path,
autosaveIcons[1].title );
}
It's just my guess because i'm not aware of the inner logic of the plugin that much, but do you think it might work or is there some catch to it?
Simple catch. Let's say you have 'abc' in your editor and you change it to 'aac'. Length is the same but contents have changed. Length is just one 'component' that could be used here.
That's true, but that's only when the user selects one character and replace it with another and how often does that happen? Surely there are some other cases but 99% interactions will change the length which might be good enough. You can then even count how many characters were added / removed and fire autosave based on that. Cause with checking checkDirty there's no difference between typing one character and pasting 1000 characters. But anyway, that's just my suggestion.
New version of plugin with some small (but important) changes proposed by @petr82 was released.

It is now possible to define user specific request parameters, assign key shortcut to autosave button and execute user specific functions on two events fired by the plugin.

@petr82 thanks for the tips.
Thanks Jakub. Now the plugin is even better!
j.swiderski wrote:
Wouldn't it be enough to count key presses?
- There are other events fired by dialog, non-dialog plugins, by undo/redo, fired in source-mode etc. Plugin registering only some changes and not all of them? Hmmm... I just don't see it, sorry.
Anyway I have started to think of other way to handle saves. Method would be more intelligent and eventually could let me drop changes counter, interval and using dirty mechanism but this part might take some time as this change is quite big and to be honest it's back to the drawing board for me

Maybe you can use my onchange plugin (as-is or just picking up the relevant parts) to get info about any change. When you perform a save store a copy in memory of the current data, then wait for the onchange event + timers or whatever and if now the getData() is different save again. That wait you don't touch the "is dirty" property and rely just on your code.
I actually have used code of your method. It showed me places I have missed. Now this pluging catches everything but the main problem is that some events, especially 'saveSnapshot', are fired to offten E.g. when you insert an Image 'saveSnapshot' gets fired 5 times. In fact all that is need here is that one time when change was actually made – it is important so that plugin didn’t send unchanged code and to manage “dirty icon” correctly.
That is why I had to add this extra check to see if editor was "really dirty".
For ASP.Net, you can capture the output of this with a simple Generic Handler.

Give it a name of AutoSave.ashx.

Replace all the code with whichever flavor below you like:
<%@ WebHandler Language="VB" Class="AutoSave" %>

Public Class AutoSave
Implements IHttpHandler, IRequiresSessionState

Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
' Do something with the data
IO.File.WriteAllText("d:\path\to\save\content.txt", _
context.Request.Form("Content").ToString)

context.Response.Clear()
context.Response.ContentType = "text/xml;charset=UTF-8"
context.Response.StatusCode = 200
context.Response.Write("<result status=""ok"" />")
context.Response.Flush()
End Sub

Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property

End Class


<%@ WebHandler Language="C#" Class="AutoSave" %>

public class AutoSave : IHttpHandler, IRequiresSessionState
{

public void ProcessRequest(HttpContext context)
{
// Do something with the data
IO.File.WriteAllText("d:\path\to\save\content.txt",
context.Request.Form("Content").ToString);

context.Response.Clear();
context.Response.ContentType = "text/xml;charset=UTF-8";
context.Response.StatusCode = 200;
context.Response.Write("<result status=\"ok\" />");
context.Response.Flush();
}

public bool IsReusable {
get { return false; }
}
}


config.autosaveTargetUrl = '/AutoSave.ashx';

Finally, if you're using .Net 4, you'll have to add the following to your web.config to avoid an HttpRequestValidationException: "A potentially dangerous Request.Form value was detected" because the content is pushed without any encoding:
<system.web>
...
<httpRuntime requestValidationMode="2.0"/>
...
</system.web>


MrG
Hey this plugin looks great.

I'm using Backbone.js so when autosave fires, I only want the Autosave plugin to call my custom callback (not fire off any HTTP requests on its own). Would that be possible without hacking in to your code?

Something like:

\$('#editor').ckeditor(config).ckeditorGet().on('autosave', function(event) {

// Backbone Model .save() method will take care of hitting my API
event.editor.config.model.save({content: event.editor.getData()});

// return true/false so the plugin knows if saving was successful
return true;

});

@chrisbraddock - it sounds to me like you only want to know when something got changed in the editor and then fire your own custom code.

If that's the case then what you really need is the onChange event part from the plugin. You're in luck because such event was created by @alfonsoml. You can find it on this forum and on his blog:
viewtopic.php?f=18&t=23605
http://alfonsoml.blogspot.com/2011/03/o ... ditor.html

Is this something you were looking for or my plugin has something more that you also need?
j.swiderski wrote:@chrisbraddock - it sounds to me like you only want to know when something got changed in the editor and then fire your own custom code.

If that's the case then what you really need is the onChange event part from the plugin. You're in luck because such event was created by @alfonsoml. You can find it on this forum and on his blog:
viewtopic.php?f=18&t=23605
http://alfonsoml.blogspot.com/2011/03/o ... ditor.html

Is this something you were looking for or my plugin has something more that you also need?

Well I like (and could use) other functionality bundled in your plugin. The onChange plugin you mention fires on every keypress I think ... I want less granular notification like your plugin provides.

But I guess I'm going to try a different route today. There's a decent jQuery plugin http://github.com/narfdotpl/jquery-typing I've used before that fires an event when a user stops typing. I'll see if I can integrate that.

Thanks for the response!
You can configure the onChange plugin with something like config.minimumChangeMilliseconds=1000 to fire the change event at most one per second.
And it doesn't take into account just keypresses but it tries to detect any change to the content (formatting, inserting elements, etc...)
alfonsoml wrote:You can configure the onChange plugin with something like config.minimumChangeMilliseconds=1000 to fire the change event at most one per second.
And it doesn't take into account just keypresses but it tries to detect any change to the content (formatting, inserting elements, etc...)

Ooh nice. I hadn't considered the formatting stuff. I'll give the onChange plugin another go. Thanks
This plugin is exactly what I want. Thanks!
I have an odd behavior in Internet Explorer (9.0.8112.16421). When pressing any CKeditor button (e.g. Bold or Italic), the plugin shows the message about abandon the page: "You will lose changes you have made in the editor since last save". Both Firefox and Chrome works without any problem.
To warn if there are unsaved modifications is a great functionality, so I wouldn't want to disable it.

Thanks in advance for any hint!

@ leocofre I have just tried this plugin and didn't get behaviour you were talking about.
Could you perhaps send me private message explaining in more detail how you are getting this problem.
Hello,

I am new to plugin installation.
Sorry for my english google translation. I am French. : s

I put the autosave folder in the ckeditor plugins folder ...

I added the lines in the config.js file of ckeditor:

/*
*/

CKEDITOR.editorConfig = function( config )
{
config.font_names =
'Arial/Arial, Helvetica, sans-serif;' +
'Courier New/Courier New, Courier, monospace;' +
'Times New Roman/Times New Roman, Times, serif;';
config.resize_enabled = false;
config.width = '96%';
config.disableNativeSpellChecker = false;
// Lignes à rajouter :
config.extraPlugins = 'autosave';
config.toolbar = [['Source','Save','Preview','-', 'Autosave']];
config.autosaveTargetUrl = 'http://mywebsite.fr/media/autosave/save/';
// Fin Lignes à rajouter
config.toolbar_Full =
[
{ name: 'document',    items : [ 'Source','-','NewPage','DocProps','Preview','Print','-','Templates' ] },
{ name: 'clipboard',   items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },
{ name: 'editing',     items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] },
'/',
{ name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },
{ name: 'paragraph',   items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl' ] },
{ name: 'tools',       items : [ 'Maximize', 'ShowBlocks' ] },
{ name: 'insert',      items : [ 'Video','syntaxhighlight' ] },
'/',
{ name: 'styles',      items : [ 'Styles','Format','Font','FontSize' ] },
{ name: 'colors',      items : [ 'TextColor','BGColor' ] },
{ name: 'insert',      items : [ 'Image','Table','HorizontalRule','Smiley','SpecialChar','PageBreak' ] },
];

// BASIC EDITOR
config.toolbar_Basic =
[
[ 'Source', '-', 'Bold', 'Italic', 'Underline', '-', 'Image', 'Link', 'Smiley', '-', 'TextColor', 'RemoveFormat', '-', 'Scayt' ]
];
};


But nothing happens ... I think I missed something.
Sorry for my noob attitude

My CMS is Nuked Klan 1.7.9 RC6 :s
http://www.nuked-klan.org/

tanks a lot for your help! :s

Please also note that under this URL config.autosaveTargetUrl = 'http://mywebsite.fr/media/autosave/save/';
you must have server-side code that will take data from editor and return proper response.
This is AJAX plugin - it works with server side code.

Aren't you getting any errors in Firebug console or net tab? I assume you may be getting JS errors or 404 HTTP error. BY installing plugin correctly (not just Java Script, server code is also important) you should be able to work them out.
j.swiderski wrote:@ leocofre I have just tried this plugin and didn't get behaviour you were talking about.
Could you perhaps send me private message explaining in more detail how you are getting this problem.

@j.swiderski Hi, Thanks for your help! I realized what the problem was: I use ckeditor with a plugin for redmine 1.3 *, and this plugin allows to add or remove the autosave button from the toolbar. So the button was not there while the autosave plugin tried to access it. Firefox and Chrome don't bother the problem, but IE complained in that odd way. I modified the t(D,E,F) function to handle this exception.

The solution seems easy, but I can post it if useful. If there is an elegant workaround I'll be pleased to learn about it.

Thanks a lot!

* Redmine CKEditor Plugin: https://github.com/ebrahim/redmine_ckeditor.