Code Syntax Highlight Plugin for FCKeditor

Introduction

This is a dialog-based plugin to handle formatting of source code for FCKeditor 2.5.x. It WON'T work with the new CKEditor (yet). It makes use of the SyntaxHighlighter 2.0.x javascript library available to download from Alex Gorbatchev's project page (the older version 1.5.1 version is available from Google Code).

The plugin primiarily edits a <pre> tag with some custom attributes. Its mainly aimed at users editing blogs or content management systems where there is a requirement to format programming languages on a website that is being edited using FCKEditor.

The plugin will not format the code in FCKEditor - the SyntaxHighlighter javascript library dynamically generates a lot of formatted HTML at runtime, which would cause problems in FCKEditor.

Skip straight to the good bits

Can't be bothered reading all this? View the online demo or download the plugin and go play with it yourself.

So what do I get then?

Correctly installed, the plugin is in the form of a tabbed dialogue box that looks like this:

Syntax Highlighter dialogue

Version history:

Huge thanks goes to Sergey Gurevich who wrote the updated code for the FCKEditor plugin to handle the latest version of the SyntaxHighlighter code and submitted useful bug fixes.

  • v2.1.0 [23 May 2009]
    - Plugin version information now being displayed
    - Line highlighting feature added
    Download | Demo
     
  • v2.0.1 [22 March 2009]
    Minor bug fix where semi-colons were sometimes positioned in the wrong place when no advanced options were selected
    Download
     
  • v2.0 [2 March 2009]
    Latest version supporting SyntaxHighlighter 2.0.x
     
  • v1.0.2 [2 March 2009]
    Bug fixes, final release supporting the older SyntaxHighlighter 1.5.1
    Note the instructions below are for the newer version of the library and makes references to syntaxhighlight2 a lot, this version uses syntaxhighlight. The documentation in the download will be more accurate.
    Download.| Demo
     
  • v1.0.1 [10 Feb 2009].
     
  • v1.0 [30 Nov 2008]. First version.

Known bugs:

Occasionally the dialogue box does not pickup the <pre> element to be edited in Firefox. It only seems to happen when a user clicks inside the <pre> tag with the mouse but doesn't actually move or interact with the cursor.
Fixed in version 1.0.2 - Thanks to Sergey Gurevich

Installation

1. Copying the files

Extract the contents of the zip in your plugins directory, so it ends up like this:
Folder structure

Note: Version 2 of the plugin must be extracted to a directory named 'syntaxhighlighter2'. The older plugin for the earlier version of the library must go in a folder named 'syntaxhighlighter'. The file fckplugin.js references this directory when the plugin initialises.

2. Adding it to FCKeditor

Now add in your fckconfig.js or custom js configuration file the following line (remember its javascript we are dealing with so everything is case sensitive!): 

FCKConfig.Plugins.Add( 'syntaxhighlight2', 'en') ;

3. Adding it to the toolbarset

Add the button 'SyntaxHighLight2' button to your toolbarset: 

FCKConfig.ToolbarSets["Basic"] = [
['Bold','Italic','-','OrderedList','UnorderedList','-','Link','Unlink', '-','SyntaxHighLight2','-','About']
] ;

4. Configure the plugin

The plugin will work 'out of the box', but you can configure a default language using the SyntaxHighlight2LangDefault parameter:

FCKConfig.SyntaxHighlight2LangDefault = 'csharp' ;

The full list of languages and corresponding codes are:

    • c++ - C++
    • csharp - C#
    • css - CSS
    • delphi - Delphi
    • java - Java
    • jscript - Java Script
    • php - PHP
    • python - Python
    • ruby - Ruby
    • sql - SQL
    • vb - VB.NET
    • xhtml - XML/HTML

In FCKEditor <pre> blocks aren't really formatted by default. I suggest editing your fck_editorarea.css (or equivalant if you are using a custom CSS file) to something that highlights code blocks better. eg:

pre
{
    background-color: #fff;
    font-family: "Consolas" , "Courier New" ,Courier,mono,serif;
    font-size: 12px;
    color: blue;
    padding: 5px;
    border: 1px dashed blue;
}

Configuring SyntaxHighlighter.

You must have SyntaxHighlighter installed and working to display properly formatted code. FCKEditor does not need it, but for code to be properly formatted on your website you must have it configurated correctly. For SyntaxHighlighter2 Alex Gorbatchev's site has loads of excellent information (he wrote it after all!), for the old version 1.5.1 library, the project wiki here is a good resouce, or this blog post might also be useful.

