Submitting a multipart AJAX form with CKEditor textarea

OK, the subject of this post is pretty niche, but that’s why I am posting about it because I do it so little I’d only forget about it for the next time!

With ASP.NET MVC if you want to allow a file upload on a form submitted by AJAX then you have to intercept the form submit event and do the submit it yourself via Javascript, rather than using MVC’s helpers. This is detailed in this useful Stackoverflow answer: How to do a aspnet mvc ajax form post with multipart form data

What the post doesn’t mention (as it wasn’t asked for!) was that if your form contains an instance of the popular rich text editor CKEditor then this code alone will not transfer the data the editor contains to the form. I think this must be because the CKEditor is waiting for the submit event to be triggered before it does the copy into the field.

It took a bit of digging, as I have not had to look at the CKEditor API before, but if you take the code from the above link as a start point, then add the following code to somewhere between the event.preventDefault() call and the ajax() call, it seems to work OK.


            for (instance in CKEDITOR.instances) {
                CKEDITOR.instances[instance].updateElement();
            }
Advertisements

Adding CKEditor to an ASP.MVC AJAX dialog

CKEditor is one of the most popular rich text editors for the Web out there, but although I’ve used it many times I’ve never added it to an MVC site before. Although following the instructions is simple enough for a static view, adding it to a dynamically created view within a jQuery dialog box proved a little bit trickier. As usual there are bits and bobs around on the Net about it, but never the whole thing in one place! So I’ve collated what I’ve found, and added some bits I discovered myself.

OK, so say you want to popup an Edit screen in a jQuery dialog, generating the Edit view to go within the dialog on-the-fly. Download the CKEditor and put the scripts into your site, it doesn’t matter what configuration you use. I usually just put it under the Scripts directory. Also download the jQuery plugin jquery.livequery and put it into your site.

You need the following script links in your page header. whatever.js is just the name of a JS file that you are going to create in a minute, obviously change the paths of the CKEditor and livequery scripts if necessary. It is crucial to include the jQuery adaptor.

<script src="@Url.Content("~/Scripts/jquery.livequery.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ckeditor/ckeditor.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ckeditor/adapters/jquery.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/whatever.js")" type="text/javascript"></script>

To start with you would put an Ajax.ActionLink on your View with a call to the Edit screen a bit like this (Razor view syntax):

@Ajax.ActionLink("Edit", "Edit", "Whatever", new AjaxOptions { UpdateTargetId = "editWhatever", OnBegin = "showEdit()"})

Where editWhatever is just an empty div somewhere else on the page, I’ll come onto the showEdit() Javascript in a minute.

The Edit view (and view model) would be something like below. Note that for some reason your field must be an Html.TextArea or TextAreaFor, a regular HTML textarea tag does not work, even though it is fine to use that tag in a non-AJAX form.

@model MyProject.ViewModels.WhateverEditViewModel

@using (Ajax.BeginForm("Save", "Whatever", new AjaxOptions { UpdateTargetId = "editWhatever", OnSuccess = "hideEditOnSuccess()" })) 
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(m => m.Site.Code)

    <div class="question-editor-label" >
       @Html.LabelFor(model => model.WhateverPropertyToEdit)
    </div>
        
    <div class="section-editor-field" >
            @Html.TextAreaFor(model => model.WhateverPropertyToEdit)
     </div>
  
     @Html.ValidationSummary()

    
    <button title="Save" type="submit" value="Save"  >Save</button> 
    <button type="button" value="Cancel" onclick="doCancel()" title="Cancel" >Cancel</button>
}

And you could probably work out the view model, but here it is:

namespace MyProject.ViewModels
{
    public class WhateverEditViewModel
    {
        [Display(Name = "Whatever Description")]
        public string WhateverPropertyToEdit { get; set; }
    }
}

And remember the ‘Save’ controller action method needs to accept HTML, so you have to decorate it with the annotation [ValidateInput(false)].

