This Blog Has Moved

Weblog:
http://blechie.com/wpierce/

Feed:
http://feeds.feedburner.com/wcpierce/

AES Encryption in VBScript

VB6/VBScript was one of the first languages I learned to use.  Today, VBScript is still my fall back language when I just need to 'Getter Done!'.  Coupled with Windows Script Host and the Scripting runtime I can use it to automate a large number of otherwise manual tasks.  We have some VBScripts laying around that export data from our datastore on scheduled intervals and make it available to our customers to download via FTP-SSL.  We are adding some new information to the export that is of the sensitive nature and so encryption came up.  Figured since 'Standard' is the S in AES, it made a good choice.  The problem was finding some code to do this in VBScript.  Some digging turned up an implementation in VB6/ASP but it was god awfully large and complicated.  Additionally it was difficult to verify that once encrypted on our side, our customer could easily decrypt it using a previously exchanged Key and Initialization Vector (also using a different programming language all together).  Since the .Net Framework makes encryption dirt simple and it was already on the server I whipped up a simple class that is exposed to COM and easily incorporated into our existing VBScript.

Without going too deep, AES requires a Key and an Initialization Vector (IV) to encrypt something.  You need the same Key and IV to decrypt the info at a later time.  Both of these are byte arrays (byte[]).  The resulting encrypted data is also a byte array.  Now sharing/hard coding a byte array in code is not all that easy.  Rather than share the raw byte array you can base64 encode the data and then you have something that is easily inserted into a text file or transferred via email.  The code below is written in a very targeted manner expecting/returning base64 data, hard coding the algorithm used, ascii encoding, etc.  Please feel free to tweak for your needs.  To expose this to COM, just run "regasm.exe My.Product.dll" and you can run the...cough...unit tests...cough at the end of the sample.  Program note: all of the methods here could be marked static, but COM doesn't like static methods.

namespace My.Product
{
  using System;
  using System.IO;
  using System.Runtime.InteropServices;
  using System.Security.Cryptography;
  using System.Text;

  [ProgId("My.Product.Crypto")]
  public class Crypto
  {
    public string Encrypt(string base64key, string base64iv, string clearText)
    {
      SymmetricAlgorithm algorithm = GetAlgorithm();
      algorithm.Key = Convert.FromBase64String(base64key);
      algorithm.IV = Convert.FromBase64String(base64iv);

      byte[] clearTextBytes = Encoding.ASCII.GetBytes(clearText);

      byte[] cipherTextBytes = Transform(algorithm, clearTextBytes, false);

      return Convert.ToBase64String(cipherTextBytes);
    }

    public string Decrypt(string base64key, string base64iv, string base64cipherText)
    {
      SymmetricAlgorithm algorithm = GetAlgorithm();
      algorithm.Key = Convert.FromBase64String(base64key);
      algorithm.IV = Convert.FromBase64String(base64iv);

      byte[] cipherTextBytes = Convert.FromBase64String(base64cipherText);

      byte[] clearTextBytes = Transform(algorithm, cipherTextBytes, true);

      return Encoding.ASCII.GetString(clearTextBytes, 0, clearTextBytes.Length);
    }

    public int DefaultKeySize
    {
      get
      {
        SymmetricAlgorithm algorithm = GetAlgorithm();
        return algorithm.KeySize;
      }
    }

    public int DefaultBlockSize
    {
      get
      {
        SymmetricAlgorithm algorithm = GetAlgorithm();
        return algorithm.BlockSize;
      }
    }

    public string GenerateBase64Key(int keySize)
    {
      SymmetricAlgorithm algorithm = GetAlgorithm();
      algorithm.KeySize = keySize;
      return Convert.ToBase64String(algorithm.Key);
    }

    public string GenerateBase64IV(int blockSize)
    {
      SymmetricAlgorithm algorithm = GetAlgorithm();
      algorithm.BlockSize = blockSize;
      return Convert.ToBase64String(algorithm.IV);
    }

