Umbraco and JSON-RPC

We recently created a companion iPhone application for a website we released late last year, which needed to retrieve content from the website.  Rather than just retrieve pages of information as html, we wanted to display lists of content, and we also wanted to cache the content on the device for offline viewing.  To do this, it was decided we would use JSON as the transport as it is lightweight.

On the client side, we used the JSON-Framework Cocoa based library created by Stig Brautaset.  On the server side we used JayRock JSON-RPC library for dotNet. Oh, and the website was based on the Umbraco CMS.

Background on JSON-RPC

The JSON-RPC specification is fairly straightforward - it consists of a request, a response, and a notification.  The request sent to the server has 3 parameters as follows:

  • Method Name
  • Parameters, and
  • Id - This is used to associate the request with it's respective reply, and provided it is unique can be used to quarantee that the calling code receives the correct reply for the request - very important if you are sending multiple requests asynchronously.

The response from the server also has 3 parameters:

  • Result object - the result in JSON notation
  • Error object - if an error is thrown by the called method, the details will be contained in this object, including any exceptions thrown, which is very useful for debugging
  • Id - this is the same as the id passed in with the Request.

Creating a JSON-RPC Service

Using the JayRock library, this is all encapsulated, and we can just go about writing our Service Methods like so:

  1. In Visual Studio, or your favourite editor, start by creating a Generic Handler (.ashx type).
  2. Change the class definition so that it derives from the JsonRpcHandler class instead
  3. Create your Method declarations as per normal, but decorate them with the JsonRpcMethod Attribute.

An example is as follows (this is the base class we have created for authenticating user requests to the Umbraco backend):

using System.Web.Security;
using Jayrock.JsonRpc;
using Jayrock.JsonRpc.Web;
using System;
using System.Web;

namespace refactored.WebServices.json
{
    /// <summary>
    /// Provides a base class for JSON-RPC Services and handles authentication.
    /// </summary>
    public class BaseJSONService : JsonRpcHandler
    {
        protected string AuthenticationToken { get; set; }

        [JsonRpcMethod("AuthenticateMember")]
        public string AuthenticateMember(string username, string password)
        {
          ..
..
} [JsonRpcMethod("AuthenticationExpired")] public bool AuthenticationExpired(string authToken) { return (HttpRuntime.Cache[authToken] == null); }
 protected bool IsUserValid() { return (!string.IsNullOrEmpty(AuthenticationToken) && !AuthenticationExpired(AuthenticationToken)); } protected string GetUserName() { if (IsUserValid()) return HttpRuntime.Cache[AuthenticationToken].ToString(); else return string.Empty; } protected MembershipUser GetMember() { if (IsUserValid()) return Membership.GetUser(GetUserName()); else return null; } private bool ValidateUser(string username, string password) { return Membership.ValidateUser(username, password); } } }



Once you compile and load the dll into Umbraco, along with the associated ashx file, you can test the webservice in your favourite web browser:

JSON-RPC Service Discovery

User Authentication and Web Services

One of the features of the iPhone Application was that it allowed users to provide feedback on the content, as well as create "favourite" lists.  This required that users be able to authenticate with the website in order to make changes to their profile and update the lists on the website.

Because Web Services cannot store the users session, we need to provide some way of authenticating the user each call. In order to do this, an AuthenticateMember(string username, string password) method was created on the Service that returned an authentication token to be used in subsequent calls:

        [JsonRpcMethod("AuthenticateMember")]
        public string AuthenticateMember(string username, string password)
        {
            if (string.IsNullOrEmpty(username) || !ValidateUser(username, password))
                throw new Exception("Your Username or Password is incorrect");

            // Create and store the AuthenticatedToken before returning it
            AuthenticationToken = Guid.NewGuid().ToString();
            HttpRuntime.Cache.Add(
                   AuthenticationToken,
                   username,
                  null,
                  System.Web.Caching.Cache.NoAbsoluteExpiration,
                   TimeSpan.FromMinutes(60),
                  System.Web.Caching.CacheItemPriority.NotRemovable,
                  null);

            return AuthenticationToken;
        }

Then, on subsequent calls, we just pass the authentication token in to be validated against the cached version.

Lets test out the method:

JSON-RPC Service Testing with error

Notice wth this screen shot that the returned result was actually the error - it even includes the Exception details along with the erorr message.

Fixing up the parameters results in the following:

JSON-RPC Service Testing with expected result

Note the returned value - this is the authentication token generated by the server - we use this when calling subsequent methods that require validation.

Well, that's it for now, hopefully you can see from this how we can use JSON-RPC to retrieve information from a website and consume it in other applications, or even using AJAX.  Not only can we retrieve information, we can also create new content and otherwise manipulate information on a website.

Embedded YouTube Videos and iPad Rotation

While working on the iPhone/iPad application for the ChickenChannel website, I needed to have the embedded YouTube video resize dynamically to fit properly and in proportion across the screen of the device.  Moreover, the iPad allows for rotating the view, and we needed the video to resize to accomodate both orientations dynamically as you rotate the device.

The Chicken Channel is written in Umbraco, and the iOS application takes advantage of that by presenting existing pages with a customised template.  So while you might look at a recipe on the website and see the nice embedded Youtube video (hidden behind a banner image that prompts you to click it to start playing the video - this was covered in a post last November here.), the iPhone and iPad views are somewhat different.

Step 1: Make the video automatically fill the width of the screen.