[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Save(WhateverEditViewModel passedModel)
{
   ...
}

Below is the Javascript for the page contained in whatever.js. In the $(document).ready() function it is crucial to use livequery to detect the appearance of the field on the edit form, as it does not exist when the document is first created. Use the jQuery call to ckeditor() to launch CKEditor. The rest should hopefully be obvious.

$(document).ready(function () {

    $('#WhateverPropertyToEdit').livequery(function () {
        $('#WhateverPropertyToEdit').ckeditor();
    });
});

function showEdit() {
    $('#editWhatever').dialog(
        {
            modal: true,
            width: 1000,
            title: 'Edit Whatever',
            dialogClass: 'edit_whatever_class',
            ....
        });
}


function doCancel() {
    $('#editWhatever').dialog('close');
}

function hideEditOnSuccess() {
    if ($('#editWhatever').find(".validation-summary-errors").size() == 0) {
        $('#editWhatever').dialog('close');
        /* do anything to the 'host' screen you need to here. */
    }
}

And you should now see the editor appear on your dialog box.

Internet Explorer does not refresh an ASP.NET MVC Ajax form correctly

I’ve come across a problem with Internet Explorer (I’ve looked at versions 8 and 10) when using a form called via Ajax in MVC. Basically, if you have a standard call to a Ajax.ActionLink such as this:

@Ajax.ActionLink("Edit Thingy", "Edit", "Thingy", new { id = thingy.Code }, new AjaxOptions { UpdateTargetId = "editMyThingy" })
 

Then IE appears to cache your form, so that the next time you view it the values in it are exactly the same as when you first loaded it up, making it look like nothing has been saved (it has been saved, it just looks like it hasn’t to the end user).

The obvious thing to try is to use the OutputCache attribute on your method like so:

[HttpGet]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] 
public ActionResult Edit(string id)
{
 ...
}
 

However IE seems to ignore this. Eventually I got around the problem in a clunky way by forcing the ActionLink to call the method via a Post rather than a Get (see below). This seems to kick IE back into gear and doesn’t cache. Seems a bit hacky though, so if anyone has any other ideas please let me know!

@Ajax.ActionLink("Edit Thingy", "Edit", "Thingy", new { id = thingy.Code }, new AjaxOptions { UpdateTargetId = "editMyThingy", HttpMethod="Post"  })
 
[HttpPost]
public ActionResult Edit(string id)
{
 ...
}
 

Bug/feature in jQuery tablesorter pager

I’ve just come across a feature/bugette of the otherwise excellent jQuery tablesorter.pager plugin which is worth making a note of:

If your table has no rows and you try to make the call to the plugin then it appears to throw a wobbly and possibly an exception too. If you are doing this within an ‘OnSuccess’ event from an MVC Ajax.BeginForm() call then it appears to cancel out of the call and possibly messes up your screen too, depending on what else you are doing in this call.

To get around it I simply returned a different PartialView that didn’t contain the table, and then in the OnSuccess event checked to see if the table existed:

 

if ($("#tblSearchResults").length > 0) {
        $("#tblSearchResults").tablesorter();
        $("#tblSearchResults").tablesorterPager({ container: $("#pager") });
    }

Another way might be to tag individual rows with a class name, and see if any instances of that class exist on the page. The above way suited me better this time.

ASP.NET MVC redirect to login page with AJAX

If you use the [Authorize] attribute on your MVC controller then your users will automatically be redirected to the login page when they GET or POST if their session or cookie has expired. However if you are using the AJAX helpers to redirect output into a panel on the page this just results in the login page appearing in the panel, rather than the whole page. And quite often you will find that it does not work anyway as it seems to lose any information that it might have had about what virtual directories it is in.

A solution for this is quite simple, but I’ve not as yet found anywhere else on the web that puts it all together. So instead of [Authorise] apply the following to your controller constructor (or action):

[CustomAuthorise]
public class MyController : Controller