    private static byte[] Transform(SymmetricAlgorithm algorithm, byte[] bytes, bool decrypt)
    {
      byte[] transformedBytes = null;
      using (ICryptoTransform transform = GetTransform(algorithm, decrypt))
      {
        using (MemoryStream memoryStream = new MemoryStream())
        {
          using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
          {
            cryptoStream.Write(bytes, 0, bytes.Length);
          }
          transformedBytes = memoryStream.ToArray();
        }
      }
      return transformedBytes;
    }

    private static ICryptoTransform GetTransform(SymmetricAlgorithm algorithim, bool decrypt)
    {
      if (decrypt)
      {
        return algorithim.CreateDecryptor();
      }

      return algorithim.CreateEncryptor();
    }

    private static SymmetricAlgorithm GetAlgorithm()
    {
      RijndaelManaged algorithm = new RijndaelManaged();
      algorithm.KeySize = 256;
      algorithm.BlockSize = 128;

      return algorithm;
    }
  }
}

 And it's usage in VBScript:

Option Explicit

Call Main()

Sub Main()
  Dim crypto
  Dim base64key, base64iv
  Dim clearText, cryptText, cryptText2, decryptText

  Set crypto = CreateObject("My.Product.Crypto")
  base64key = crypto.GenerateBase64Key(crypto.DefaultKeySize)
  base64iv = crypto.GenerateBase64IV(crypto.DefaultBlockSize)
  clearText = "EncryptThis"

  cryptText = crypto.Encrypt(base64key, base64iv, clearText)
  decryptText = crypto.Decrypt(base64key, base64iv, cryptText)
  If clearText <> decryptText Then
    MsgBox "Decrypting did not yield correct value"
    Exit Sub
  End If

  cryptText = crypto.Encrypt(base64key, base64iv, clearText)
  cryptText2 = crypto.Encrypt(base64key, base64iv, clearText)
  If cryptText <> cryptText2 Then
    MsgBox "Encrypting twice produces different results"
    Exit Sub
  End If

  MsgBox "All Tests Passed"    
End Sub

MonoRail - Efficient Tab Indexing

I'm a big fan of keyboard shortcuts for the Windows, Resharper, Firefox, Web Browsing, GMail, etc.  The less I touch the mouse the more productive I am.  One thing that can make a great web application suck hard is poor or absent tab indexing on form fields.  It takes a little extra time on the developers part but it makes a world of difference to the end user.  Rather than hard code tabIndexes, I use the following technique using a simple accumulator:

<?brail
  tabIndex = 1
?>

${Form.FormTag({'action':'login'})}
<table>
  <tr>
    <td>${Form.LabelFor('username', 'Username')}</td>
    <td>
      ${Form.TextField('username', {'tabIndex':tabIndex++})}
    </td>
  </tr>
  <tr>
    <td>${Form.LabelFor('password', 'Password')}</td>
    <td>${Form.PasswordField('password', {'tabIndex':tabIndex++})}</td>
  </tr>
  <tr>
    <td />
    <td>${Form.Submit('Login', {'tabIndex':tabIndex++})}</td>
  </tr>
</table>
${Form.EndFormTag()}

One problem I struggled with in ASP.Net was allowing tabIndexes to properly flow from a page to a user control then back again.  I'm going to ping Oren to see if it is possible to declare tabIndex 'globally' (in a layout maybe?) and allow all views/sub views/view components to use the same value to try and address this in MonoRail.

Dexagogo - Alert Validation Advice

I recently blogged about using Dexagago validation API with MonoRail.  The API allows you to specify your own advice messages in any HTML element.  The framework will take care of showing/hiding the element as needed during validation.  I wanted to be able to display a javascript alert box with validation messages in certain cases.  Here's what I came up with:

<script type="text/javascript">
  function AlertAdvice(isValid, el)
  {
    if( !isValid )
    { 
      alert(el.title);
      if(el.select) el.select();
    }
  }
</script>

${Form.FormTag({'action':'login','immediate':'false','stopOnFirst':'true','onElementValidate':'AlertAdvice'})}
<table>
  <tr>
    <td>${Form.LabelFor('username', 'Username')}</td>
    <td>
      ${Form.TextField('username', {'class':'required','title':'Please enter a Username'})}
      <div id="advice-username" style="visibility: hidden;" />
    </td>
  </tr>
  <tr>
    <td>${Form.LabelFor('password', 'Password')}</td>
    <td>${Form.PasswordField('password', {'class':'required'})}</td>
  </tr>
  <tr>
    <td />
    <td>${Form.Submit('Login')}</td>
  </tr>