5. Use it

Now clear your browser cache (this stage is important!) and reload the editor, the new button Insert code snippet button should be ready to use.

6. Future updates

There's some additional functionality I might add at a later date:

  • Preview tab using the SyntaxHighlighter library
  • More configuration options, eg, available languages, path to SyntaxHighlighter files
  • Dynamically adding a <pre> formatting style to the editor by default

7. And finally...

Thanks goes to Alex Gorbatchev for creating Syntax Highlighter!.

Currently rated 4.7 by 10 people

  • Currently 4.7/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Using FCKEditor & CKFinder to create a CMS in .NET

This article aims to demonstrate how to effectively use FCKEditor and CKFinder to create content management functionality using C# and SQL Server.

FCKEditor is a long standing open source project to develop a lightweight but powerful cross-platform in-line HTML editor. It supports most major browsers. You can find out more about FCKEditor here. Combined with the ajax file manager CKFinder its a killer combination.

Index

  1. Running the application
  2. Architecture
  3. The FrameworxLite namespace
  4. Embedding FCKEditor in .NET applications
  5. Editing Content in .NET
  6. Content Delivery
  7. Summary & source code

What this article contains.

This isn't supposed to be an example of cutting edge C# coding. Its primarily about FCKEditor and how it can be used with .NET. The administration area for the CMS has no security, its been removed from the original application. People have many different ways of implementing user logins, I'll leave it up to you.

The End Result.

The final application is a fully functioning website and CMS called FrameworxLite, with a database-driven backend displaying content editing and formatted using FCKEditor, with images being added using CKfinder. FrameworxLite is a cut down version of a much larger CMS developed in-house by my company, Frame Digital which i'm a joint partner in.

You'll need IIS and SQLServer to run it. The entire application is released under the GNU General Public License. In short, it's free and you can do with it as you want, but I can't support it.

There is a functioning demo running at frameworxlite.psykoptic.com. You can download the source code for this article here (1.2Mb).

Running the application [skip]

The site is coded in C#, using the 3.5 version of the .NET frame work. It won't work for v2.0. You'll need IIS and SQL Server 2005 or later. The site is setup so that it must run from the domain root, eg

http://localhost/ - will work
http://localhost/myapp/ - won't work

IIS is available in XP Pro and most versions of Vista. If you don't have SQL Server, you can download the Express Version here. To get the best out of this article, you'll also need to have Visual Studio 2008 installed. If you don't have it, you can download Visual Studio Express from Microsoft.

You'll also need to create the FrameworxLite database. You can either run the SQL script in the \database\sql\ directory in SQL Server or use the database installer in \database\db-installer\

Once you have the site configured in IIS and the database setup you'll need to modify the web.config file for your database settings:

<connectionStrings>
<add name="AppConnString" connectionString="Server=.\SQLEXPRESS;UID=[userid];  PWD=[password];Database=FrameworxLite;" providerName="System.Data.SqlClient"  />
</connectionStrings>

Once everything is is up and running you should see the demo site homepage.

FrameworxLite demo site

The architecture bit [skip]

FrameworxLite has a public UI and an administration area available from http://[domain]/admin

The application is an example of a multi-tier entity/service architecture. The UI componets talk to the entities, the entities to the services, the services to the data access components. An entity class contains the basic data construct and the Service Layer handles the business logic translation between the entities and data access layer (a class named DataObject). This diagram outlines the seperate tiers interacting during a load/page postback life cycle.

The CMS application is structured like this:

CMS Architecture
CMS Architecture


Each module is independent of one another and uses FCKEditor and CKFinder differently.

  • Pages
    Core Page content management functionality. FCKEditor is used to format page content
  • News
    News module. FCKEditor is used for HTML formatting and CKFinder is used to select summary images
  • File Manager
    Main install of CKfinder for managing files and images.

The FrameworxLite namespace

The main structure is as follows:

/BaseClass/

  • Frameworx.cs. core base class for CMS modules
  • PageBase.cs base class for website pages

/Helper/

This directory contains a number of helper classes with varying functionality:

  • DataObject.cs this is the main database access layer
  • DynamicBuilder.cs translates database objects to Entity objects
  • FCK.cs used for embedded FCKeditor in CMS modules
  • Tools.cs various small helper functions

/Interface/

Interface classes.

  • ICMS.cs - Interface used for CMS
  • IMaster.cs interface used for Masterpages (if required)

/Entity/