The source for the CustomAuthorise attribute is as below (obviously substitute in your own login URL!). Note that you can still use other custom authorise attributes if you do this, e.g. a role checker.

 public class CustomAuthoriseAttribute: AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            if (filterContext.Result == null || (filterContext.Result.GetType() != typeof(HttpUnauthorizedResult)
                || !filterContext.HttpContext.Request.IsAjaxRequest()))
                return;

            var redirectToUrl = "Security/LogOn?returnUrl=" + filterContext.HttpContext.Request.UrlReferrer.PathAndQuery;

            filterContext.Result =  new JavaScriptResult() { Script="window.location = '" + redirectToUrl + "'"};
    
        }
    }

Thanks to these posts elsewhere on the web for helping out:
http://craftycodeblog.com/2010/05/15/asp-net-mvc-ajax-redirect/
http://beckelman.net/2010/04/01/custom-aspnet-mvc-authorization-attribute-for-ajax-requests/

Editable data grid in ASP.NET MVC

I was asked by a colleague recently how to create an editable data grid in MVC, as that is quite easy in Webforms. I was surprised to find that there is not much in the way of examples on the web at the moment; most seem to say download a 3rd party helper which is a bit of a cheat. 🙂 So I decided to post my notes about how I generally do it. This example assumes you are a reasonably experienced programmer – I don’t explain the all the bits that aren’t relevant to the topic! Also, I am using Sharp Lite in this example, which is a framework that sets up a Visual Studio template for MVC3/Razor and NHibernate. This more-or-less follows their recommended pattern in terms of CRUD. I dare say that there are dozens of more elegant ways to do this, but this method seems to work well for very simple sites at least.

This example is very simple – a list of ‘Special Codes’, each record in the underlying database table has just 2 fields. The grid itself has no paging or user-controlled sorting but hopefully it should be reasonably easy to see how to add it. Each item on the list has an Edit and Delete button, and there is a Create button at the bottom of the list. Editing is done ‘inline’, but depending on how you style the edit form it could just as easily be turned into a dialog box.

First of all the NHibernate database entity and its simple SharpLite-style ViewModel that we are going to list in the grid:

    [HasUniqueDomainSignature(ErrorMessage="2 Special Codes may not have the same code")]
    public class SpecCode:Entity
    {
        public SpecCode() { }

        public SpecCode(string _code)
        {
            Check.Require(!String.IsNullOrEmpty(_code), "_code must not be null or empty");
            Code = _code;
        }

        [DomainSignature]
        [Display(Name = "Code")]
        [Required(ErrorMessage = "Code is required")]
        [StringLength(2, ErrorMessage = "Code must be a maximum of 2 characters")]
        public virtual string Code { get; set; }

        [Display(Name = "Description")]
        [Required(ErrorMessage = "Description is required")]
        [StringLength(100, ErrorMessage = "Description must be a maximum of 100 characters")]
        public virtual string Description { get; set; }
    }

    public class SpecCodeEditViewModel
    {
        public SpecCode SpecCode { get; set; }
    }

This is going to be an AJAX grid accessible via the URL /SpecCode/List. We have 3 HTML pages. The first one is a View that just contains any calls to the Layout view and the relevant Javascript libraries. The second is a PartialView that contains the grid itself (and is within a div to allow easy update via the MVC AJAX helpers). The third is another PartialView containing the edit form.

the next 3 listings are the views mentioned above – the first one being the main ‘List’ View:

@model IEnumerable<MyProject.Domain.SpecCode>

@if (!Request.IsAjaxRequest()) 
    {
        /* Putting this inside an IsAjaxRequest() block prevents reloading of the JS libraries on every Ajax call.*/
     <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
     <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

     <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
     <script src="@Url.Content("~/Scripts/list-specialcode.js")" type="text/javascript"></script>
    }
     
@Html.Partial("Listing", Model)

Next is the PartialView that contains the datatable itself. The Ajax.ActionImageLink() method is not a standard MVC helper – it is one of my own. You can use the standard Ajax.ActionLink() to achieve the same thing. As you can hopefully see, all that the Edit or Create buttons do is to load the ‘Item’ PartialView into the ‘divEditItem_*’ div. The Javascript is listed further down.