</table>
${Form.EndFormTag()}

First we specify a javascript function 'AlertAdvice' that will be called by the validation API for each validator on our form.  If the validator fails (empty required field, alpha in a numeric field, etc) we display the contents of the title attribute, then for good measure we select the contents of the field.  When specifying your form tag you want to make sure to set immediate to false.  I found this out the hard way (took me a few tries :) otherwise you will be locked in a tight loop with a constant alert on invalid fields.  I also set stopOnFirst to true so that the user doesn't get overwhelmed with prompts for a large number of invalid fields.  Finally, we hook up our AlertAdvice function with the onElementValidate property.

You need to put the message you want displayed in the prompt in the title attribute of your field, ${Form.TextField('username', {'class':'required','title':'Please enter a Username'})} in our example above.  Finally, I wasn't able to find a way to disable the API's automatic display of an advice message.  To work around this, I add a hidden div element with a name of advice-<fieldName>. 

Lightly tested in IE6 and FF2.

MonoRail - Initial Focus ViewComponent

Need to set initial focus to a field on your form?  This component is adapted from Fábio's code on the MonoRail forums.

namespace My.Product.Mvc
{
  using Castle.MonoRail.Framework;

  [ViewComponentDetails("InitialFocus")]
  public class InitialFocusComponent : ViewComponent
  {
    private const string FOCUS_TEMPLATE =
      "<script type=\"text/javascript\">Event.observe(window,'load',function(){{$('{0}').focus();}});</script>";

    private string _id;
    private bool _shouldRender;

    [ViewComponentParam(Required=true)]
    public virtual string Id
    {
      get { return _id; }
      set { _id = value; }
    }

    public override void Initialize()
    {
      if( !Context.ContextVars.Contains("__Initial_Focus_Set__") )
      {
        Context.ContextVars["__Initial_Focus_Set__"] = Id;
        _shouldRender = true;
      }
    }

    public override void Render()
    {
      if( _shouldRender )
      {
        RenderText(string.Format(FOCUS_TEMPLATE, Id));
      }
    }
  }
}

Use in your view like so:

<% component InitialFocus, {'id':'username'} %>

This component is "first in wins" meaning if you include multiple component references in the same view/subview, the first field specified will get initial focus.  So far this has only been lightly tested on text fields.

MonoRail - Collocate your Strings

Localization is a pain.  Anybody who says different is a sadist.  It's especially difficult if you are trying to retrofit an app that didn't even try i.e. hard coding all strings in code/views.  Even if I'm not developing an application with localization in mind, I still strive to put all strings in a resx file and never hard code them anywhere.  The recommended method for localizing a standard ASP.Net application was to have a single resx file per page.  Similarly for a WinForms app you would have one resx file per Form.  In a WinForms app this is slightly more understandable since you may need to resize fields to accommodate different languages.  However in web apps the only thing I ever store in a resx file is strings. 

ASP.Net 2.0 introduced the concept of an App_GlobalResource that can be accessed from any page in your site.  This was a step in the right direction because you could easily reuse common strings as needed.  There are cases however when I need to generate a message in my business layer that will ultimately be displayed to the user.  So I end up needing a resx file in my Core assembly, and a resx file in my web app.  Kind of defeats the purpose of a centralized location for strings.  The problem is further compounded by the fact that VS2005 generates a strongly typed wrapper for your resx file that has all strings marked as internal.  So I can't easily put all of my strings in my Core assembly and then reference them in my web app.

I little googling turned up a work around that allows you to generate a wrapper with public properties.  This method worked just fine but I get anal about such things and wanted my publicly generated code to match as closely as possible to what VS2005 would have created for me.  Here is my adapted code to generate a file called Strings.Designer.cs that contains a public wrapper class called Strings in namespace My.Product.Core for my Strings.resx resource file:

