Bill Morefield My thoughts, discoveries, and occasional rambiings.

September 19, 2009

FCKEditor under ASP.NET MVC

Filed under: Uncategorized — Bill Morefield @ 2:00 pm

Often in a web application, I’ve found the need to let a non-technical user enter rich HTML style text into a form or control. I’ve been working on a medium scale application in ASP.NET MVC for the last few weeks and found myself needing the ability to do just this. In the past I’ve used the FCKEditor to do just this. I like this editor over many of its competitors because it includes the ability to upload files and images directly instead of having a third party program to do this. Note that in the newest version (renamed CKEditor 3.0) the file browser component has been split into a separate pay component.

I spent some time online and found a few articles on doing this. One I found on CodeProject was a bit outdated, but had some good information and a blog entry by Stefan Kamphuis had a good and working implementation, but didn’t cover all the features of the editor. It took me a couple of days of off and on work to get everything working so I’m going to document the process for my own reference and to help anyone else trying to do this.

As I mentioned above, the file browser component is now a pay component in the newest version so we’ll be using the latest in the 2.x line, FCKeditor 2.6.4.1. You’ll also need the FCKeditor.Net 2.6.3 package both of which can be downloaded from the CKEditor web site. Unzip both into a directory.

First we’ll create a blank ASP.NET MVC application in Visual Studio 2008 and get the editor control working . Copy the fckeditor folder that you unzipped from the FCKeditor_2.6.4.1.zip package into your application. I like to create a folder under Content called js and then place the fckeditor folder in there. You can delete everything out of this directory except the editor subfolder and two files: fckconfig.js and fckeditor.js files.

Next open up the Index.aspx file under our Views / Home folder. We’ll start by adding a textbox to this view that will become our rich text box. Add this line to the page.

<%= Html.TextArea("fckeditor", "", new { @name="fckeditor" })%>

Note that we are setting the name of the control to match the ID which is important.

Next we need to add a reference to the JavaScript files that we added to our project that actually implements the rich editor. This is done by adding the following line somewhere on the page.

[js]<script src="<%= Url.Content("~/Content/Js/fckeditor/fckeditor.js") %>" type="text/javascript" ></script>[/js]

And now we add the script to replace our standard text editor with the rich editor. Add this to the page below the script call above.

[js]
<script type="text/javascript"> window.onload = function() { var sBasePath = ‘<%= Url.Content("~/Content/Js/Fck/") >’;

var oFCKeditor = new FCKeditor( ‘FckEditor1’ ) ;

oFCKeditor.BasePath = sBasePath ;

oFCKeditor.ReplaceTextarea() ;

} </script>
[/js]

By default the framework will throw an error if you submit the form as it is now. The error “A potentially dangerous Request.Form value was detected from the client” is telling you that one of the items on the form contains HTML characters. To stop this, we need to turn off this checking. The easiest way to do this is by adding the following attribute to the action on the controller:

[csharp][ValidateInput(false)][/csharp]

At this point we have a rich control that can post text back. Next let’s get the file browser and upload component working. First we add a reference to the asp.net library that we also downloaded. There are several files in the zip file, but the file we need to reference is the FredCK.FCKeditorV2.dll located in binRelease2.0. This file implements the server logic.

Next we need to create a folder that will hold the content that users will be allowed to upload. I’m going to create this folder under the Content folder and call it usercontent. This folder must be granted the appropriate permission if you’re going to allow users to upload. Under most newer web servers giving the Network Service read/write/modify permission to the folder will do this.

Now back in our fckeditor folder we need to modify the fckconfig.js file to tell it that we’re going to use the ASP.NET server side component that we just referenced. Look for the two lines:

[js]
var _FileBrowserLanguage        = ‘php’ ;        // asp | aspx | cfm | lasso | perl | php | py

var _QuickUploadLanguage        = ‘php’ ;        // asp | aspx | cfm | lasso | perl | php | py
[/js]

and change them to

[js]
var _FileBrowserLanguage        = ‘aspx’ ;        // asp | aspx | cfm | lasso | perl | php | py

var _QuickUploadLanguage        = ‘aspx’ ;        // asp | aspx | cfm | lasso | perl | php | py
[/js]

Now we need to configure the server component to set up some security and to let it know where we want the content to be located. The file that we need to find is located under the fckeditor folder in fckeditor/editor/filemanager/connectors/aspx named config.ascx.

First we need to modify the CheckAuthentication() function to control who we want to use this component. You can simply change the function to always return true, but as the comments note, this is a really bad idea and will basically let anyone upload content to your site. That’s just begging to be hacked. More likely you will only want certain users or certain roles to be able to do this. You could also check a user’s role or do any checks that you want and return true or false as needed. For example if you want to allow any authenticated user then we can change the line to:

[csharp]return HttpContext.Current.Request.IsAuthenticated;[/csharp]

This will return true for any users who are logged in and false for anonymous users.

Lastly we need to set where our content will be located (the folder that we created a little earlier) The two lines that we need to change are:

[csharp]UserFilesPath = VirtualPathUtility.ToAbsolute("~/Content/usercontent/");[/csharp]

and

[csharp]UserFilesAbsolutePath = HttpContext.Current.Server.MapPath("~/Content/usercontent/");[/csharp]

There are some other settings here that you can change to control where and what users can upload, but this should get the basics of the editor working.

September 2, 2009

Renaming Folder Names from _svn to .svn in Subversion

Filed under: Uncategorized — Bill Morefield @ 6:26 pm

At one time Visual Studio didn’t like folders that started with a period.  Unfortunately Subversion uses a folder name .svn to store information by default.  As a workaround most Subversion and Subversion clients for Windows allowed users to set an environment variable (and the TortiseSVN client would do this for you) to use and underscore in place of the period so .svn became _svn.

So tonight I was getting around to finally setting up the development environment on my new Windows 7 laptop (and so far I love it) and moving the projects backed up from my old machine to my new one.  When I opened one up to work on it, I found that it didn’t seem to recognize that it was a local copy of a project.  Some investigation showed that the old laptop had this variable set, so it was looking for _svn instead of .svn for the information.

Most likely when I moved to my last laptop I set this variable as I still had one old ASP.NET 1.1 project that I maintained and simply left it that way even though I haven’t touched the project in two years.  Or I just set the variable so that the old files that had already been checked out with the variable set on its predecesor would work.  Now though I wanted to make a change and it seemed there had to be an easier way than going through all the sub folders in each project, unhiding each _svn folder, renaming it, and then hiding the renamed .svn folder back.

Sure enough at this site I found a batch file to do just that.  The back file is simply:

FOR /R %%f IN (_svn) DO IF EXIST "%%f" (
ATTRIB -h "%%f"
RENAME "%%f" .svn
ATTRIB +h "%%f"
)

I repeat the warning on that page that it works for me, but you use at your own risk.

August 30, 2009

Windows 7 Install

Filed under: Uncategorized — Bill Morefield @ 10:45 pm

I’ve had a laptop for about eighteen months and it’s about due for a reinstall.  Mostly because I didn’t take the time to reformat it from scratch at the start and left a lot of the vendor bloatware in place.  Plus it’s my primary development machine and sees a lot of installs and uninstalls when testing software and other products.

I bought it with 3 GB of memory as I didn’t want to go 64 bit at the time, but after having worked with a 64bit machine at work for the last few months, I felt more comfortable with the idea.  The maximum memory the laptop will take is 4 GB so Saturday I bought a 2 GB SODIMM to replace the 1 GB in it and take myself up to a total of 4 GB.  Overnight I left the laptop running and imaged the hard drive to an external drive.

Monday I booted up my Windows 7 RTM and began the install.  The only thing that didn’t detect out of the box or after a Windows Update was my USB wireless.  So far I’m impressed with Windows 7.

July 2, 2009

IN Queries using Linq to SQL

Filed under: Uncategorized — Bill Morefield @ 12:30 am

    I’ve been working on my first “real” project using Linq to SQL for the last few nights.  So far I’ve been quite happy, but tonight I ran into my first real “how do I do this?” moment.

     

    Simplifying the description a little, I have three types of objects.  One is a location, the second is a person, and the third we’ll call an object.  An object is assigned to a location.  Right now a person is assigned to a location, but may be assigned to more than one location.  What I need to find out is which objects are at a location or locations that a person is assigned to.

     

    That to me in would be something like this as a SQL query:

    SELECT Object.id FROM Objects WHERE Objects.location IN (SELECT PersonAssignments.location FROM PersonAssignments WHERE PersonAssignments.person = @PERSONID)

     

    In my experience ‘IN’ queries tend to be where most simple ORM systems break down.  Usually the only options I’ve found are to either write a stored procedure in SQL or come up with some hack that looks even worse than it performs.  So I began skimming through documentation and then searching and came across an article by Rob Conry at http://blog.wekeroad.com/blog/creating-in-queries-with-linq-to-sql/.

     

    In short, it can be done, but you have to think about the problem a little differently.  First we get the locations where the person is assigned.

     

    MyDB db = new MyDB();

     

    var locationList = from assignment in db.PersonAssignments

    where assignment.location == personId

    select assignment;

     

    And now we can then write our query like this:

    var objectList = from object in db.Objects

    where locationList.Contains(object.location)

    select object.id;

     

    It seems a little backwards to me at first, but works.  A nice feature (see the blog post above for a more details explination) is that since a query is only executed when you enumerate the results, this generates a single query when you enumerate the objectList.  Also nice is that you can do the NOT IN query the same way by simply using a not (!) opeator on the conditional.  So to get all the objects not in a location assigned to a person we can do:

    var objectList = from object in db.Objects

    where !locationList.Contains(object.location)

    select object.id;

     

    References:

  1. http://blog.wekeroad.com/blog/creating-in-queries-with-linq-to-sql/
  2.  http://www.thinqlinq.com/Default/Use-the-new-LINQ-Contains-extension-method-for-the-SQL-IN-clause.aspx
  3.  