@model IEnumerable<MyProject.Domain.SpecCode>
@using MyProject.Web.Helpers

<div id="ListView">

<h2>Special Codes</h2>
   
    <table>
       <tr>
        <th>
        <span class="codeStyle">Code</span>
        <span class="descStyle">Description</span>
        
        </th>
       </tr>

    @foreach (var item in Model)
    {
        
        <tr>
            <td style="width:400px">

                <div id='@String.Concat("divEdit_", item.Id.ToString())'>
                    <div id='@String.Concat("divDisplayItem_", item.Id.ToString())'>
                        <span class="codeStyle"> @Html.DisplayFor(x => item.Code)</span>  <span class="descStyle"> @Html.DisplayFor(x => item.Description) </span>
                    </div>

                    <div id='@String.Concat("divEditItem_", item.Id.ToString())' >
         
        
                    </div>

                </div>
    
            </td>
            <td style="vertical-align:top">
          
                 @Ajax.ActionImageLink(Url.Content("~/Content/images/edit24.png"), "Edit", "Edit", "SpecCode", new { id = item.Id }, new AjaxOptions { UpdateTargetId = "divEditItem_" + item.Id, OnBegin = "showEdit(" + item.Id + ")" })

                 <a href='@String.Concat(new string[]{"javascript:delete_item(", item.Id.ToString(), ")"} )' ><img src='@Url.Content("~/Content/images/delete16.png")' alt="Delete" title="Delete" height="24px" width="24px"/></a>
            </td>
        </tr>
        
    }
    <tr>
            <td style="width:500px">
                <div id='divEdit_0'>
                    <div id='divDisplayItem_0'>
                    </div>

                    <div id='divEditItem_0' >
                
        
                    </div>
                </div>
            </td>
            <td style="vertical-align:top">
          
              @Ajax.ActionImageLink(Url.Content("~/Content/images/add32.png"), "Create New", "Edit", "SpecCode", new { id = 0 }, new AjaxOptions { UpdateTargetId = "divEditItem_0", OnBegin = "showEdit(0)" })  
            </td>
    </tr>

    </table>

 </div>

Finally the ‘Item’ PartialView that contains the form for editing. This will simply show two fields side by side with a ValidationSummary helper that may show text underneath those fields.

@model MyProject.Tasks.ViewModels.SpecCodeEditViewModel
@using (Ajax.BeginForm("Edit", new AjaxOptions { UpdateTargetId = "divEditItem_"+  Model.SpecCode.Id, OnSuccess = "hideEditOnSuccess(" + Model.SpecCode.Id + ")" })) 
{
    @Html.AntiForgeryToken();
    @Html.HiddenFor(m => m.SpecCode.Id)                
   <span class="codeStyle"> @Html.EditorFor(m => m.SpecCode.Code, "SmallTextTemplate")  </span>
    
   <span class="descStyle"> @Html.EditorFor(m => m.SpecCode.Description, "LongNameTemplate") </span>
    
    <button title="Save" type="submit" value="Save"  ><img src='@Url.Content("~/Content/images/save.png")' alt="Save" title="Save" height="16px" width="16px"/></button> 
    <button type="button" value="Cancel" onclick="hideEdit(@Model.SpecCode.Id)" title="Cancel" ><img src='@Url.Content("~/Content/images/cancel.png")' alt="Cancel" title="Cancel" height="16px" width="16px"/></button>
    
    @Html.ValidationSummary() 

}

You can see from the last two listings that quite a lot of Javascript calls are made. These reference functions in a JS file called list-specialcodes.js, which is referenced in the ‘List’ View. Below is the Javascript code which should be resonably self-explanatory:


function showEdit(ID) {

    $('#divDisplayItem_' + ID).hide();
    $('#divEditItem_' + ID).show();
}