<Compile Include="Strings.Designer.cs">
  <AutoGen>True</AutoGen>
  <DesignTime>True</DesignTime>
  <DependentUpon>Strings.resx</DependentUpon>
</Compile>

<ItemGroup>
  <EmbeddedResource Include="Strings.resx">
    <SubType>Designer</SubType>
    <LastGenOutput>Strings.Designer.cs</LastGenOutput>
  </EmbeddedResource>
</ItemGroup>

<!-- Generate Strings.Designer.cs with Public access -->
<Target Name="BeforeBuild" DependsOnTargets="GenStrongTypeResource" />
<Target Name="GenStrongTypeResource" Inputs="Strings.resx" Outputs="Strings.Designer.cs">
  <GetFrameworkSdkPath>
    <Output TaskParameter="Path" PropertyName="SdkPath" />
  </GetFrameworkSdkPath>
  <Exec Command="&quot;$(SdkPath)\bin\resGen.exe&quot; /str:c#,My.Product.Core,Strings,Strings.Designer.cs /publicClass Strings.resx" />
</Target>

The first part of the post is pretty generic, lets see how we can now use our strongly typed Strings class in our MonoRail views.

MonoRail supports Localization in a similar manner to ASP.Net.  You specify which resource file you want to read from and then assign it a friendly name you can use in your view.  Using my technique described above, you essentially bypass all of that.  My view engine of choice is Brail.  To be frank, I'm not sure if you will be able to use this method with NVelocity or any of the other engines.

First off, we need to configure Brail with our Core assembly.  You do so in your web.config file:

<brail debug="true" saveToDisk="false" saveDirectory="BrailGen" batch="false" commonScriptsDirectory="CommonScripts">
  <reference assembly="My.Product.Core" />
</brail>

Next, you add an import statement to your view and then you can use the generated Strings class just like you would in your C# code.  Here is the localized version of the login view I used as an example in my last post:

<%
  import My.Product.Core
%>

${Form.FormTag({'action':'login'})}
<table>
  <tr>
    <td>${Form.LabelFor('username', Strings.Username)}</td>
    <td>${Form.TextField('username', {'class':'required'})}</td>
    <td><div id="advice-username" class="advice" style="display: none;">${Strings.UsernameRequired}</div></td>
  </tr>
  <tr>
    <td>${Form.LabelFor('password', Strings.Password)}</td>
    <td>${Form.PasswordField('password', {'class':'required'})}</td>
    <td />
  </tr>
  <tr>
    <td />
    <td>${Form.Submit(Strings.Login)}</td>
    <td />
  </tr>
</table>
${Form.EndFormTag()}

Nice and easy. Now all of the strings used in your application are centralized in a single file. This makes translation more straight forward and helps prevent duplication of strings in multiple resx files. You could easily extend this and have a Strings assembly that only contains a resx file that could be reused in multiple applications.

MonoRail - Form Validation the Hard Way

Why would you want to do something the hard way?  Well of course you do something the hard way when you can't do something the easy way (unless you are Oren in which case you do everything your way :).  Hammett has several excellent posts on how to do form validation the easy way using the Castle Validator component (screencast and additional thoughts).  The component makes validation dirt simple especially if you are using ActiveRecord.  However the default implementation ties validators to attributes on the model which works 80% of the time. 

For the 20% of the time when you want to get validators from somewhere else one would need to implement a custom IValidatorRegistry.  IValidatorRegistry is responsible for providing all validators for given type and/or property back to the validation framework.  Are you feeling the love?  All you need to do is customize where the validators come from, and you still benefit from the runtime and client side validation generation. w00t!  So, in theory your DatabaseValidatorRegistry could query a...database...to retrieve all validators for a given type and property.  Allowing you to craft an interface where by the end user could modify validation rules at will.  I'm getting a little dizzy just thinking about the possibilities.  I haven't gone there yet but when I do you will be the first to know.

That last paragraph literally popped into my head as I was starting this post.  I actually was going in a different direction for this particular discussion but I just had to put it down on paper.  What I really want to talk about is doing simple client side validation using the client side Validator scripts.  I have a simple login prompt with username and password.  I want to do client side validation to ensure they enter a username and password before submitting the form.  What do you do?  Go.