This was quite easy:  all I needed to do was clear the width and height the enclosing div and the object tags and set the width to 100% on the embed tag.  I could have set the height as well, but given that there are 3 possible width with the devices, and I'm "veiling" the page until it's loaded anyway, I didn't see any point.  This displays a rather wide but short video on the iPhone:

<div class="youTubePlayer">
  <object class="youTubePlayer">
    <param name="movie" value="http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0" />
    <param name="allowFullScreen" value="true" />
    <param name="allowscriptaccess" value="always" />
    <embed class="youTubePlayer" src="http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="100%" />
  </object>
</div>



Step 2: Use Javascript to add the aspect ratio back in (set the Height).

Using jQuery this is a really simple excercise:  We simply get the width of the window, and, because all our videos are in 16:9 aspect ratio, we use that to derive the height before applying it to the relevant tags.  (Notice the embed and object tags also have the class="youTubePlayer" attribute above?) - you'll also need to add the jQuery core library to your page...

  <script type="text/javascript">
    $(document).ready(function() {
      var newHeight = $(window).width()*9/16;
      if (newHeight > 500)
          newHeight = 500;
      $('.youTubePlayer').attr("height", newHeight);
    });
  </script>      



Right, after testing a little, we notice that the page renders, then the video gets lengthened to the correct ratio and all is good in the world.  However, when rotating the iPad application, the videos dimensions aren't resized along with the rest of the content.  Actually, the video's width is resized, but the height stays where we left it.

Step 3: Use the Resize event to adjust the aspect ratio on Rotation

The final step to this process was to take advantage of the UIWebView's resize javascript event to perform the resize again:

  <script type="text/javascript">
    $(window).resize(function() {
      var newHeight = $(window).width()*9/16;
      var oldHeight = $('div.youTubePlayer:first').attr("height");
      if (newHeight > 500)
          newHeight = 500;
      if (newHeight = oldHeight)
          return;
      $('.youTubePlayer').attr("height", newHeight);
    });
</script>



After a little more testing (ie, me madly waving the iPad around in the air and doing acrobatic contortions in the process) we have established that the video now resizes gracefully when the device is rotated to Portrait or Landscape mode.  All is better in the world.

Side note: While playing around with this, I had a javascript alert(newHeight); line in the resizing code.  on my iPad with the shiny new iOS 4.3.1 installed just last night, this promptly caused the application to crash.  I've submitted a bug report to Apple, and we'll see how it goes.

As always, comments and suggestions are always welcome.

I love NuGet!

It's official.  NuGet is now my all-time favourite Visual Studio Add-on.

The other day I was trying to upload a simple website to my hosting platform, but was having trouble with the dependencies.  As it happens, the hosting environment doesn't have MVC3 installed, or SqlServer Ce 4 for that matter.  MVC wasn't much of a problem, but the Ce database server on the other hand - well.  That's a different story.

Since the website was very simple in functionality - it provides a form that users can fill out to be notified of developments for a piece of real-estate in Melbourne's Yarra Valley region - and we only had a day to build it, I thought the combination of MVC3 with SqlServer Ce (we used the Code-First data model approach, which worked out really well) would be a no-brainer.

That was until I proceeded to upload the database, and started having to hunt around for all the necessary dll's.  It wasn't enough to just mark the dependencies so they would be copied to the output directory, I actually had to go and manually hunt around for additional dll's as well.

Enter NuGet.

Nuget allows you to download and automatically set up additional packages and libraries with ease.  It takes care of the dependencies, and updates your configuration files as well so you can just start using the new functionality without having to worry about web.config settings, for example.

So.  I added the package for EFCodeFirst.SqlServerCompact to the project, which resulted in all the dependencies being added as well in one easy step, and then all I had to do was make a few tweaks here and there and upload the changes.  Too easy!

Thank you, NuGet, you've just made my life that much easier.

Embedded Youtube Videos with Placeholder Images

One of the requests I've had for a website I'm currently working on is to provide a placeholder image for an embedded Youtube Video.  Essentially, the client wants to display the image until someone clicks on it, then replace it with the video.  So here's how I did it with the new uTube package for Umbraco, but the same technique can be used for any web page...

With this method, I'm not trying to play the video in a separate window, or resize anything, I'm just wanting to have the video take the place of the image on the website.  This makes things a little easier, as I can render both the video and the image in separate div's and use CSS to lay one on top of the other using z-index and position elements.  In addition, I can use some fairly straight forward jquery javascript to hide one element and show the other.

Rendering a Flash Video player with an Image overlay

Here's a sample of the HTML including embedded javascript:

    <div id="mainFeature">
        <div id="mediaVideo" style="visibility: hidden">
            <div class="youTubePlayer" style="height: 433px; width: 680px;">
                <div id="ytPlayerAPI-rldN0jSBbZQ">
                    <p>
                        You need Flash version 8 and JS enabled to view the video</p>
                </div>
                <script type="text/javascript">
                    //SWF Embedd
                    var flashVars = { video_id: "rldN0jSBbZQ", playerapiid: "ytPlayerAPI-rldN0jSBbZQ", allowFullScreen: "true" }
                    var params = { allowScriptAccess: "always", wmode: "transparent", allowFullScreen: "true" };
                    var atts = { id: "ytPlayerAPI-rldN0jSBbZQ" };
                    swfobject.embedSWF("http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0&enablejsapi=1&version=3&playerapiid=ytPlayerAPI-rldN0jSBbZQ", "ytPlayerAPI-rldN0jSBbZQ", "680", "433", "8", null, flashVars, params, atts);
                </script>
            </div>
        </div>
        <div id="mediaImage">
            <a href="/recipes/crispy-chinese-chicken-with-sichuan-salt-pepper" title="Crispy Chinese Chicken with Sichuan Salt & Pepper">
                <img src="/media/2245/crispy_chinese_chicken_01.jpg" title="Crispy Chinese Chicken with Sichuan Salt & Pepper"
                    alt="Crispy Chinese Chicken with Sichuan Salt & Pepper" /></a></div>
        <script type="text/javascript">

            $(document).ready(function () {
                $('div#mediaImage a').click(function () {
                    $('div#mediaVideo').css('visibility', 'visible');
                    $('div#mediaImage').fadeOut(1000);
                    return false;
                });
            });
      
        </script>
    </div>