Contains all the entity classes for the project. Business entities are used to pass data between component and services. In this case our entities represent the news and page data.

/Service/

Contains all the service classes used for the project. The service layer is used to provide translator components that translate entities between the UI and raw data.

Embedding FCKEditor in .NET applications [skip]

FrameworxLite tries to make adding new instances of FCKEditor as easy as possible. A quick outline:

  • The assembly FredCK.FCKeditorV2.dll must be added to the /bin/ directory
  • The web.config file at /admin/web.config contains settings using by the application and FCKEditor:
    • FCKBasePath - the path to the FCKEditor install
    • The FredCK.FCKeditorV2 assembly is registered at <system.web>..<pages>..<controls> This is so we don't have to register it on a per-page basis
  • The Helper.FCK class is used to instantiate an instance of FCKeditor
  • FrameworxLite uses a custom default ToolbarSet, the ability to create forms etc has been removed.

This is the full FCK.Setup method:

    /// <summary>
    /// Applies settings to the supplied FCK instance
    /// </summary>
    /// <param name="instance">The instance.</param>
    /// <param name="toolbarset">ToolbarSet defined in the FCKconfig.js file</param>
    /// <param name="height">Height in pixels</param>
    public static void Setup(FCKeditor instance, string toolbarset, string height)
    {
        if (instance == null)
        {
            return;
        }

        if (toolbarset.Length == 0)
        {
            toolbarset = "Default";
        }

        if (height.Length == 0)
        {
            height = "400";
        }
        string basePath = ConfigurationManager.AppSettings["FCKBasePath"];
        instance.BasePath = basePath;
        instance.SkinPath = basePath + "editor/skins/default/";
        instance.Height = Unit.Parse(height);
        instance.ToolbarSet = toolbarset;

    }

Using a method means we can consistently embed FCKEditor without using the same code over and over again. Now FCKEditor can be embedded by a simple call, specifying the FCKEditor instance name, ToolBarSet and Height each time:

FCK.Setup(FCKContent, "Default", "400");

Editing content using .NET [skip]

The underlying service layer is where most of the magic happens. The services handle all the data inserts, updates and population of empty entities. Explaining how this works in detail is beyond the scope of this article.

The entities are mapped to DataReaders making use of DynamicBuilder, an excellent class library posted on CodeProject by Herbrandson. You can read more about this on codeproject.com.
Each CMS module follows the same load/postback cycle - this example uses the News edit module:

Page_Load
Load the page, setup FCKEditor and configure default values such as the page header, button visibility and any form default values. We store the unique id in ViewState so it can be used later for updating or deleting.

      public void Page_Load(object sender, System.EventArgs e)
    {

        int id = Tools.RequestAsInt("ID");

        FCK.Setup(FCKContent, "Default", "");

        if (!IsPostBack)
        {
            //  decide what buttons to display
            if (id > 0)
            {
                ViewState["id"] = id;
                this.GetRecord(id);
                btnDelete.Visible = true;
                this.SetBread("News articles / Edit article");

            }
            else
            {
                this.SetBread("News articles / Create new article");
                chkEnabled.Checked = true;
            }
        }
    }
  

GetRecord
If record id has been submitted create a new News entity and use the NewsSvc.GetByID method to load from the database and populate the entity.
Note the service makes use of the DataObject class, which handles database access. Employing the using directive ensures that database connections are created then disposed of after use. If you don't use this, then pretty soon you'll have open database connections everywhere. This is a Bad Thing™.

If we get a valid entity returned from the service, populate the form with the news entity values, and store in the ViewState for saving on PostBack.

    /// <summary>
    /// load current record
    /// </summary>
    /// <param name="id">id of item</param>
    private void GetRecord(int id)
    {

        News news = null;
        using (var newsSvc = new NewsSvc())
        {
            news = newsSvc.GetByID(id);
        }

        if (news != null)
        {
            txtTitle.Text = news.Title;
            txtSummary.Text = news.Summary;
            txtURL.Text = news.URL;
            FCKContent.Value = news.Article;
            chkEnabled.Checked = news.Enabled;

            if (news.Image.Length > 0)
            {
                imgThumb.ImageUrl = news.Image;
            }

        }
        else
        {
            Response.Redirect("List.aspx");
        }

    }

Updating and creating new pages
The same method is used for creating and editing news articles. If a valid id is stored in the ViewState we are updating, otherwise we are creating a new article.