First things first, you need to install the client side scripts.  It is recommended to do this in your layout so that the scripts are included on all of your pages:

<head>
    <title>MonoRail Validation</title>
    ${Ajax.InstallScripts()}
    ${Form.InstallScripts()}
</head>

What exactly does this do?  Among other things it emits script tags for the Prototype libraries and the Dexagogo 'really easy field validation' libraries.  The next step is to create a form in your view with the necessary fields:

${Form.FormTag({'action':'login'})}
<table>
  <tr>
    <td>${Form.LabelFor('username', 'Username')}</td>
    <td>${Form.TextField('username', {'class':'required'})}</td>
    <td><div id="advice-username" class="advice" style="display: none;">Please enter a Username</div></td>
  </tr>
  <tr>
    <td>${Form.LabelFor('password', 'Password')}</td>
    <td>${Form.PasswordField('password', {'class':'required'})}</td>
    <td />
  </tr>
  <tr>
    <td />
    <td>${Form.Submit('Login')}</td>
    <td />
  </tr>
</table>
${Form.EndFormTag()}

How hard is it to make a field required?  Excruciating!  First the Form.FormTag automatically hooks up the required logic to the validation libraries.  See the 'class':'required' in the TextField and PasswordField elements?  The Dexagogo code uses CSS selectors (similar to Behaviour) to execute validation when the user clicks the Login button.  THAT'S IT!  Now, when you click Login without entering a username, the form will not be submitted and you will be presented with a lovely error message.  The Dexagogo site documents the twelve built in validations you can perform (dates, numbers, email, etc).  If you need more you can specify your own validation callback that will get executed when the form is submitted.  Dexagogo has a number of standard validation messages, but you can easily substitute your own by adding an element to your page with an id of "advice-<fieldName>".  If validation fails, this element will be shown to the user.  I applied a class of "advice" to my element so I could easily style error messages.  One oddity was that I had to inline specify the display: none.  Putting this in my external stylesheet caused the error message to not be shown properly.  The use of CSS selectors also makes it very easy for you to style your required fields (with a yellow background maybe?)  and style your numeric only fields and provide a standard legend to your users.

You can also specify some additional options in the FormTag (Hammett touched on these briefly in his screen cast).  If you want "advice" shown to the user immediately when the leave a field and you want to turn off the default of focusing on invalid fields, you would specify your FormTag like so.

${Form.FormTag({'action':'login','immediate':'true','focusOnError':'false'})}

Finally I'll reiterate the best practice you've heard before, always validate your data server side in addition to any client side validation you may perform.  For your reference here is the fully generated code for the view above:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head>
    <title>MonoRail Validation</title>
    <script type="text/javascript" src="/MonoRail/Files/AjaxScripts.rails?RC3_0006"></script>
    <script type="text/javascript" src="/MonoRail/Files/FormHelperScript.rails?RC3_0006"></script>
  </head>
  <body>
    <form action='/login/login.rails' method='post' id='form1' >
      <table>
        <tr>
          <td><label for="username">Username</label></td>
          <td><input type="text" id="username" name="username" value="" class="required" /></td>
          <td><div id="advice-username" class="advice" style="display: none;">Please enter a Username</div></td>
        </tr>
        <tr>
          <td><label for="password">Password</label></td>
          <td><input type="password" id="password" name="password" value="" class="required" /></td>
          <td />
        </tr>
        <tr>
          <td />
          <td><input type="submit" value="Login" /></td>
          <td />
        </tr>
      </table>
      <script type="text/javascript">
        if (!window.prototypeValidators) prototypeValidators = $A([]);
        var validator = new Validation('form1', {onSubmit:true, focusOnError:true, stopOnFirst:false, immediate:true, useTitles:true});
        prototypeValidators['form1'] = validator;
      </script>
    </form>
  </body>
</html>

MonoRail - URL Rewriting and Persisting QueryString Parameters

