Xceedas

Xceedas
xceedas

Thursday, 15 January 2015

Using CKEditor with Razor for .NET MVC 3

Getting Started

To get started, and throw in a quick plug, I am going to use .NET MVC Boilerplate, our open source project at Moov2 to give us a head start with a new project using the .NET MVC framework. We have our project, the next step is to download the CKEditor. The current version at the time of writing is 3.6. Once downloaded, unzip the contents (should be a single directory called "ckeditor") and copy to the root directory in the website.

Implementation

Lets just get the CKEditor loaded on a page in the website. Firstly the JS files for CKEditor need to be included on the web page, include the required JS files into the master page (or just onto the web page the ckeditor is required on) of the project for the moment as shown below.
<script src="ckeditor/ckeditor.js"></script>
<script src="ckeditor/adapters/jquery.js"></script>
ckeditor.js is the main JS file that holds all the code that makes up the CKEditor. I always utilise the JQuery framework to assist with JS coding on a website as it just makes things a lot easier. So in order to get the CKEditor working with JQuery the jquery.js adapter is required. In order to get the editor working, some JS is required in order to convert a DOM element into the CKEditor. You can use a divp or textarea, I recommend using a textarea because the textareais a reasonable fallback when the browser doesn't have JS enabled.
<textarea id="my-textarea"></textarea>
Above is the textarea I have added to the web page so it can be converted into a ckeditor. I never use inline JS, so I have added the code below to the main external JS file (js/mylibs/boilerplate.js). The code is basically saying when the document had loaded, convert the specified selector into a ckeditor. If the selector doesn't match any elements in the DOM, then nothing happens.
$(function () {
    $('#my-textarea').ckeditor();
});
Provided you have everything in place correctly, you should see something resembling the screenshot below. The textarea has been converted into a CKEditor offering a rich set of content formatting tools. Super!
Installation.

Sending Content to the Server

So we have our CKEditor on the page, next step is to send the content added by the user to the server so it can be stored or whatever needs to be done with it. With the .NET MVC framework, a process known as model binding is used to send data from the client to a method in a controller on the server. Model binding will take the form values or query string values and try to set those values on the properties of an object instance (the view model), that object is then passed to a method on a controller.
Lets create a view model that will hold the content that is created by the user via the ckeditor.
using System;
namespace CKEditorExample.ViewModels.Content
{
    public class ContentViewModel
    {
        public string Text { get; set; }
    }
}
The Text property will have the user content set on it automatically by MVC 3 before it is passed into a controller method. Next step is to modify the razor view page that shows the ckeditor to tell MVC to bind the textarea to the view model that we just created. Razor provides lots of useful helper methods, particular to do with setting up HTML form elements for model binding. The code for the razor view for the content submission is shown below.
@model CKEditorExample.ViewModels.Content.ContentViewModel

@{
    ViewBag.Title = "Content Entry";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Content Input</h2>

<form method="post" action="/Content/Submit">
@Html.TextAreaFor(x => x.Text)

<p><input type="submit" value="Submit" /></p>
</form>
At the top on the view the model is declared, so now the textarea can be binded to a property on our model. As you can see we are using the razor Html.TextAreaFor to bind a textarea to the Text property on our view model, so when the form is submitted the value in the textarea at the time of submission should be set on the Text property. You may notice that the form is being submitted to /Content/Submit, I have created another razor view / controller method to handle the form submission and display a web page showing the content from the ckeditor that was sent to the server. The ContentController has two methods now, Index which returns a view that displays the form, and Submit that takes care of the form submission.
using System;
using System.Web.Mvc;
using CKEditorExample.ViewModels.Content;

namespace CKEditorExample.Controllers
{
    public class ContentController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Submit(ContentViewModel model)
        {
            ViewBag.Text = model.Text;
            return View();
        }
    }
}
Above the Submit method shown in the controller above are a couple of attributes. HttpPoststipulates that only POST requests are accepted. The second attribute ValidateInput is telling MVC not to validate the user input, without this when submitting the form you will see the error message shown below.
Form submission exception when controller method doesn't have ValidateInput attribute.
"[HttpRequestValidationException (0x80004005): A potentially dangerous Request.Form value was detected from the client..." exception is thrown because there is HTML being sent to the server. The [ValidateInput(false)] attribute will prevent this error happening, however we are opening ourselves up to a security vulnerability. So, to handle this you can encode the HTMLbefore it is saved a database, or run checks on the content to ensure there isn't any malicious HTML, such as script tags for JS code blocks. This will allow all our properties on the model to have HTML passed to the server, if your not keen for that you can add [AllowHtml] above the property that is permitted to accept HTML, thus removing the need for the[ValidateInput(false)] attribute.
So submitting the form in the state as shown below.
Content being submitted.
Will result in the web page shown below.
Content after submission.
As you can see the HTML that is created by the CKEditor has been sent to the server and is now displayed in the response. This demonstrates creating a CKEditor and sending the content entered by a user to the server.

Customization