We populate the entity with values from the form the send it to the NewsSvc for processing.

    /// <summary>
    /// admin/Update item
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnSave_Click(object sender, EventArgs e)
    {
        if (Page.IsValid)
        {
            var news = new News();
            news.ID = Convert.ToInt16(ViewState["id"]);

            if (news.ID > 0)
            {
                news.IsNew = false;
            }

            news.Title = txtTitle.Text;
            news.Summary = txtSummary.Text;
            news.URL = txtURL.Text;
            news.Article = FCKContent.Value;
            news.Enabled = chkEnabled.Checked;

            news.Image = txtImage.Text.Length > 0 ? txtImage.Text : imgThumb.ImageUrl;
           

            using (var newsSvc = new NewsSvc())
            {
                newsSvc.Save(news);
            }

            Response.Redirect("List.aspx");

        }
    }

The NewsSvc.Save method figures out whether the entity requires updating or saving depending on the .IsNew property and offloads the entity to the relevant internal method.
The entire process can be outlined in this diagram:

Postback process
postback process

Content Delivery [skip]

Page content is embedded in the website using one of two methods.

  • Directly on a page implementing the user control in /usercontrols/PageContent.ascx
  • Using virtual pages, created in the CMS with a specific URL.

Using the UserControl
The usercontrol is easy. Just add it to any page and complete the Page_Id property. The page_id is the unique id of the page content in the database table CMS_Page.

<CMS:PageContent  ID="PageContent1" EmbedMeta="false" EmbeddTitle="false" PageID="1"  runat="server"  />

It has a number of properties:

  • PageID - content PageID found in the database table [CMS_Page].[id]
  • EmbedMeta - use the page meta Keyword and Description information associated with this content and add to the page header. Defaults to true.
  • EmbedTitle - use the page title information associated with this content and add to the page header. Defaults to true.

Using Virtual Pages

This method is even easier. You don't have to create a physical page on the file system. Enter a URL when creating a page and the request will be handed off to the /get-virtual.aspx page, embedding the content.

Virtual page
Configuring a virtual page

Why use two ways to embed content?

Sometimes people want complicated pages with multiple content elements and functionality, sometimes its just a page of information, such as the license page on the demo. Having two methods makes the application flexible for different implementations.

The demo site show a number of uses, such as virtual pages, using two controls on a page and a combined page with news articles and content.

Caching

Content loaded from the front-end of the website is cached -  after all, content is not getting updated every minute, so there's no need to keep hitting the database everytime the page loads.

The PageSvc.GetById method uses the .NET HttpRuntime.Cache to store each page for 10 minutes. If a page is updated in the CMS, the cached object is destroyed.

        ///  <summary>
        ///  Returns CMS_Page entity, with cachable options
        ///  </summary>
        ///  <param name="pageID"></param>
        ///  <param name="UseCached">used cached version of item</param>
        ///  <returns>An entity of type Entity.PageData</returns>
        ///  <remarks></remarks>
        public Entity.CMS_Page GetByID(int pageID, bool UseCached)
        {

            CMS_Page GetByIDReturn = null;
            if (UseCached)
            {
                GetByIDReturn = (CMS_Page)HttpRuntime.Cache.Get("Pageid" + pageID); ;
            }

            if (GetByIDReturn == null)
            {
                const string SQL = "SELECT " + Columns + ", CMS_Section.[Name] AS SectionName, CMS_Section.ID AS SectionID FROM CMS_Page INNER JOIN CMS_Section ON CMS_Page.SectionID = CMS_Section.ID WHERE CMS_Page.ID=@pageID;";

                _data.CommandText = SQL;
                _data.Parameters.Add("@pageID", SqlDbType.Int).Value = pageID;

                GetByIDReturn = _data.LoadEntity<CMS_Page>(_data.GetReader());

                if (UseCached && GetByIDReturn != default(CMS_Page))
                {
                    // add to cache
                    HttpRuntime.Cache.Insert("Pageid" + pageID, GetByIDReturn, null,
                        DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration);
                }

            }
            return GetByIDReturn;
        }

Summary

FrameworxLite contains a lot of code, way too much to cover in one article. Its also based on a larger application so there may be some items that seem redundant or could have been put to better use. Well, they probably were at one point :-)

Most people I see on the FCKEditor forums repeatedly ask the same questions, usually about basic configuration issues, often associated with not having the correct paths setup. Having a fully working application where everything is already setup and working I hope will make it easier to understand how all the pieces of the jigsaw fall into place.

Download the source code for this article here.(1.2Mb)

View functioning demo here.

Back to top

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,