In my last post I talked about how each of our customers identify themselves to our app via a CustomerId query string parameter.  Rather than give links to our customers with ?CustomerId=12345 sprinkled about, I thought it would be nice to take advantage of the built in URL rewriting capabilities of MonoRail.  My target URLs have the following form, https://my.product.com/customerId/controller/action.rails and this will get rewritten to https://my.product.com/controller/action.rails?CustomerId=customerId.  Turns out this is a very simple configuration change.  URL rewriting is handled by the routing service in MonoRail.  You can configure custom 'routes' by adding a subsection to the monorail config section in your web.config file.  You also need to add the routing HTTP module to your list of loaded modules.  Here is what I added to get my desired result (adapted from the Exesto routing configuration):

<routing>
  <rule>
    <pattern>/(?'customerId'\d+)/(?'controller'\w+)/(?'action'\w+)\.rails(\?(?'queryString'.*))?</pattern>
    <replace><![CDATA[ /${controller}/${action}.rails?CustomerId=${customerId}&${queryString} ]]></replace>
  </rule>
</routing>
<httpModules>
  <!-- The order these are listed is important -->
  <add name="routing" type="Castle.MonoRail.Framework.RoutingModule, Castle.MonoRail.Framework" />
  <add name="monorail" type="Castle.MonoRail.Framework.EngineContextModule, Castle.MonoRail.Framework" />
</httpModules>

A few things to note.  We aren't currently using Areas and this routing rule would conflict with the default routing.  Make sure you load the routing module before the monorail module or your custom routes may not work.  Happy day!  We get prettier URLs and we still uniquely identify each customer's site which means they see their custom UI and execute their custom business logic.  There is one small issue though.  Whenever we LinkTo, Redirect, or BeginFormTag, we will need to take the CustomerId and make sure it is injected in the right place in our URLs.  Ugh!  big pain, no thank you.  MonoRail to the rescue! 

The beauty of MonoRail is that it is composed of a number of independent services that can be replaced at will with custom implementations.  The difficult task is determining where is the best place to inject the custom functionality you desire.  In my previous post we implemented runtime views and layouts using a Filter.  Is this the only way we could have achieved it?  No, but it was probably the most logical and the way MonoRail was intended to be extended.  There are a number of ways I can extend MonoRail to ensure that the CustomerId querystring parameter is persisted from request to request.  I chose to make my extension to MonoRail's UrlBuilder.  The UrlBuilder is in charge of...building URLs :)  It allows you to pass in an area, controller, and action, and it will return a well formed URL to said resource.  This seemed like the most logical place to add something to a URL.  I derive from DefaultUrlBuilder and override InternalBuildUrl which is the method used to generate final URLs (I've submitted a patch to make this method virtual, should be in the trunk in the next few days).  I simply check to see if CustomerId is already on the QueryString.  If so, it is inserted into the proper place to generate a URL that will be recognized by the routing rule we implemented above.

namespace My.Product.Mvc
{
  using Castle.MonoRail.Framework;
  using Castle.MonoRail.Framework.Services;

  public class AppendCustomerIdUrlBuilder : DefaultUrlBuilder
  {
    protected override string InternalBuildUrl(string area, string controller, string action, string protocol, string port,
                                               string domain, string subdomain, string appVirtualDir, string extension,
                                               bool absolutePath, bool applySubdomain, string suffix)
    {
      if( !absolutePath )
      {
        string customerId = MonoRailHttpHandler.CurrentContext.Request.Params["CustomerId"];

        if( !string.IsNullOrEmpty(customerId) )
        {
          appVirtualDir = string.Concat(appVirtualDir, '/', customerId);
        }
      }

      return base.InternalBuildUrl(area, controller, action, protocol, port, domain, subdomain, appVirtualDir, 
        extension, absolutePath, applySubdomain, suffix);
    }
  }
}

Now, how do I instruct MonoRail to use my UrlBuilder instead of the DefaultUrlBuilder?  Once again, it is a simple configuration change.  Based on information from the MonoRail Configuration reference, its a piece of cake to slip in a custom service.  The configuration below goes in the MonoRail section of your web.config.  Using the well known id 'UrlBuilder' and the full type of my class, we are rocking and rolling.

<services>
  <service id="UrlBuilder" type="My.Product.Mvc.AppendCustomerIdUrlBuilder, My.Product.Core" />
</services>

MonoRail - Runtime Views and Layouts with Filters