And the corresponding CSS to initially lay out the elements:

#mediaVideo, #mediaImage {
  position: absolute;
  left: 0;
  top: 0;
}

#mediaImage {
  z-index: 10;
}

Notice that in this example I'm using the SWFObject library to render the flash player. For some reason I haven't fully explored yet, if you render the flash object using object tags, the flash player is rendered on top of the mediaImage instead of obeying the CSS rules and rendering the image div on top as it should.

The result? When the user clicks on the image, it fades out and is replaced by the flash object.

Displaying embedded Youtube Videos on an iPhone/iPad

The thing about iPhone and iPad is it doesn't yet support Flash.  However, if you use the object tags approach, the device is smart enough to recognise the Youtube player and use the built-in YouTube app in it's place.  This is a pretty cool feature, but doesn't appear to work if you use the SWFObject library approach above.  Only problem is, although iPhones etc support javascript, and will honour the code above, all you see is the following message:

Rendering Youtube on iPhone with SWFObject libraryRendering a Youtube embedded video with the SWFObject on iPhone...

And using the <object> tag approach:

Rendering Youtube on iPhone with object TagsRendering a Youtube embedded video with object tags on iPhone...

Clicking on the video launches the Youtube App.  The iPad plays the video in place in the website using the Youtube app, which is pretty cool, only launching a separate window if you view it in fullscreen mode.

The code for rendering the Youtube with object tags is as follows:

            <div class="youTubePlayer" style="height: 433px; width: 680px;">
                <object width="680" height="433">
                    <param name="movie" value="http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0" />
                    <param name="allowFullScreen" value="true" />
                    <param name="allowscriptaccess" value="always" />
                    <embed src="http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0" type="application/x-shockwave-flash"
                        allowscriptaccess="always" allowfullscreen="true" width="680" height="433" /></object></div>

The best of both worlds: Supporting iPhone and rendering placeholder Images...

It's a compromise, but since we can't get both apple devices and standard browsers to render the same thing, we need to use the HTTP_USER_AGENT string to conditionally render the different approaches.

Umbraco and the uTube plugin...

