Integrate FCKEditor with your Ruby on Rails application
By Joshua M Charles ( josh.charles AT gmail DOT com)
Last Modified 11/21/2005 - THIS IS A WORK IN PROGRESS
This setup does not work properly if you are submitting your form through an ajax call. I am working on getting a fix for this, however... UPDATE: AJAX is now working with the directions provided below. It‘s complicated, however.
Table of Contents
- Basic Setup
- Advanced Work
- Processing Text
- Limitations
- Working with AJAX and FCKEditor
- Plans for the future.
Basic SetupFCKEditor is an open source Javascript application for embedding a rich text box into an HTML form. I will show you have to integrate this application with your Ruby on Rails application.
The first step is to head over to http://www.fckeditor.net and download the latest version of the editor. Once it is downloaded, unzip the contents into a temp file. Copy the following files and folders into your /public/javascripts folder in your rails application:
/editor/
fckconfig.js
fckeditor.js
fckstyles.xml
fcktemplates.xml
When you have them copied over, your javascripts directory should look like this:
Now you need to edit some of the default configuration settings. Open up ‘fckeditor.js‘ in your favorite text editor and set the default path to “/javascripts/”:
Once that is complete, your are ready to insert the editor into your view.
Open up the view you want the editor on, and add the reference to the base javascript file with this line:
<%= javascript_include_tag "fckeditor" %>
That line should fall before the </head> html tag.
The editor works by replacing a text area that already exists, so we need to go ahead and create one:
<textarea id="MyTextarea" name="MyTextarea">This is <b>the</b> initial value.</textarea>
The final thing we need to do is write the javascript to replace the text area:
<script type="text/javascript">
window.onload = function()
{
var oFCKeditor = new FCKeditor( ‘MyTextarea‘ ) ;
oFCKeditor.ReplaceTextarea() ;
}
</script>
Once that is complete, we can fire up the page and view the new editor:
The entire rhtml file follows:
<html>
<head>
<title>test</title>
<%= javascript_include_tag "fckeditor" %>
<script type="text/javascript">
window.onload = function()
{
var oFCKeditor = new FCKeditor( ‘MyTextarea‘ ) ;
oFCKeditor.ReplaceTextarea() ;
}
</script>
</head>
<body>
<form action="/default/save" method="POST">
<textarea id="MyTextarea" name="MyTextarea">This is <b>the</b> initial value.</textarea>
<input type="submit" value="Update">
</form>
</body>
</html>
Treat the editor like any other form field, and everything will just work.
You can edit the FCKEditor settings just like you would normally - most of them are contained in the ‘fckconfig.js‘ file. Some of the things you might want to change are the default toolbars, height and width of the editor, and available styles.
Advanced Work
We can create an application helper to make it easy to use the current setup. In the end, I want to be able to just to this:
<%= fckeditor_text_field %>
Start by opening up your application_helper.rb. You need to add the following method to this file:
def fckeditor_text_field(object, method, options = {})
text_area(object, method, options ) +
javascript_tag( "var oFCKeditor = new FCKeditor(‘" + object + "_" + method + "‘);oFCKeditor.ReplaceTextarea()" )
end
FCKEditor has since changed the way text areas are replaced in Mozilla based browsers. It now needs to read:
javascript_tag( "var oFCKeditor = new FCKeditor(‘" + object + "[" + method + "]‘);oFCKeditor.ReplaceTextarea()" )
This tells Rails to create a regular <textarea> field, and then sends the javascript to replace that textarea field with the fckeditor. The reason I did it like this is that it creates the possibility to use this in an AJAX call, with the added benefit of not interfering with any other "body.onload" javascript functions you have in place. This method works in both Firefox and Internet Explorer, though I‘ve not tested it in other browsers. See Limitations for possible problems you could face when working with AJAX.
UPDATE: There has been a change in the fckeditor code, so that helper may not work with the latest version of rails. I‘m still investigating this problem, but it seems to rotate around the fact that fckeditor replaces test areas based on the name attribute instead of the id attribute now.
With that method in place you can use the following example inside your html:
<%= fckeditor_text_field( "ObjectName", "MethodName" ) %>
If you run into problems, make sure you are still including the fckeditor.js file at the top of your page. All you should need for this helper to work is a browser supported by FCKEditor, and the javascript file included at the top of the page you want the editor on.
Processing Text
Let us suppose you have the following simple form:
<%= start_form_tag :action => ‘save‘ %>
<%= fckeditor_text_field("Page", "content") %>
<%= submit_tag "Save" %>
<%= end_form_tag %>
<%= link_to ‘Back‘, :action => ‘index‘ %>
This example might be part of a simple content management system that allows users to edit the content of a page. In order to access this content on the other side, treat it as you would any other form field in Rails:
def save
page = Page.new(params[:Page])
page.save
end
newRole.content holds the html that was submitted. If storing this in a database, you need to follow the usual guidelines: escape the characters that need escaping. Decide whether or not to remove <script> tags. Remove any server side script tags. Etc. Basically the things that you ought to be doing anyway ;)
Limitations
The biggest problem you are likely to run into is the fact that you can only have one instance of FCKEditor with the same name on the same page. What does this mean? Under certain circumstances the editor will not load, even though is seems like it should. Here are some examples:
- The page has been refreshed. When I‘ve worked with this on my local machine, the editor refused to load after the first refresh. The fix is to manually reset the window location, through either a server side redirect or a javascript function.
UPDATE: With the AJAX modifications below, this is no longer a problem - Multiple AJAX calls without a page reload. It‘s basically the same problem as above. You have a piece of code that is calling a serverside function that returns an FCKEditor. The Object and Method that the editor is tied to is the same through all calls. Because the textarea is going to have the same ID‘s, the editor will not load itself into subsequent textareas.
UPDATE: With the AJAX modifications below, this is no longer a problem
Currently, the server browser and the file upload capabilities are not working as well. Work is ongoing to get these features working.
Working with AJAX and FCKEditor
After much testing, I‘ve discovered a method of dynamically loading and unloading FCKEditor. At the moment, it‘s rather complicated, but as time goes on, I expect that I‘ll be able to simplify things. For now, I recommend caution. The following steps are required:
- Modify the fckeditor_text_field helper so that it is the following:
def
fckeditor_text_area(object, method, value, options = {})
javascript_tag( "FCKeditorAPI = null; " ) + "\n" +
javascript_tag( "__FCKeditorNS = null; " ) + "\n" +
"<textarea id=\"" + object + "_" + method + "\" name=\"" + object + "[" + method + "]\">" + value + "</textarea>" +
javascript_tag( "var oFCKeditor = new FCKeditor(‘" + object + "[" + method + "]‘);oFCKeditor.ReplaceTextarea()" )
end
Note that I added a value parameter to this function so that the initial text may be set.
- Modify your Model that will be used to store the data from fckeditor to have a dumby tmp field. The reason for this is that for some reason, FCKEditor does not work the first time with Prototype‘s Form.serialize method. We will be fixing this by creating a hidden text field that copies the fckeditor data into it before we submit the form. Here is my model:
class Page < ActiveRecord::Base
attr_accessor :tmp
def before_create
@tmp = ""
end
def tmp ( any )
""
end
end
- On the page you want to load the editor from, setup the AJAX Transaction:
<%= link_to_remote("Load Editor", :update => ‘results‘, :url => { :action => :fckedit, :id => @page.id } ) %>
If desired, the id can be a pointer to the initial data.
- The view that contains the editor has the option to load an initial value into it. Here is the method I use inside the controller:
def fckedit
pid = params[:id]
@page = Page.find( pid )
end
Along with that, here is the view that I use: <%= form_remote_tag( :update => "results",
:url => { :action => :SavePage, :id => @page },
:html => { :id => ‘pageForm‘ } ) %>
<%= hidden_field( "Page", "name", :value => @page.name ) %>
<%= hidden_field( "Page", "description", :value => @page.description ) %>
<%= fckeditor_text_area( "Page", "tmp", @page.content ) %>
<%= text_area( "Page", "content", :style => ‘display:none‘ ) %>
<input name="commit" type="submit" value="Save" onClick="var oEditor = FCKeditorAPI.GetInstance(‘Page[tmp]‘);document.getElementById( ‘Page_content‘ ).value = oEditor.GetHTML();" />
<%= end_form_tag %>
The method is pretty straight forward, but the view is not. First, my Page Model has the obvious attributes: name, id, description, and content in the database. Also, we are using the tmp attribute that we added on to the model later. There are a couple of things to note here. The first is that the Page.content field contains a style tag to not display the field. This is import because otherwise, you‘ll end up confusing your users. Secondly is the onClick() method in our submit button. This javascript is what takes the value of the FCKEditor an copies it into our Page.content attribute. Without this, the data will not be accessable on the server side.
- To access your data on the server side has changed minimally. Here is my code:
def SavePage
page = Page.find(params[:id])
newpage = Page.new( params[:Page] )
page.update_attribute( :content, newpage.content )
page.save
end
I had to use a couple workarounds in this when the obvious method was not working. Hopefully I‘ll figure out what the exact issue is and fix it.
And there you have it. A basic (albeit complicated) method of dynamically loading an FCKEditor instance and submitting it over AJAX. I may be refactoring this to make this a little nicer, and be including a few more helper functions, but for now, this works.
Future Plans My goals in keeping this project going are as follow:
- Setup a nice Ruby API for the Filebrowser and File Upload capabilities. I have this working for a project I‘m doing, but the code is very specific to the project. I want to be able to generalize it for others to use.
- Write the ruby code to connect the fckeditor to GNU Aspell for serverside spell checking. This ought to be pretty easy; just a translation of the php code that already exists for this.
- Clean up the code to make integration easier.
- Test, Test, Test!
I suspect by the time I‘m done, I‘m going to have modified the original codebase of FCKEditor quite a bit. I‘m going to be taking out all the PHP, ASP, and other language support to create a smaller package, as well as remove the javascript code that is not needed. Therefore, what I‘ll probably end up with is a distribution of FCKEditor specific to Ruby on Rails.
This brings up a couple of issues. First is Deployment. An installation of the integration between these two technologies will include javascript files, controllers, possibly modifications to existing models, stylesheets, new helper functions, and perhaps even some new generators. I‘m not sure what the best way to go about this is, so I‘m willing to listen to suggestions. I‘m not sure if the new plugin system in Rails 1.0 will be able to handle all of this.
If you have any questions, feel free to contact me at josh.charles AT gmail DOT com