I've been on a bit of a MonoRail kick lately and thought I might share some gems.  In his recent post Hammett talks about, among other things, using a filter to change layouts at runtime.  He didn't elaborate so I thought I would share the way I've implemented his idea.  In our hosted environment we generally prefer each customer to have a separate host header.  For various reasons this isn't possible for some customers.  Instead, we identify them based on an internal id.  We key everything about the application off of this id.  We use this id to display a customized user interface and run custom logic on a per customer basis.  The way I am currently implementing the customized user interface is, as Hammett stated, with Filter. 

We grab the CustomerId from the query string and use that to determine if that particular customer has a custom view using a simple folder structure: Views -> CustomerId -> Controller -> View.brail.  If no custom view is found, we instead use a 'base' view that is stored in an identical structure Views -> Base -> Controller -> View.brail.  Pretty straight forward.  We do the same thing for layouts.  Decorate your base controller with an AfterAction attribute and you are all set.

namespace My.Product.Mvc
{
  using System.IO;
  using Castle.MonoRail.Framework;

  public class CustomViewFilter : IFilter
  {
    public const string BASE = "Base";
    public const string LAYOUTS = "Layouts";

    #region IFilter Members

    public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller)
    {
      string customerId = context.Params["CustomerId"];
      return Perform(exec, context, controller, customerId);
    }

    #endregion

    public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller, string customerId)
    {
      // Use the Base or Customer specific view
      string viewName = null;
      if( null != controller.SelectedViewName )
      {
        viewName = Path.Combine(BASE, controller.SelectedViewName);
        if( null != customerId )
        {
          string customerViewName = Path.Combine(customerId, controller.SelectedViewName);
          if( controller.HasTemplate(customerViewName) )
          {
            viewName = customerViewName;
          }
        }
      }
      controller.SelectedViewName = viewName;

      // Use the Base or Customer specific layout
      string layoutName = null;
      if( null != controller.LayoutName )
      {
        layoutName = Path.Combine(BASE, controller.LayoutName);
        if( null != customerId )
        {
          string customerLayoutName = Path.Combine(customerId, controller.LayoutName);
          string customerLayoutPath = Path.Combine(LAYOUTS, customerLayoutName);
          if (controller.HasTemplate(customerLayoutPath))
          {
            layoutName = customerLayoutName;
          }
        }
      }
      controller.LayoutName = layoutName;

      return true;
    }
  }
}

Binsor the Friendly DSL

Oren posted quite a while ago on a little DSL for configuring Castle Windsor using Boo. He has creatively titled this DSL Binsor. I had previously used the Batch Registration facility because I did not want to maintain XML configuration for 75+ components.  Since I am revisiting the project now and updating to the trunk Rhino Tools I thought I would try out Binsor.  Below is the complete script I use to configure MonoRail with Windsor, configure logging, wire up the NHibernate Repository, load all of my MonoRail Controllers/ViewComponents, and all supporting components.  This script is derived from the Hibernating Forums binsor script and the source for the Batch Registration facility.

import System
import System.Reflection
import Castle.Facilities.Logging
import Castle.MonoRail.Framework
import Castle.MonoRail.WindsorExtension

Facility( "rails", RailsFacility)
Facility( "logging", LoggingFacility, loggingApi: "log4net", configFile: "My.Product.log4net")
Component("repository", IRepository, NHRepository)
Component("unitOfWorkFactory", IUnitOfWorkFactory, NHibernateUnitOfWorkFactory)

coreAsm = Assembly.Load("My.Product.Core")

for type in coreAsm.GetTypes():
  if typeof(Controller).IsAssignableFrom(type):
    Component(type.Name, type)
  elif typeof(ViewComponent).IsAssignableFrom(type):
    Component(type.Name, type)
  elif type.IsDefined(typeof(CastleComponentAttribute), false):
    compAttr = type.GetCustomAttributes(typeof(CastleComponentAttribute), false)[0] as CastleComponentAttribute
    Component(compAttr.Key, compAttr.Service, type, compAttr.Lifestyle)
 


You are viewing a mobilized version of this site...
View original page here

Mobilized by Mowser Mowser