For those of us utilising the excellent new uTube plugin with Umbraco, the following XSLT code should do the trick - note that you'll have to render the Chrome player if you want Full screen mode, as the chromeless player doesn't support it.  See my earlier post about this: Enabling Alternate Media with the uTube Umbraco Package

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
  <!ENTITY nbsp " ">
]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon"
  xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes"
  xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath"
  xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions"
  xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings"
  xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:yt="http://gdata.youtube.com/schemas/2007"
  xmlns:media="http://search.yahoo.com/mrss/"
  xmlns:uTube.XSLT="urn:uTube.XSLT"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath
  Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets atom yt media uTube.XSLT"> <xsl:output method="xml" omit-xml-declaration="yes"/> <xsl:param name="currentPage"/> <xsl:template match="/"> <!-- YouTube URL or VideoID --> <xsl:variable name="uTubeVideo" select="/macro/uTubeVideo" /> <xsl:variable name="allowFullScreen" select="/macro/allowFullScreen" /> <xsl:variable name="allowRelatedVideos" select="/macro/allowRelatedVideos" /> <xsl:variable name="jsPlayerApiId" select="/macro/jsPlayerApiId" /> <!-- Don't do anything unless we have a value in uTubeVideo --> <xsl:if test="/macro/uTubeVideo"> <xsl:variable name="uTubeVideoID" select="uTube.XSLT:GetVideoId($uTubeVideo)" /> <xsl:variable name="uTubeXML" select="uTube.XSLT:GetVideoData($uTubeVideoID, 60)"/> <xsl:variable name="uTubeRatio" select="uTube.XSLT:GetAspectRatio($uTubeVideoID)"/> <!-- Video Width --> <xsl:variable name="uTubeWidth"> <xsl:choose> <!-- If the Width is not set, then check if Height is available --> <xsl:when test="not(/macro/uTubeWidth) and /macro/uTubeHeight"> <!-- Use the Height to get the Width --> <xsl:value-of select="uTube.XSLT:GetVideoWidth(/macro/uTubeHeight, $uTubeRatio)" /> </xsl:when> <!-- If the Width is set, use it! --> <xsl:when test="/macro/uTubeWidth"> <xsl:value-of select="/macro/uTubeWidth" /> </xsl:when> <!-- Otherwise fall back on a default value --> <xsl:otherwise> <xsl:value-of select="number(480)" /> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- Video Height --> <xsl:variable name="uTubeHeight"> <xsl:choose> <!-- If the Height is not set --> <xsl:when test="not(/macro/uTubeHeight)"> <!-- Then use the Width to get the Height --> <xsl:value-of select="uTube.XSLT:GetVideoHeight($uTubeWidth, $uTubeRatio)" /> </xsl:when> <xsl:otherwise> <!-- Otherwise use the user-defined value --> <xsl:value-of select="/macro/uTubeHeight" /> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="allowFullScreenText"> <xsl:choose> <xsl:when test="$allowFullScreen = 1">true</xsl:when> <xsl:otherwise>false</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="uTubeRel"> <xsl:choose> <xsl:when test="$allowRelatedVideos = 1">&rel=1</xsl:when> <xsl:otherwise>&rel=0</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="enableJSApi"> <xsl:if test="$jsPlayerApiId != ''">&enablejsapi=1&playerapiid=<xsl:value-of select="$jsPlayerApiId" /></xsl:if> </xsl:variable> <div class="youTubePlayer" style="height:{$uTubeHeight}px; width:{$uTubeWidth}px;"> <xsl:choose> <xsl:when test="uTube.XSLT:AllowEmbed($uTubeVideoID) = true()"> <xsl:choose> <xsl:when test="contains(umbraco.library:RequestServerVariables('HTTP_USER_AGENT'), 'iPad') or contains(umbraco.library:RequestServerVariables('HTTP_USER_AGENT'), 'iPhone')"> <object width="{$uTubeWidth}" height="{$uTubeHeight}"> <param name="movie"
                   value="http://www.youtube.com/v/{$uTubeVideoID}?fs={$allowFullScreen}{$uTubeRel}{$enableJSApi}"></param> <param name="allowFullScreen" value="{$allowFullScreenText}"></param> <param name="allowscriptaccess" value="always"></param> <embed src="http://www.youtube.com/v/{$uTubeVideoID}?fs={$allowFullScreen}{$uTubeRel}{$enableJSApi}" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="{$allowFullScreenText}" width="{$uTubeWidth}" height="{$uTubeHeight}"></embed> </object> </xsl:when> <xsl:otherwise> <!-- Video is allowed to be embedded --> <div id="{concat('ytPlayerAPI-',$uTubeVideoID)}"> <p>You need Flash version 8 and JS enabled to view the video</p> </div> <!-- SWFObject call to Embed Chromless player --> <script type="text/javascript"> //SWF Embedd var flashVars = {video_id : "<xsl:value-of select="$uTubeVideoID"/>",
                                            playerapiid: "<xsl:value-of select="concat('ytPlayerAPI-',$uTubeVideoID)" />",
                                            allowFullScreen: "<xsl:value-of select="$allowFullScreenText"/>" } var params = {allowScriptAccess: "always", wmode: "transparent",
                                            allowFullScreen: "<xsl:value-of select="$allowFullScreenText"/>" }; var atts = { id: "<xsl:value-of select="concat('ytPlayerAPI-',$uTubeVideoID)" />" }; swfobject.embedSWF("http://www.youtube.com/v/<xsl:value-of
                                                    select="$uTubeVideoID" />?fs=<xsl:value-of
                                                    select="$allowFullScreen" /><xsl:value-of
                                                    select="$uTubeRel" />&enablejsapi=1&version=3&playerapiid=<xsl:value-of
                                                    select="concat('ytPlayerAPI-',$uTubeVideoID)" />","<xsl:value-of
                                                    select="concat('ytPlayerAPI-',$uTubeVideoID)" />", "<xsl:value-of
                                                    select="$uTubeWidth" />", "<xsl:value-of
                                                    select="$uTubeHeight"/>", "8", null, flashVars, params, atts); </script> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <!-- Video Not Allowed to be embedded --> <p>This video does not allow it to be embedded.</p> </xsl:otherwise> </xsl:choose> </div> </xsl:if> </xsl:template> </xsl:stylesheet>

Any questions, comments or suggestions, feel free to leave a comment...

Enjoy!

Enabling Alternate Media with the uTube Umbraco Package

The project I'm currently working on requires embedding youtube videos in the page. For that I've settled on the excellent uTube Umbraco package found over here (or visit the Umbraco package page on our.umbraco.org...).

Out of the box, the uTube package provides some excellent functionality - you can upload videos directly from the Media repository, skinnable player as well as the full youtube player, ready made macros for including content...

However, as the site doesn't yet have videos for all the articles, and there's no guarantee that the articles will do so in the future, I wanted to be able to provide an alternative image instead of the video if it was missing.

In addition, some videos were already uploaded to youtube, and uTube currently doesn't have an import datatype for the Media section, so I wanted to be able to provide another alternative to the user to specify a youtube url as well as be able to choose from the Media items.

So I wrapped up the uTube macro's in my own "wrapper" macro:

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:CWS.Twitter="urn:CWS.Twitter" xmlns:Locator="urn:Locator" xmlns:tagsLib="urn:tagsLib" xmlns:BlogLibrary="urn:BlogLibrary"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets CWS.Twitter Locator tagsLib BlogLibrary ">


<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>
    <xsl:variable name="mainFeatureId" select="/macro/mainFeatureId"/>
    <xsl:variable name="mainFeature" select="umbraco.library:GetXmlNodeById($mainFeatureId)"/>
<xsl:template match="/">

  <xsl:choose>
    <xsl:when test="$mainFeature/uploadedVideo != ''">
      <xsl:variable name="macro">
        <![CDATA[<?UMBRACO_MACRO macroAlias="uTube.ChromelessPlayer.media" mediaID="]]><xsl:value-of select="$mainFeature/uploadedVideo"/><![CDATA[" uTubeWidth="680" uTubeHeight="433"></?UMBRACO_MACRO>]]>
      </xsl:variable>
      
      <!-- Render the chromeless player macro -->
      <xsl:value-of select="umbraco.library:RenderMacroContent($macro, $currentPage/@id)" disable-output-escaping="yes"/>

    </xsl:when>
    <xsl:otherwise>
      <xsl:choose>
        <xsl:when test="$mainFeature/video != ''">
          <xsl:variable name="macro">
            <![CDATA[<?UMBRACO_MACRO macroAlias="uTube.ChromelessPlayer" uTubeVideo="]]><xsl:value-of select="$mainFeature/video"/><![CDATA[" uTubeWidth="680" uTubeHeight="433"></?UMBRACO_MACRO>]]>
          </xsl:variable>
          
          <!-- Render the chromeless player macro -->
          <xsl:value-of select="umbraco.library:RenderMacroContent($macro, $currentPage/@id)" disable-output-escaping="yes"/>
        
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="count($mainFeature/bannerMedia) > 0">
            <a href="{umbraco.library:NiceUrl($mainFeature/@id)}" title="{$mainFeature/@nodeName}"><img src="{umbraco.library:GetMedia($mainFeature/bannerMedia, 'false')/umbracoFile}" title="{$mainFeature/@nodeName}" alt="{$mainFeature/@nodeName}" /></a>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

</xsl:stylesheet>

Taking a leaf out of the uTube.ChromelessPlayer.media macro's book (which is in itself a wrapper for the uTube.ChromelessPlayer macro), I'm testing for the existence of the uploadedVideo property, which points at the Media Item containing the uTube uploaded media.  If that doesn't exist, I then check for the video property, which is the alternative uTube Single Video Picker (the user simply pastes in a Youtube url).  If that doesn't exist, we fallback on a placeholder image instead.

End result: Graceful media fallback on the web page.

uTube.ChromelessPlayer.media

Broken Protected Pages in Umbraco 4.5?

Today, while implementing the membership functionality for a new site I'm working feverishly on to get go live next week, I came across this error when I tried to access a Protected page while not logged in:


Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.


Stack Trace:

[NullReferenceException: Object reference not set to an instance of an object.]
   System.Web.Security.Membership.GetCurrentUserName() +68
   System.Web.Security.Membership.GetUser() +17
   umbraco.requestHandler..ctor(XmlDocument umbracoContent, String url) +8037
   umbraco.UmbracoDefault.Page_PreInit(Object sender, EventArgs e) +2534
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +24
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +41
   System.EventHandler.Invoke(Object sender, EventArgs e) +0
   System.Web.UI.Page.OnPreInit(EventArgs e) +11042957
   System.Web.UI.Page.PerformPreInit() +41
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1255

As it turns out, it seems you need to add the runAllManagedModulesForAllRequests="true" attribute to the Modules node in your web.config file:

    <modules runAllManagedModulesForAllRequests="true">

Worked like a charm :)  Thank you Lee from Blogg Fodder UK, you saved me heaps of time with this one :)