function hideEdit(ID) {
   
    $('#divEditItem_' + ID).hide();
    $('#divDisplayItem_' + ID).show();
}


function hideEditOnSuccess(ID) {
   
    /* if no errors remove the form from the page and reload the list */
    if ($('#divEditItem_' + ID).find(".validation-summary-errors").size() == 0) {
        $('#divEditItem_' + ID).hide();
        $('#divDisplayItem_' + ID).show();
        reload_listing();
    }
}

function reload_listing() {
    var urlpath = "";
    var urlstr = urlpath + "/SpecCode/List";

    $.ajax({
        cache: false,
        url: urlstr,
        success: function (data) {
            $("#ListView").html(data);

        }
    });
}


function delete_item(ID) {

    if (confirm("Do you want to delete this special code?") == false) {
        return;
    }

    var urlpath = "";
    var urlstr = urlpath + "/SpecCode/Delete/" + ID;

    $.ajax({
        cache: false,
        url: urlstr,
        type: "POST",
        success: function (data) {
            reload_listing();
        }
    });
}

Obviously there needs to be a controller for this – here is the code:

    public class SpecCodeController : Controller
    {
        private IRepository<SpecCode> _rcRepo;
        private SpecCodeCudTasks _cudTasks;

        // SpecCodeCudTasks is a SharpLite CUD service class.
        public SpecCodeController(IRepository<SpecCode> rcRepo, SpecCodeCudTasks cudTasks)
        {
            Check.Require(rcRepo != null, "rcRepo cannot be null");
            Check.Require(cudTasks != null, "cudTask cannot be null");
            _rcRepo = rcRepo;
            _cudTasks = cudTasks;
        }

        [HttpGet]
        public ActionResult List()
        {

            // Obviously you could order by something in a parameter here if you wanted
            List<SpecCode> SpecCodes = _rcRepo.GetAll().OrderBy(x => x.Code).ToList();

            if (Request.IsAjaxRequest()) // From the JS file
                return PartialView("Listing", SpecCodes);
            else // URL called direct
                return View("List", SpecCodes);
        }

        public ActionResult Edit(int id)
        {
            // This is just to stop someone navigating directly, not that necessary really  
            if (!Request.IsAjaxRequest())
                return View("NotFound");

            if (id != 0)
            {
                SpecCode rc = _rcRepo.Get(id);
                return PartialView("Item", _cudTasks.CreateEditViewModel(rc));
            }
            else
            {
                return PartialView("Item", _cudTasks.CreateEditViewModel());
            }
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(SpecCode speccode)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    // Sharp Lite CUD stuff.
                    ActionConfirmation<SpecCode> confirmation;

                    confirmation = _cudTasks.SaveOrUpdate(speccode);
                    if (confirmation.WasSuccessful == false)
                    {
                       ModelState.AddModelError("SpecCode.Code",confirmation.Message);
                    }
                }

                return PartialView("Item", _cudTasks.CreateEditViewModel(speccode));
            }
            catch
            {
                return View();
            }
        }

        [HttpPost]
        public ActionResult Delete(int id)
        {
            _cudTasks.Delete(id);
            return new ContentResult();
        }
    }

I’ve not included any styling here, or the SharpLite CudTasks class, but hopefully they are fairly simple to figure out (think inline-block for the styling!), and are not that relevant to this example anyway. Hope this helps someone, and especially my colleague :-).

Increasing the security of AJAX requests from the browser

Just a quickie.

I’ve a situation where I need to use AJAX to get a list of items for a particular user.

The for the request is in a hidden field (i.e. fully publicly viewable) in the DOM

e.g. http://www.example.com/users/alexhardman/items

The problem with using the username here is that it is human readable which makes it very easy to hack by changing on or more of the characters.

To increase the securing I’ve added a guid row to my user table which will be used to retrieve users.

e.g. http://www.example.com/items/for/7275670d-f06d-4be2-b260-4c8e094ead6c

This makes it infinitely more difficult to hack the url and return information for a user other than the one given in the url.

e.g.