2010
09.02

My company recently started a corporate wiki. We initially used ScrewTurn, an excellent open source ASP.NET application. It worked great for our department, but it wasn’t taking off for the rest of the company. Non-technical users just couldn’t get wiki markup and weren’t able to manage edits with the visual editor. So we turned to Confluence, an enterprise class commercial wiki. I won’t get into a feature comparison here. There are plenty of other sites devoted to that.

Since we had been using ScrewTurn for several months, we had a few hundred pages that we needed to migrate to Confluence. We hoped to find a utility to take care of the migration, but we were unable to find one. So in the end, I wrote my own UWC implementation for ScrewTurn.

The Confluence Universal Wiki Converter (UWC) is defined as

… a standalone application which converts pages from other wikis to Confluence. It’s also an extensible framework, allowing users with wikis that aren’t currently supported to add their own converters.

Here are the general steps I took to implement a wiki converter for ScrewTurn.

  1. Implement a Wiki Converter for ScrewTurn. I used MediaWiki’s Syntax Converter as a base since the basic wiki syntax is very similar. I also implemented a few Converter classes, UserDateConverter (requires the Confluence UDMF plugin), PagenameConverter, AttachmentsConverter, MetaDataCleaner (to remove the first three lines in ScrewTurn page files that include page name, date, and ##PAGE##).
  2. In ScrewTurn, change the page storage provider to Local Pages Provider (if its using a different provider such as SQL).
  3. Run the customized UWC implemented in step 1 and convert one namespace at a time.

You can also view my StackOverflow question and answer that inspired this post.

Confluence UWC with ScrewTurn converter source code

2010
06.01

I ran into an issue today where I needed to validate a few controls without ViewState. These were drop down lists whose items were added from the returned value of a WebService.

function bindDropDownList(items, controlSelector)
{
    var itemsHtml = "<option><\/option>\n";
    $(items).each(function(index, item)
    {
        itemsHtml += "<option value='" + item.Value + "'>" + item.Text + "<\/option>\n";
    });

    $(controlSelector).html(itemsHtml);
}

This worked great, until I tried to use a RequiredFieldValidator. The client side validation actually worked, but the server side validation kept saying that there was no value selected. This is because the items added by client script aren’t persisted in ViewState, so there is no server side value. This had me stumped for a few minutes. At first I tried replacing the ASP.NET validators with a client side form submit handler, but it was going to be tricky to get the validation messages into the ValidationSummary. I thought about jQuery Validation for a moment, but I didn’t want to have to deal with two different validation frameworks (ASP.NET and jQuery).

Then it finally dawned on me. It was so simple. All that I needed to do was replace the RequiredFieldValidator with a CustomValidator and then implement both the client and server side validation methods. I also replaced the DropDownList control with a normal HTML select tag since I couldn’t access its selected value from server side code.

<select id="ddlCounty" name="ddlCounty">
    <option value="">[Select a State]</option>
</select>
<asp:CustomValidator ID="valCountyRequired" runat="server"
    ClientValidationFunction="validateCounty"
    OnServerValidate="valCountyRequired_ServerValidate" Display="Dynamic"
    ErrorMessage="The County is required." ValidateEmptyText="true" />

Here’s the client side handler

function validateCounty(sender, args)
{
    args.IsValid = validateRequired(countySelector);
}

function validateRequired(selector)
{
    var value = $(selector).val();
    return !String.isNullOrEmpty(value);
}

And the server side handler

protected void valCountyRequired_ServerValidate(object source, ServerValidateEventArgs args)
{
    args.IsValid = ValidateRequired(Request.Form["ddlCounty"]);
}

private static bool ValidateRequired(string value)
{
    return !String.IsNullOrEmpty(value);
}

Notice that I’m not using args.Value in either handler. That’s because I didn’t set ControlToValidate on the CustomValidator since without ViewState, it won’t be able to get the value. That’s why I’m using Request.Form[“ddlCounty”] in the server side handler to get the value.

Hope this helps!

2010
05.31

There seems to be a lack of up to date themes in the BlogEngine.NET community lately. Initially, there were a ton of great themes available from very talented designers. But the themes haven’t kept up with new releases of the blog engine. I used a few themes here on older versions, but alas, it seems that all of the cool themes look bad in BlogEngine.NET 1.5+ so now I’m back to the Standard theme.

I’m a much better developer than designer, but I was able to update the Scruffy theme for version 1.6.1.0, which includes reCAPTCHA support. All that I had to do was replace CommentView.ascx and the comment related CSS in style.css with what’s in the Standard theme. I did this for my wife since she can’t blog without Scruffy. If I find the time, I may try updating a few more. I’m open to suggestions.

Scruffy
Title : Scruffy
Caption :
File name : Scruffy.zip
Size : 281 kB
2010
04.24

I switched my web host from GoDaddy to WinHost this week. I only I ran into two issues. The first was how WinHost handles sub-domains. GoDaddy’s sub-domains point to a sub folder of the same name, e.g. http://sub.example.com points to \sub. You can then access sub-domain content from http://sub.example.com/abc or http://sub.example.com/sub/abc. There is no way that I could find to enforce either /abc or /sub/abc and often after arriving to the site via /abc you would be linked to /sub/abc.

WinHost doesn’t automatically redirect your sub-domains; they leave that up to the customer. They suggest using custom code on the default page to redirect based on the server name Request variable. This would be fine if users always went to that page first, but what if you already have links to http://sub.example.com/somepage.aspx? They will be broken, of course!

WinHost allows you to manage IIS7 through the Remote IIS Manager and includes the URL Rewrite Module, so I thought I’d give that a try. My first attempt was to rewrite http://john.rummell.info to /john using the information provided by Ben Powell on his blog:

<rule name="john.rummell.info" stopProcessing="true">
  <match url="(.*)" />
  <conditions>
    <add input="{HTTP_HOST}" pattern="^john.rummell.info$" />
  </conditions>
  <action type="Rewrite" url="john/{R:1}" />
</rule>

This works if you use only absolute paths, but it broke all of my relative css links and form actions. This is because even though you never see /john in the url, Request.ApplicationPath is still /john/somepage.aspx. I decided that this wasn’t right path to take. Then I tried a similar rule that would redirect instead of rewrite:

<rule name="john.rummell.info" stopProcessing="true">
    <match url="(.*)" />
    <conditions logicalGrouping="MatchAll">
        <add input="{HTTP_HOST}" pattern="^john.rummell.info$" />
        <add input="{REQUEST_URI}" negate="true" pattern="/john" />
    </conditions>
    <action type="Redirect" url="http://john.rummell.info/john/{R:1}" />
</rule>

This will redirect all requests from http://john.rummell.info to http://john.rummell.info/john. The first input is the same as the rewrite rule. The second input makes sure that the path doesn’t already start with /john. This prevents it from redirecting infinitely (/john/john/john/john etc). Its not ideal since the sub domain appears in the url twice, but at least its guaranteed to always be that way, unlike GoDaddy.

The other issue I had was that using the Facebook Developer Toolkit and reCaptcha both threw a SecurityException. This was an easy fix once I realized that I could change the trust level in IIS. Changing it to High fixed the problem. So far I’ve been very pleased!

So far I’ve been very pleased with WinHost. They have reasonable pricing, their support team has been quick to respond, and they allow you to manage your site with IIS Manager and SQL Server Management Studio. Good bye GoDaddy!

2010
01.26

I’ve been using Google Chrome a lot more for casual browsing lately. I continue to use Firefox with Firebug for web development tasks, but for social networking and email I use Chrome. Perhaps its because I have loaded down Firefox with too many extensions, but I can’t get over how much faster Chrome is. And the latest beta (version 4) now supports extensions. So far I’ve added five extensions and I haven’t noticed a difference in speed.

My Extensions

One Number by megazzt

Check GMail, Google Reader, Google Voice, and Google Wave. Four sources, one number.
https://chrome.google.com/extensions/detail/cfkohgkpafhkpdcnfadadcibfboapggi 

Send from Gmail (by Google) by extensions.for.gapps

Makes Gmail your default email application and provides a button to compose a Gmail message to quickly share a link via email
https://chrome.google.com/extensions/detail/pgphcomnlaojlmmcjmiddhdapjpbgeoc

Shareaholic for Google Chrome by meattle

Share, save or email any web page with your friends right from the page you are on using Twitter, Facebook, GMail, and many more!
https://chrome.google.com/extensions/detail/kbmipnjdeifmobkhgogdnomkihhgojep

Xmarks Bookmarks Sync by Xmarks Inc.

Backup and sync your bookmarks across computers and browsers. Xmarks is also available for Firefox, Safari and IE.
https://chrome.google.com/extensions/detail/ajpgkpeckebdhofmmjfgcjjiiejpodla

RSS Subscription Extension (by Google) by finnur@chromium.org

Adds one-click subscription to your toolbar.
https://chrome.google.com/extensions/detail/nlbjncdgjeocebhnmkbbbdekmmmcbfjd

2009
12.23

I’ve been wanting to create a Facebook app for a while now, but I didn’t have time or a need to fill. I took some time off over the last week and I was able to come up with both. I’m avid PC gamer and most of my games are on Valve’s Steam platform. Steam has achievements for some of its biggest newer games like Left 4 Dead and Team Fortress 2. They are much like the Xbox 360 and PS3 achievements. But unlike the Xbox and PS3, there wasn’t a Facebook app that notifies your friends with your latest achievements. There was an app that worked great for a while back, but its now broken (and still using the steamachievements url suffix!).

I’ve created an app at http://www.facebook.com/apps/application.php?id=211407042025.

I first tried an FBML Canvas app built with ASP.NET MVC and the MS Facebook SDK, but I couldn’t find a complete working example. Then I tried FBML with ASP.NET WebForms (traditional ASP.NET) but testing was super annoying since it had to be run in the context of Facebook and I couldn’t get FBJS Ajax to work. Then finally, I tried an IFrame Canvas app built with ASP.NET WebForms. I could now test easier and use jQuery instead of FBJS! For more information, see this wiki topic on IFrame vs. FBML Canvas apps.

Now that I was in familiar territory, I threw a wrench in the mix by using WCF web services instead of asmx. Not that WCF is better/worse than asmx, I had just never used them before. I had some help from Rick Strahl’s blog post called jQuery AJAX calls to a WCF REST Service. This is was no easy task for me, so I’ll list all of the details here in case anyone else is struggling. First, here is the service interface:

[ServiceContract]
public interface IAchievementService
{
    [OperationContract]
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    List<Achievement> GetAchievements(string steamUserId, int gameId);

    [OperationContract]
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    List<Game> GetGames();

    [OperationContract]
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    bool UpdateAchievements(string steamUserId);

    [OperationContract]
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    bool UpdateSteamUserId(long facebookUserId, string steamUserId);

    [OperationContract]
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    bool PublishLatestAchievements(long facebookUserId, string steamUserId);
}

Note the WCF attributes: ServiceContract and OperationContract. These kind of correspond to WebService and WebMethod in an asmx web service. I say kind of because a WCF service doesn’t have to be a web service, but for this example it is. The WebInvoke attribute tells ASP.NET how the client will interact with the service. The method is POST, and both the request and the response will be json. The WrappedRequest BodyStyle is what allows you to send a json JavaScript object to a service method that gets de-serialized into parameters. If you were to omit this, you’d have to create a class for your parameters and change the method signature to accept one instance of that class. Another thing to note is that I’m returning List<T> instead of IEnumerable<T>. This is because there seems to be a serialization bug with WCF and IEnumerable<T>.

The implementation of IAchievementService is very straight forward. It is simply hands all of the heavy lifting to a manager class that uses LINQ to SQL to communicate with the database. The next part is the WCF configuration in web.config.

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="SteamAchievements.Services.AchievementServiceBehavior" name="SteamAchievements.Services.AchievementService">
        <endpoint address="" binding="webHttpBinding" contract="SteamAchievements.Services.IAchievementService" behaviorConfiguration="SteamAchievements.Services.AchievementServiceBehavior" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SteamAchievements.Services.AchievementServiceBehavior">
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="True" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="SteamAchievements.Services.AchievementServiceBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false">
      <baseAddressPrefixFilters>
        <add prefix="http://www.rummell.info/" />
      </baseAddressPrefixFilters>
    </serviceHostingEnvironment>
  </system.serviceModel>

This is fairly standard except for a few things. I’m using the webHttp endpoint behavior to enable AJAX and I also had to add a baseAddressPrefixFilter to get it working on my web host.

Finally, here is the JavaScript that calls the WCF service methods:

var parameters = { "steamUserId": steamUserId, "gameId": gameId };
callAjax("GetAchievements", parameters, ondone);

function callAjax(method, query, ondone)
{
    var onerror = function(m)
    {
        $("#log").text(m.Message).show();
    };

    $.ajax({
        url: _serviceBase + method,
        data: JSON.stringify(query),
        type: "POST",
        processData: true,
        contentType: "application/json",
        timeout: 10000,
        dataType: "json",
        success: ondone,
        error: function(xhr)
        {
            if (!onerror)
            {
                return;
            }

            if (xhr.responseText)
            {
                try
                {
                    var err = JSON.parse(xhr.responseText);
                    if (err)
                    {
                        onerror(err);
                    }
                    else
                    {
                        onerror({ Message: "Unknown server error." });
                    }
                }
                catch (e)
                {
                    onerror({ Message: e.toString() });
                }
            }
            return;
        }
    });
}

This is basically the same snippet from Rick Strahl (see link above) that I’ve modified slightly to fit my application.

One final note on using the Facebook API from the context of a web service. The PublishLatestAchievements service method uses the API to publish achievements to the user’s profile. Since its outside the scope of a page, you can’t use Master.Api. To get around this, you’ll need to create an instance of CanvasSession:

string appKey = WebConfigurationManager.AppSettings["APIKey"];
string appSecret = WebConfigurationManager.AppSettings["Secret"];
List<Enums.ExtendedPermissions> permissions =
    new List<Enums.ExtendedPermissions> {Enums.ExtendedPermissions.publish_stream};
CanvasSession session = new IFrameCanvasSession(appKey, appSecret, permissions, false);
Api api = new Api(session);

Then you can publish to the stream:

api.Stream.Publish(description, attachment, links, null, facebookUserId);

Most samples don’t include the uid parameter (facebookUserId), but its required here since HttpSession is unavailable.

If you’d like to see more, take a look at the Google Code project.

2009
11.18

Update: SQL Source Control was released a while back! See my article on Simple-Talk for more information.

At work we maintain a few SQL Server Management Studio (SSMS) solutions for our SQL views, stored procedures and functions. We also use TortoiseSVN for source control. Unfortunately, there are no SVN add-ins for SSMS and the ones for Visual Studio don’t work (VisualSVN, AnkhSVN). Its a bit frustrating that SSMS is built on the same technology as Visual Studio, but lacks so many of the features that I’ve grown accustomed to, such as the Add-in Manager.

Red Gate, however, is currently working on a add-in called SQL Source Control with a planned release in 2010. But what to do until then? Well, there is one officially supported point of extensibility in SSMS: External Tools. Here are a few that I’ve been using with TortoiseSVN lately:

Title: SVN Commit
Command: C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
Arguments: /Command:commit /path:”$(SolutionDir)
Initial directory: $(SolutionDir)

Title: SVN Update
Command: C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
Arguments: /Command:update /path:”$(SolutionDir)”
Initial directory: $(SolutionDir)

Title: SVN Log (Solution)
Command: C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
Arguments: /Command:log /path:”$(SolutionDir)”
Initial directory: $(SolutionDir)

Title: SVN Log (Current Item)
Command: C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
Arguments: /Command:log /path:”$(ItemFileName)$(ItemExt)”
Initial directory: $(ItemDir)

Title: SVN Diff
Command: C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
Arguments: /Command:diff /path:”$(ItemFileName)$(ItemExt)”
Initial directory: $(ItemDir)

2009
09.17

I’m currently working on my first ASP.NET MVC project. Naturally, I’m writing a good number of unit tests. I ran into a problem tonight with mocking Controller.User. Thankfully, someone at Stack Overflow had already asked a question about this. I took Bruno Reis’ answer:

var principal = new Moq.Mock<IPrincipal>();
// ... mock IPrincipal as you wish

var httpContext = new Moq.Mock<HttpContextBase>();
httpContext.Setup(x => x.User).Returns(principal.Object);
// ... mock other httpContext's properties, methods, as needed

var reqContext = new RequestContext(httpContext.Object, new RouteData());

// now create the controller:
var controller = new MyController();
controller.ControllerContext =
    new ControllerContext(reqContext, controller);

and I rewrote it using NUnit.Mocks and wrapped it into an implementation of HttpContextBase:

/// <summary>
/// A mock <see cref="HttpContextBase"/> that implements <see cref="HttpContextBase.User"/>.
/// </summary>
public class MockHttpContext : HttpContextBase
{
    private IPrincipal _user;

    public MockHttpContext()
    {
        DynamicMock identity = new DynamicMock(typeof (IIdentity));
        identity.ExpectAndReturn("get_Name", "testUser");

        DynamicMock user = new DynamicMock(typeof (IPrincipal));
        user.ExpectAndReturn("get_Identity", identity.MockInstance);

        _user = (IPrincipal) user.MockInstance;
    }

    public override IPrincipal User
    {
        get { return _user; }
        set { _user = value; }
    }
}

Now mocking Controller.User is as easy as this:

// create an instance of RequestContext using MockHttpContext.
RequestContext requestContext =
    new RequestContext(new MockHttpContext(), new RouteData());

// initialize the controller's ControllerContext
_controller.ControllerContext = new ControllerContext(requestContext, _controller);
2009
08.16

I recently had to add a CSS class to all validators in an ASP.NET web application.  I started with the theme’s skin file:

<asp:CompareValidator runat="server"
    CssClass="error" />
<asp:CustomValidator runat="server"
    CssClass="error" />
<asp:RequiredFieldValidator runat="server"
    CssClass="error" />
<belCommon:ZipCodeValidator runat="server"
    CssClass="error" />
<belCommon:PhoneNumberValidator runat="server"
    CssClass="error" />

But what if I decide to use another validator down the road? I would have to remember to add it to the skin. Knowing that I was bound to forget, I sought out another method. After doing some digging, I found that ASP.NET generates a JavaScript variable called Page_Validators. This is an array of all the validator span elements on the current page. Now that I have access to the spans, I could write a script in the site’s Master Page to apply the class:

if (Page_Validators != null)
{
    for (i = 0; i < Page_Validators.length; i++)
    {
        Page_Validators[i].className = "error";
    }
}

To have it run when the page is loaded, I added it as an Sys.Application.init handler:

Sys.Application.add_init(function(sender, args)
{
    if (Page_Validators != null)
    {
        for (i = 0; i < Page_Validators.length; i++)
        {
            Page_Validators[i].className = "error";
        }
    }
});

You could also use jQuery’s document.ready handler:

$(document).ready(function()
{
    if (Page_Validators != null)
    {
        for (i = 0; i < Page_Validators.length; i++)
        {
            Page_Validators[i].className = "error";
        }
    }
});
2009
08.12

Since my last post, I’ve completely rethought and re-implemented my take on xVal for WebForms. If you’re not familiar with xVal, stop now and read the tutorial. Now that you’re back, lets talk about xVal and WebForms.

Model

This is the model we’ll be using (you should recognize it from the xVal tutorial):

public class Booking
{
    [Required]
    [StringLength(15)]
    public string ClientName { get; set; }

    [Range(1, 20)]
    public int NumberOfGuests { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime ArrivalDate { get; set; }
}

Form

And here is the form:

<asp:ValidationSummary ID="valSummary" runat="server" />
<label for="txtClientName">
    Your name:</label>
<asp:TextBox ID="txtClientName" runat="server" />
<label for="txtNumberOfGuests">
    Number of guests:</label>
<asp:TextBox ID="txtNumberOfGuests" runat="server" />
<label for="txtArrivalDate">
    Arrival date:</label>
<asp:TextBox ID="txtArrivalDate" runat="server" />
<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click" />

ModelValidator

My first try at validation was adding a validator control for each input field. After playing with it a bit, I decided that it would be better to have one validator for the entire model. This control defines the model’s type (ModelType), and then maps each property (PropertyName) to an input control (ControlToValidate).

<val:ModelValidator ID="valBooking" runat="server" ModelType="xVal.WebForms.Demo.Booking, xVal.WebForms.Demo">
    <ModelProperties>
        <val:ModelProperty PropertyName="ClientName" ControlToValidate="txtClientName" />
        <val:ModelProperty PropertyName="NumberOfGuests" ControlToValidate="txtNumberOfGuests" />
        <val:ModelProperty PropertyName="ArrivalDate" ControlToValidate="txtArrivalDate" />
    </ModelProperties>
</val:ModelValidator>

ControlToValidate

The biggest challenge was figuring out how to reference the input controls by given ID instead of by the elementPrefix + PropertyName convention. In other words, MVC xVal assumes that your ClientName input control ID is booking.ClientName, where booking is the elementPrefix and ClientName is the name of the property. This doesn’t work out so well with web forms and generated IDs. I got around this with the ModelProperties collection of ModelValidator. Then I updated the json formatted rule script to include each property’s control ID.

Complete Source and Demo

Get the complete source (with Bookings demo) at the xVal.WebForms project page on CodePlex.