Blog Categories in Blog4Umbraco - Take Two

So my first attempt at manipulating Tag Groups by using Events didn't go so well.  Sure, it created the tags with the correct group, but it also left the original tags in the csmTags table, and if you went back and edited the blog entry, the tags would be gone because the Tag Data Type was ignoring anything that didn't belong to the default group.

So. Back to the drawing board.

Edit (10/11/2010): The first package download didn't go so well and failed dismally.  I've uploaded a new version of the package and this time double-tested it, so it should be all good.  Leave a comment if things go awry...

This time I decided to try and tackle the problem at a deeper level - what if I introduced a new Data Type that could intercept the tag group and inject the Blog Category instead?

Package Download: You can download an Umbraco Package to install the Dll that will do this for you from here.  It contains a single Dll that has a couple of Extensions in it aimed at Blog 4 Umbraco, but is not dependent on it being installed.

Enter the Blog Tag Data Type.

I didn't want to have to re-create the entire Tag Data Type, it's Editor Control, or it's Prevalue Editor, so I took advantage of the fact that almost everything can be extended in Umbraco, and just derived a new Data Type class:

 

public class DataType : umbraco.editorControls.tags.DataType
    {
        #region IDataType Members

        private IDataEditor _Editor;

        public override IDataEditor DataEditor
        {
            get
            {
                if (_Editor == null)
                {
                    var preValues = ((umbraco.editorControls.tags.PrevalueEditor)PrevalueEditor).Prevalues;
                    int? nodeId = int.Parse(umbraco.helper.Request("id"));
                    if (nodeId.HasValue)
                    {
                        string category = (string)BlogExtensionsLibrary.GetValueRecursively(nodeId.Value, "category");

                        if (!string.IsNullOrEmpty(category))
                        {
                            if (preValues["group"] == null || (string)preValues["group"] == "default")
                                preValues["group"] = category;
                        }
                    }
                    _Editor = new umbraco.editorControls.tags.DataEditor(Data, preValues);
                }
                return _Editor;
            }
        }

        public override string DataTypeName
        {
            get { return "Blog Tags"; }
        }

        public override Guid Id
        {
            get { return new Guid("BD8B240F-0DE3-47E5-A172-2DE212CC30B6"); }
            // this was the core umbraco tags datatype GUID.
            //get { return new Guid("4023e540-92f5-11dd-ad8b-0800200c9a66"); }
        }

        #endregion
    }

 