June 10, 2009

Complex Object Databinding to a Gridview

Filed under: Uncategorized — Bill Morefield @ 7:15 pm

    I’ve recently been taking the time to clean up a couple of projects before I hand them off to someone else to manage.  Both were done fairly quickly under a severe time pressure and a hard deadline so design was sacrificed to getting them working.

     

    The web application connects to a large application using an Oracle database that contains a lot of data, most of which is irrelevant to this application.  Originally the application connected and pulled information out using SQL calls embedded into the application itself.  As part of this cleanup, I wanted to move all these SQL calls into an assembly that can be reused in this and other related projects that will need to access this same database in the future.  It will also help us update when newer versions of that other system come out, which happens about every eighteen months.

     

    First I had to pull out the database code and build an assembly that wrapped objects around the database calls.  Now instead of a SQL statement, a simple repository pattern allows data to be queried and objects representing the data to be used.

     

    When I went to the original web application to change it to use the new complex objects being returned, I ran into my first real problem.  In a number of places I’m using a GridView to display data that matches a search.  It turns out that .NET does not work well with objects that contain properties which are other objects.  For a simple example, take the following two classes:

     

        public class Book

        {

            public string Title { get; set; }

            public Person Author { get; set; }

        }

     

        public class Person

        {

            public string Firstname { get; set; }

            public string Lastname { get; set; }

        }

     

    Now take a collection of Book objects and attempt to bind it to a GridView designed like this.

     

    <asp:GridView ID=”BookView” runat=”server” CssClass=”grid” Width=”90%”>

        <Columns>

            <asp:BoundField DataField=”Title” HeaderText=”Title” />

            <asp:BoundField DataField=”Author.Lastname ” HeaderText=”Last Name” />

        </Columns>

    </asp:GridView>

     

    When you run this the gridview will complain that it cannot dechipher Author.Lastname.  Instead of looking for an object that is a property named Author and then getting the Lastname property of it, it instead seems to look for a property named “Author.Lastname” which of course does not exist.  Why this doesn’t work I have no idea, but it doesn’t.

     

    After searching the Internet, I found three possible solutions:

  1. Turn the column into a TemplateField and use the DataBinder.Eval approach which will correctly look for an Author property and then the Lastname property of it.  It works, but leaves your code a bit messy.  The above example would now look like this:
     
  2. <asp:GridView ID=”BookView” runat=”server” CssClass=”grid” Width=”90%”>

        <Columns>

            <asp:BoundField DataField=”Title” HeaderText=”Title” />

            <asp:TemplateField DataField=”Author.Lastname ” HeaderText=”Last Name”>

    <ItemTemplate>

    DataBinder.Eval(Author.Lastname)

    </ItemTemplate>

            </asp:TemplateField>

        </Columns>

    </asp:GridView>

     

    While this works pretty well for one column, imagine if you had ten columns to do like this.

     

  3. Build properties into the parent object that allow you to access the children directly.
  4. To do this you would modify our Book class above to read:

        public class Book

        {

            public string Title { get; set; }

            public Person Author { get; set; }

            public AuthorLastname

     get

    {

    return Author.Lastname;

    }

    Public AuthorFirstname

    {

    Get

    {

    Return Author.Firstname;

    }

            }

        }

     

    This works, but seems to defeat the purpose of having classes in the first place.  Still for only one or two properties and a lot of bound objects, this is probably the approach that I’d use.

  5. The other option I found is to use something a little “smarter” than the built in BoundColumn that understands complex objects and handles them appropriately.  For two examples of doing this see:* ComplexBoundField – Databinding Complex Objects to a Gridview and ObjectField 1.1.
  6.  

    In my case I chose to do the third option and used the ObjectField class by James Gregory.  Only thing that I needed to do was add code to register the new assembly on the pages and change the BoundColumn to the new ObjectField column.  Once I did that, everything worked as expected.

« Newer Posts

Powered by WordPress