We started using FCKEditor on the iTOK intranet as the front end in to the document management subsystem. It is a fantastic open source project which has provided amazing capabilities for the iTOKNet system.
If you visit my blog often you will notice that I have pulled the articles about configuring an asp.Net connector for the default filebrowser. This is because the FCKEditor connector architecture is out and out the wrong tool for the job when it comes to providing a secure filesystem. The code we had working with 2.0FC broke with the 2.0 release and tracking down the reason for events not being called was not only pointless but highlighted the added layer of (unecessary) complexity which the generic filebrowser added.
This conclusion in no way demerits the power of FCKEditor – like I said before – the filebroswer is the wrong tool for the job in providing a proxy filesystem. FCKEditor is still the technology I am going to present in this article; in fact, to FCKEdit’s credit, it took an hour to write a custom filebrowser in .Net which plugged straight in to the existing FCKEditor without any fuss! One change to the config file and we were up and running.
If you DON’T read this blog often you may wonder what I am talking about. Fair call – this article is about integrating the FCKEditor in to a .Net web application which uses a proxy (virtual) filesystem. I will explain what a proxy fileysystem is later, but first lets take a look at what FCKEditor is;
“This HTML text editor brings to the web many of the powerful functionalities of desktop editors like MS Word. It’s lightweight and doesn’t require any kind of installation on the client computer.”
An open source HTML editor which is cross browser with features like Cut, Paste, Undo and Redo, Paste from Word(!), Image insertion, with upload and server browsing support, Skins support, Plugins support and a Spell checker! Quite impressive so you begin to see why we chose it.
If you have an editor which also provides hooks in to a document management subsystem and virtual filesystem then you have a very simple to use interface for editing content and managing attachments in a secure environment. This is what we were trying to implement for iTOK.
The document management system we use at iTOK is a fulltext searchable content library which has a complete role based permissions model associated with it. This has introduced two complex issues when integrating this (or any) third party editor technology which can manage files as well as edit text/HTML content.
Firstly, files themselves are an issue. We do not want to directly expose a filesystem to users. The approach which we have implemented is to create an FCKEditor file browser to use our virtual filesystem. The user is given a workspace during the editing process and the files are consumed in to the repository once editing is complete (or as it occurrs).
Secondly, links back in to the repository when viewing documents (without the editor) have to be done via view-only proxy (aspx page) which again is enforcing role based permissions inherited from the document being viewed before providing access to the files in the repository.
You see then that we have a few objectives in this article:
1. Illustrate a simple proxy filesystem
2. Configure FCKEdit for a .Net project
3. Create a custom filebrowser for FCKEdit
Project Primer
The project file attached to this article contains the complete 2.0 distro of FCKEditor as well as the .Net FCKEditor library. For the sake of simplicity in managing the database-driven aspect of this demonstration I am using the BizBlox API to perform the object-relational mapping so none of this example requires any SQL or database code. I have included 0.9.9.1 of the BizBlox ORM installer in the project too. To get the database “encrypted connection string” property you find in the web.config simply go to the Tools menu of the SQL import tool and choose ‘create encrypted connection string’ – just enter your connection string for the demo database and you’re off! For more information on how to format a connection string you can find info in the BizBlox knowledge base article here.
Part One: Proxy Filesystem
The process of actually DOWNLOADING a binary file from an area which IIS does not have permissions is the key to the proxy concept. We are using an ASP.Net page as the proxy which can perform intermediate permissions checking – or just use embedded .Net permissions – the access to the filesystem. The ASPX page gets a reference to the file (via the virtual filesystem key) and opens a stream to the requesting web browser and serves up the appropriate file. The ASPX page acts as a proxy between the web browser and the filesystem via the application server layer.
Why would you use a proxy instead of a custom HTTP handler for binary filetypes? Get some background by reading this great introduction to custom HTTPHandlers. The benefits of using a proxy are that files in other locations (outside of the bounds of our CMS system) are not filtered by the HTTPHandler in the same web appliction, so their performance is unhindered by this system. It also requires a tonne less configuration than having to setup custom extensions for the handlers in IIS. Of course with the next generation of IIS this will be less of an issue.
Now, lets look at our proxy example in the demofilesystem project:
The OnLoad event of the ViewVirtualFile.aspx page looks like this:
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim id As Guid Try id = New Guid(Request("id")) Dim vFactory As New VirtualFileFactory Dim vf As VirtualFile = vFactory.Retrieve(id) Dim d As Downloader = vf.GetDownloader() d.Download() Catch ex As Exception Response.End() End TryEnd Sub
The actual process of downloading a file is performed by – you guessed it – the Downloader class, the VirtualFile and VirtualFileFactory are BizBlox inherited classes which provide us with a VirtualFile record from the database. The GetDownloader() method is mapping virtual file to real filesystem entry and setting up an instance of the Downloader with that information.
The guts of the Downloader class is simple – we just reset the HTTPResponse going back to the browser client and set the MIME type based on the filename suffix (see the MimeMapper class) and use TransmitFile to send it back. Note, we are not intending on dealing with very large files in this example so see the sidebar below for the impact of large files on a system like this.
Public Sub Download() Dim r As HttpResponse = HttpContext.Current.Response r.Clear() r.ContentType = Me._mimeType r.AddHeader("Content-Disposition", _ "inline; filename=" & Me._targetFilename) r.TransmitFile(Me._sourceFilename) r.End()End Sub
Note that by setting the Content-Disposition header, we are determining how the browser behaves when it receives the file. We are indicating what the filename should be and that the file should be added inline with other content. This directive affords the browser flexibility to determine if content should or should not be in the web page. A filetype which cannot be natively rendered inline will still display the download dialog box or attempt to load the helper application it is assigned to on the client browser.
[SIDEBAR: Large Files] Many issues can come in to play when uploading or downloading large files and you must take this in to account. There are solutions for handling large files such as ABCUpload. And several articles have been written regarding how to manage memory efficiently when streaming binary files to the client. Worthwhile reading – but outside the scope of this article.
Thats it! Well, actually, not quite – the magic of proxy happens when the source file is outside of the IIS directory. At that point, you can physically only access this file through the proxy page. There is no way (permissions or not) that IIS could show a file which is outside its web-roots all by itself.
What about uploads I hear you cry! Well, you have to hang in there for the final installment to get that juicy part! In the mean time, lets figure out how to set up FCKEditor for .Net.