Basically, I stripped out all the stuff I didn't want to duplicate from the tags DataType class, and overrode the DataEditor property.  A few interesting things to note:

  • I could have left the Id property alone and not overridden it.  If had done so, my DataType would have taken over the core Tags DataType, and you would basically not have to do anything more.  However, I'm trying to be a good Umbraco Citizen and behave in a right neighbourly fashion.
  • The code has been designed so that if the category attribute doesn't exist in the Document Type or one of it's Ancestors, then it will not try to inject anything into the group, so it would be quite safe to replace the Core Tags Control with the new one.
  • BlogExtensionsLibrary.GetValueRecursively() is described in my previous post attempting to deal with Blog Categories, so not going to go into it here.

Ok, once you have compiled your code and copied the dll into the bin directory of your Umbraco installation, the remaining steps are as follows:

Update the Tags Data Type, or alternatively create a new one.

You will need to go and change the Tags Data Type so that it uses the new Blog Tags control instead of the Core Tags control.  Note that if you decided not to play nice, and let the new code override the Core Tags control by using the old GUID, then essentially you shouldn't need to do anything here!

Blog Tags Data Type

Update the Blog Post Document Type

You would only need to change the Blog Post Document Type if you created a new Data Type instead of modifying the Core Tags Data Type.

Altering Xslt Files for Blog Categories with Tags

This is the third post in a series looking at the issue of having Categories at the Blog level with Blog 4 Umbraco.  It builds upon the post dealing with extending the Blog Tags to take make them specific to a particular Blog by taking advantage of the Tag Group feature.  For background, see the following posts:

Blog 4 Umbraco installs a suite of xslt files that among other things aggregate Tags that the posts are associated with.  However, some of the code looks specifically for the default Tag Group, while other parts of it don't care what group the tags are in.  If you have multiple blogs on a site, this leads to tags being displayed that may actually have nothing to do with the Blog you are looking at.

In this article we're going to attempt to address this issue and clean the xslt files up a little.  Starting with the BlogCategories.xslt file, which by default looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:tagsLib="urn:tagsLib"
  exclude-result-prefixes="msxml umbraco.library tagsLib">


<xsl:output method="html" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<xsl:variable name="blogRoot" select="$currentPage/ancestor-or-self::Blog/@id"/>

<xsl:template match="/">
<ul>
  <li class="cat-item"><a href="{umbraco.library:NiceUrl($blogRoot)}">All</a> <span> (<xsl:value-of select="count($currentPage/ancestor-or-self::Blog//BlogPost)"/>)</span></li>
  <xsl:for-each select="tagsLib:getAllTagsInGroup('default')/tags/tag">
        <li class="cat-link">
            <a href="{umbraco.library:NiceUrl($blogRoot)}?filterby={current()}"><xsl:value-of select="current()"/></a> (<xsl:value-of select="@nodesTagged"/>)
        </li>
  </xsl:for-each>
</ul>

</xsl:template>

</xsl:stylesheet>​


Note the line that retrieves all Tags in the default using tagsLib:getAllTagsInGroup().  This is where the problem lies:  We have a blog that has had a category attribute set to "Events" (see the first article listed above), and we now have tags that are associated with the "Events" group, not the "default" group.

In order to fix this, we need to retrieve the category from the Blog node, and use that to retrieve the correct tags.  However, if the blog hasn't implemented the category attribute, or the attribute is left blank, we need to fall back on the current functionality.  So I've made the following adjustments:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:tagsLib="urn:tagsLib"
  exclude-result-prefixes="msxml umbraco.library tagsLib">


<xsl:output method="html" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<xsl:variable name="blogRoot" select="$currentPage/ancestor-or-self::Blog"/>
<xsl:variable name="blogRootId" select="$blogRoot/@id"/>
<xsl:variable name="blogCategory" select="$blogRoot/category"/>

<xsl:template match="/">
  
  <ul>
  <li class="cat-item"><a href="{umbraco.library:NiceUrl($blogRootId)}">All</a> <span> (<xsl:value-of select="count($currentPage/ancestor-or-self::Blog//BlogPost)"/>)</span></li>
  <xsl:if test="$blogCategory = ''">
    <xsl:call-template name="listCategories">
      <xsl:with-param name="category" select="'default'"/>
    </xsl:call-template>
  </xsl:if>
  <xsl:if test="$blogCategory != ''">
    <xsl:call-template name="listCategories">
      <xsl:with-param name="category" select="$blogCategory"/>
    </xsl:call-template>
</xsl:if>

</ul>

</xsl:template>

<xsl:template name="listCategories">
  <xsl:param name="category"/>
  <xsl:for-each select="tagsLib:getAllTagsInGroup($category)/tags/tag">
      <li class="cat-link">
          <a href="{umbraco.library:NiceUrl($blogRootId)}?filterby={current()}"><xsl:value-of select="current()"/></a> (<xsl:value-of select="@nodesTagged"/>)
      </li>
  </xsl:for-each>
</xsl:template>
</xsl:stylesheet>​



Notice that we've split the actual rendering of the categories list into a separate template called listCategories and have conditionally called it with either the category retrieved from the Blog document or the 'default' tag Group if the category doesn't exist.