In the screenshots above you can see that CKEditor offers a large set of options in the toolbar. By default CKEditor offers all the features, however you might not want to overwhelm users and offer just a small subset of the commonly used tools. That is easily done, CKEditor comes with a "Full" and "Basic" tool set collection by default. In order to switch to the basic option you need to open config.js in the ckeditor/ directory and make the changes shown below.
/*
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

CKEDITOR.editorConfig = function( config )
{
    // Define changes to default configuration here. For example:
    // config.language = 'fr';
    // config.uiColor = '#AADC6E';
    config.toolbar = 'Basic';
};
Below is a screenshot of the basic toolbar.
Basic toolbar set on CKEditor.
Now the basic toolbar maybe too minimal. Not to worry, you can create custom toolbar collections. In the same config.js file, you can specify custom toolbars and set theconfig.toolbar to a custom toolbar. For this, I used the CKEditor documentation to see the names of the different individual tool icons, as there is an example of the "Full" toolset. I have made the following custom toolbar set.
/*
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

CKEDITOR.editorConfig = function( config )
{
    // Define changes to default configuration here. For example:
    // config.language = 'fr';
    // config.uiColor = '#AADC6E';
    config.toolbar = 'Custom';

    config.toolbar_Custom = [
    { name: 'styles', items: ['Styles', 'Format'] },
    { name: 'basicstyles', items: ['Bold', 'Italic', 'Strike', '-', 'RemoveFormat'] },
    { name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Blockquote'] },
    { name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
        { name: 'insert', items: ['Image'] },
        { name: 'tools', items: ['Maximize', '-', 'About'] }
    ];
};
The toolset that I have made follows the CKEditor convention of naming the toolbar "toolbar_Custom". So in order to change the current toolbar to my own, the config.toolbar is set to "Custom". The toolbar that I have specified is shown in action below.
Custom CKEditor toolbar.
The custom toolbar declaration in the JS code is an array of objects that specify the different sections in the toolbar. The objects in the custom toolbar array are made up of a name and a collection of items. The array of items specify the different tools to be available in that section. You can specify that a spacer be used by adding a "-" in the items collection. So in the screenshot of my custom toolbar, you can see that between the "Strike" and "RemoveFormat" options there is a spacer. In the custom toolbar all the sections are lined up horizontally on a single line, if you have more options though you may want to have sections on multiple lines. To do this, add a '/' to the main array as shown below.
/*
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

CKEDITOR.editorConfig = function( config )
{
    // Define changes to default configuration here. For example:
    // config.language = 'fr';
    // config.uiColor = '#AADC6E';
    config.toolbar = 'Custom';

    config.toolbar_Custom = [
    { name: 'styles', items: ['Styles', 'Format'] },
    { name: 'basicstyles', items: ['Bold', 'Italic', 'Strike', '-', 'RemoveFormat'] },
    { name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Blockquote'] },
        '/',
    { name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
        { name: 'insert', items: ['Image'] },
        { name: 'tools', items: ['Maximize', '-', 'About'] }
    ];
};
The first three sections will be in a line above the last three sections as shown below.
Custom multi-lined toolbar.

Integration with CKFinder

One of the requirements for content management usually involves users being able to upload images from their computer to the server and have the image included in the content. This functionality doesn't come straight out of the box with CKEditor, it requires integration withCKFinder. CKFinder is an AJAX file manager developed by the creators of CKEditor that integrates seamlessly. CKFinder requires interaction with the server and is available for ahandful of platforms, the one we are obviously interested in is ASP.NET. To start integrating the CKFinder download the ASP.NET zip, extract and add the ckfinder/ directory into the root of your website.
Now you have all the files it is worth excluding the _source directory from the project (right click directory and then "Exclude from Project") otherwise lots of compilation errors will appear. Add a reference to CKFinder.dll that can be located in the ckfinder/bin/Releasedirectory.
Next step is to tell our CKEditor instances to use the CKFinder, we do this in the JS. In the same JS file that declares the DOM element to become a CKEditor specify the setup of the CKFinder as shown below.
$(function () {
    $('#Text').ckeditor();

    CKFinder.setupCKEditor(null, '/ckfinder/');
});
null is passed to tell CKFinder to apply to all current instances of CKEditor, otherwise a particular instance of CKEditor can be specified when there are multiple editors on a single page but only a particular one requires CKFinder. When clicking the "Image" option an "Upload" tab now appears as shown below.
CKEditor with the Upload tab.
When first trying to upload an image to the server I was alerted a popup saying "The file browser is disabled for security reasons. Please contact your system administrator and check the CKFinder configuration file.". Turns out that the setup wasn't complete. By default the CKFinder is setup to not authorize sending files to the server. Open ckfinder/config.ascx, this is the main configuration file, temporarily change the CheckAuthentication() method to returntrue instead of false. It is recommended to implement your own authentication logic to fit with the project, but for this example returning true will do. When re-attempting sending a file to the server you should be greeted with the screen below.
You can also now "Browse Server" on the default image tab, this will allow you to select any files that you have already uploaded to the server.
The config.ascx file is where you go when you need to configure CKFinder. Open the file to see what you can configure. The main parts are the authentication, licensing (LicenseName & LicenseKey properties) and ability to specify where the uploaded files are saved on the server (BaseUrl & BaseDirectory properties).

Production Ready

The CKEditor & CKFinder combine to a total size of around 10mb. Although this isn't a huge amount of disk space, there isn't any point having unneeded files on the server or in source control. The documentation on the CKEditor website has a handy section that describes what files & directories are required. The first directory I deleted was the samples & sourcedirectory in both the ckeditor/ & ckfinder/ directory. There are a handful of files in the root of the CKEditor & CKFinder directory that can be deleted, as described at the bottom of thedocumentation for minimum setup. Once that is done, there isn't much else that can be removed. If you wanted to you could remove the JS files in the lang directory for the languages that you aren't going to be needing.

Licensing

The CKEditor is open source and free for non-commerical use as described on their license page. A clever tactic by the guys at CKEditor though is to charge for the use of CKFinder. Being able to upload images is a integral part of content management, so without the CKFinder the CKEditor is missing a key part of functionality. It is a brilliant WYSIWYG editor so I believe the $59 (£38) fee to use the CKFinder on a personal website isn't too steep.

Source Files

Final bits

Turns out that implementing CKEditor with CKFinder isn't much of a stress fest, and with useful documentation it was fairly seamless. While implementing this example I was using thenew Visual Studio 2012 RC and thoroughly enjoyed using it. I would definitely recommend switching over and giving it a go.

No comments :