Next we tackle the Tag Cloud in much the same way.  The original Xslt source:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
    <!ENTITY nbsp " ">
]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:BlogLibrary="urn:BlogLibrary"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib BlogLibrary ">


    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <xsl:param name="currentPage"/>

    <xsl:template match="/">
        <div class="tagcloud">
            <p>

                <xsl:for-each select="tagsLib:getAllTags()/tags/tag [@nodesTagged > 0]">
                    <xsl:sort select="." order="ascending"/>
                    <a href="{umbraco.library:NiceUrl($currentPage/ancestor-or-self::Blog/@id)}?filterby={.}">
                        <xsl:attribute name="class">
                            <xsl:choose>
                                <xsl:when test="@nodesTagged > 5">
                                    <xsl:value-of select="string('tagweight5')"  />
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select="concat('tagweight',@nodesTagged)"/>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:attribute>
                        <xsl:value-of select="."/>
                    </a>
                    <xsl:text> </xsl:text>
                </xsl:for-each>

            </p>
        </div>

    </xsl:template>

</xsl:stylesheet>​

And after our modifications:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
    <!ENTITY nbsp " ">
]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:BlogLibrary="urn:BlogLibrary"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib BlogLibrary ">


    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <xsl:param name="currentPage"/>

    <xsl:variable name="blogRoot" select="$currentPage/ancestor-or-self::Blog"/>
    <xsl:variable name="blogRootId" select="$blogRoot/@id"/>
    <xsl:variable name="blogCategory" select="$blogRoot/category"/>
    
    <xsl:template match="/">
        <div class="tagcloud">
            <p>
              
            <xsl:if test="$blogCategory = ''">
              <xsl:call-template name="createCloud">
                <xsl:with-param name="category" select="'default'"/>
              </xsl:call-template>
            </xsl:if>
            <xsl:if test="$blogCategory != ''">
              <xsl:call-template name="createCloud">
                <xsl:with-param name="category" select="$blogCategory"/>
              </xsl:call-template>
            </xsl:if>
             
            </p>
        </div>

    </xsl:template>
    
  <xsl:template name="createCloud">
    <xsl:param name="category"/>

    <xsl:for-each select="tagsLib:getAllTagsInGroup($category)/tags/tag [@nodesTagged > 0]">
        <xsl:sort select="." order="ascending"/>
        <a href="{umbraco.library:NiceUrl($blogRootId)}?filterby={.}">
            <xsl:attribute name="class">
                <xsl:choose>
                    <xsl:when test="@nodesTagged > 5">
                        <xsl:value-of select="string('tagweight5')"  />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="concat('tagweight',@nodesTagged)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>
            <xsl:value-of select="."/>
        </a>
        <xsl:text> </xsl:text>
    </xsl:for-each>

  </xsl:template>
</xsl:stylesheet>​​​


And there we have it. Both the Tags list and the Tag Cloud now behave nicely when used within the context of a Blog.

Adding a Blog Category to Blog4Umbraco

Recently, I've been working on an Umbraco website for a client that required several blogs, but each blog needed to have it's own set of tags and the Tag Cloud needed to be compartmentalised.

 

NOTE: The code described below didn't quite work out as I'd hoped with further testing, so I went back to the drawing board.  Take a look at the post Blog Categories in Blog4Umbraco - Take Two for a much cleaner and more transparent solution to the problem with flexible Tag Groups.

Problem Number 1: Tag Clouds in Umbraco are non-discriminatory

If you have more than one blog in Umbraco, go take a look at your Tag Cloud.  You will notice that all tags are listed, not just those for a specific blog.  Worse, even Documents that have a Tag Data-typed attribute will contribute to the Tag Cloud, regardless of whether they are a Blog Post document or not.  As a result, clicking on one of those tags will not necessarily result in listing blog posts.

Adding a Tag Category to an blog

Adding a category is as simple as adding another attribute to the Blog Document Type.  Here's my settings:

Adding a Category attribute to the Blog Document TypeAdding a Category attribute to the Blog Document Type

To apply the category, go to the root document of your blog and update the field, save and publish.  Done.  Now what?

It's all about the Tags

First, some background:  The Umbraco Tag datatype allows the administrator to set up tags for document types, which can then be used in various ways - as a Tag Cloud, to loosely associate related documents (allowing the developer to list those documents automatically on an Article page, for example), etc.

By default, the Tags Data Type uses a Tag Group named, surprisingly enough, "Default".  However it's possible to create a new Data Type - call it say "Event Tags" - and specify a new Group - in this case, "Events":

Event Tags Data Type

Creating a new Event Tags Datatype - Notice the Tag group that can be specified once you choose the Tags control.

The advantages of doing this mean that you could have a tag cloud for a specific group of articles - essentially creating an article Category... Exactly what we want for our multiple blogs scenario.

Note: For this solution, creating a new Tag Data Type is not actually necessary.  This step is one of the paths I originally took trying to resolve this issue, and serves as background information to what we are trying to do. However I quickly realised that this wasn't going to serve my needs at all.

Using a non-standard Tag Group in Blog4Umbraco

Ok, so now we have a category associated with a blog, and we have a grouping for a set of tags.  But how do we make sure that tagging an article in a Blog Entry uses the correct Tag Group?

As it turns out, this isn't so straightforward.  We can't just change the Blog Post Document Type to use our new Event Tags Data Type, as all blog entries, on all blogs, must by necessity use the Blog Post Document Type.  If you try to create a new Document Type based on the Blog Post one, Blog 4 Umbraco simply refuses to work with it.

After some investigation, I decided that the best approach was to implement some Event Handling Code.  The beauty of this approach is that you are actually able to roll your own Library - there's no need to modify any existing source code.  So here goes:

Writing an Event Handler

We want to write an Event Handler.  In order to make sure our event handler is registered, we need to derive from the Umbraco ApplicationBase class, and register our handlers in the Constructor:

    public class SetTags : ApplicationBase
    {
        public SetTags()
        {
            Document.AfterSave += Document_AfterSave;
        }

        void Document_AfterSave(Document sender, umbraco.cms.businesslogic.SaveEventArgs e)
        {
            FixTags(sender);
        }
    }

Handling the Document.AfterSave Event

The core of this solution is to examine the Blog Post document, retrieve a list of Tags, and any tags that don't currently have a Group that matches the category attrbute we applied to the Blog earlier need to be removed and replaced with new ones associated with the desired group.  For this I have the following Method:

private void FixTags(Document sender)
        {
            if (sender.ContentType.Alias == "BlogPost")
            {
                if (sender.Parent != null)  //If top of tree, something is wrong.  Skip.
                {// Get the Blog category.
                    // We want to remove any tags on the document that aren't associated with the Blog category.

                    string category = BlogLibrary.GetValueRecursively(sender.Id, "category").ToString();
                    if (string.IsNullOrEmpty(category))
                        // No point continuing - we'll use the default tag group.
                        return;

                    StringBuilder tags = new StringBuilder();
                    foreach (var t in umbraco.editorControls.tags.library.GetTagsFromNodeAsITags(sender.Id))
                    {
                        if (t.Group != category)
                        {
                            if (tags.Length &gt; 0)
                                tags.Append(",");
                            tags.Append(t.TagCaption);

                            umbraco.editorControls.tags.library.RemoveTagFromNode(sender.Id, t.TagCaption, t.Group);
                        }
                    }
                    umbraco.editorControls.tags.library.addTagsToNode(sender.Id, tags.ToString(), category);
                }
            }
        }

Some notes on this code:

  • Line #9: The BlogLibrary.GetValueRecursively method is a refactoring of the private method in the Autoping Event Handler class in the Blog4Umbraco dll.  All I've done is extract it into a public static method in the BlogLibrary class, and switch the parameters (mainly because it makes more sense to me to have the Node Id as the first parameter).  I've also changed the return value to an object type instead of string.  For reference, I include the code for that method here:

        public static object GetValueRecursively(int nodeId, string alias)
        {
            Document n = new Document(nodeId);
            Property p = n.getProperty(alias);

            if (p != null && !string.IsNullOrEmpty(p.Value.ToString()))
                return p.Value;
            else if (n.Level &gt; 1)
                return GetValueRecursively(n.Parent.Id, alias);

            return string.Empty;
        }
  • Lines 23 & 26: The umbraco.editorControls.tags.library exposes all the tag manipulation methods we need. We retrieve the list of tags associated with this node, and any node that doesn't have a group matching the Blog Category gets removed and added to the new Tag list.  Then after going through all the tags, we add back the new Tags with the Blog category as their Tag Group.

Compile your dll, copy it to the bin directory of your Umbraco installation, and try it out.  Whenever you tag a Blog Post and save the document, all tags will re-added with the new Tag Group:

Tags Database Table

The Tags Database Table after the test Tag has been added with the Events Group.

Of course, you'll be wanting to go and tweak your Tag Cloud xslt code to take advantage of the Blog Category and Tag Groups next... I'll cover that in another Blog Entry...

Blog 4 Umbraco conflict with UComponents

Playing with Blog 4 Umbraco some more this week, I ran into this problem and decided to look deeper.

The "Author Type" datatype

The owner property on a blog relates to the "Author Type" datatype that is created when Blog 4 Umbraco is installed.  You can see this by taking a look at the Blog Document Type in Settings.  Go to the Generic Properties Tab and there it is, right at the bottom.  The problem arises because by default, Blog 4 Umbraco creats the Author Type with a databaset type of Ntext. When a new Blog is created, the code attempts to insert the Current User's Id (an integer field) into the owner property, which then blows up because the two data types are incompatible.

What Render Control is this?

As it happens, as part of the initial configuration of the site, I'd installed Blog 4 Umbraco followed by UComponents.  Both of which include a User Picker Render Control, and both of which use exactly the same Globally Unique Identifier for it.  However, the configuration interface for each datatype is different:

Blog 4 Umbraco Author PickerBlog 4 Umbraco Userpicker configured in the Author Picker - note the Database datatype.

uComponents_authPickerThe Author Picker after installing uComponents - note the Data Type GUID is identical.

It appears that the uComponents User Picker control is overriding the Blog 4 Umbraco one, regardless of which order you install the packages in.

Resolving the "Operand type clash: int is incompatible with ntext" error when creating a new Blog

So the solution that presents itself to resolve the error that occurs when you try to create a new Blog is quite simple without needing to recompile source code:

  1. Go to the Developer Section, and expand out the Data Types node.
  2. Select the "Author Picker" Data Type form the list.
  3. If you don't have uComponents installed: Change the Database datatype field to nvarchar
  4. Save the Data Type.

Go and create a test blog, and all should be well.

As always, any comments, thoughts, or feedback are always welcome.