DotNetNuke supports Razor

On January 13th at CodeMash in Sandusky, Ohio, Microsoft introduced Razor – a new lightweight syntax for developing WebPages as part of its WebMatrix offering. In a separate article in this issue, Andrew Nurse, one of the developers who created Razor describes the motivations behind this new syntax and an overview of the syntax itself. In this article I will focus on how you can use Razor scripts in DotNetNuke 5.6.1.

The Razor Host Module
In addition to the new bits that are available as part of the WebMatrix suite, the Razor parser and the new WebPages Framework requires ASP.NET 4.0. As we still wish to retain compatibility with ASP.NET 3.5 we cannot directly integrate support for Razor scripts into the DotNetNuke core, as this will force all of our users to upgrade to ASP.NET 4.0.
 
Therefore, our approach to providing support for Razor scripts is to provide a “Razor Host” module. If DotNetNuke users wish to use Razor scripts as part of their sites, they will need to install this module. 
 
A Beta of this module has been available since early December and was used as the basis for the recent Hackathon contest. However, DotNetNuke 5.6.1 will contain an initial release of the Razor Host module.
Adding the Razor Host Module to a Page
An instance of this module can be added to a DotNetNuke page in the usual way (see Figure 1).
 
 
Figure 1: Adding an instance of the Razor Host module to a page.
 
The resulting module looks pretty sparse (see Figure 2). The body of the module does not display anything – as no script is selected -  and there is only a single Action – Edit Script – which allows Host users to create and edit scripts.
 
Figure 2: The Razor Host Module on a page.
 
“Only Host Users can Create or Edit Razor Scripts”
 
It is very important to note that the only users who will see the “Edit Script” Action button or menu item are Host (or Super) Users. This is because you can do anything in Razor script, so only users with the highest level of access should be able to create Razor scripts.
 
Configuring the Razor Host Module
As with most modules in DotNetNuke the Razor Host module does have settings – actually a single setting – the ability to select a previously created Razor script (see Figure 3).
 
Figure 3: Setting the Razor Script to use.
 
While editing and creating scripts can only be done by a Host User, any user with Edit permissions can select an existing script.
 
In the example shown in Figure 3, selecting the PageInfo.cshtml script will result in the module displaying the content shown in Figure 4.
 
Figure 4: The Rendered Razor Script.
Creating and Editing Razor Scripts
So selecting a previously created script is fairly easy, but how can we edit or create scripts. When logged in as a Host user, selecting the previously described “Edit Script” Action will bring up the “Edit Script” control (Figure 5).
 
Figure 5: Editing a Razor Script using the Edit Script control.
 
This control allows you to:
 
·         Add a new Script File
·         Choose a Script file to Edit.
·         Modify the Script source in the Text Area.
·         Set the current Script to be the Active Script (Is Active) – as an alternative to choosing the “Active” script in the Module Settings.
·         Save the Modified Script.
 
The script files are all stored in the “Scripts” sub-folder.
 
The Text Area does not provide any Syntax Highlighting (or Syntax checking). But if you want some “Syntax Highlighting” you can use the WebMatrix IDE to modify the script files (Figure 6) or even Visual Studio 2010.
 
Figure 6: Editing a Razor Script using the WebMatrix IDE.
 
Note that in the WebMAtrix IDE the C# code has a different highlighting than the HTML.
Hosting the Razor Engine
So far we have seen how we can use the Razor Host Module, but how is this achieved. 
 
Razor can actually be thought of as a templating engine and if you are planning on building a module that requires templates you might want to consider Razor. So, by describing how we host the Razor Engine hopefully I will be able show how you might do that. 
The WebPage Class
The WebPage Class (actually WebPageBase) is the basis of the new WebPages Framework, in the same way that the “Page” class is the basis of the WebForms Framework.
 
Essentially the approach we use in our “Razor Host” module is as follows.
 
1.    Identify the Razor script file (e.g. Twitter.cshtml)
2.    Call BuildManager.GetType(scriptFile) to create an instance of WebPage. Note that the System.Web.WebPages assembly contains a special class – PreApplicationStartCode – that ensures that the extensions cshtml and vbhtml are registered with the BuildManager
3.    Call the ExecutePageHierarchy method of the WebPage class and capture the rendered content.
4.    Create a LiteralControl with the rendered content and add it to the control tree.
 
In order to achieve this there are two new classes in a new assembly (DotNetNuke.Web.Razor.dll) which is distributed as part of the “Razor Host” Module.
 
1.    RazorModuleBase – this class implements IModuleControl by sub-classing ModuleUserControlBase
2.    DotNetNukeWebPage – this class subclasses WebPageBase and provides some DNN specific enhancements.
 
The RazorModuleBase Class
The RazorModuleBase class is where everything comes together. All DNN Modules need to implement the IModuleControl Interface – this is how the module injection logic knows what to inject. RazorModuleBase is a new base class that implements IModuleControl and is used as the base class for the Razor Host Module.
 
In the OnPreRender method of the class we first check if our script - RazorScriptFile – exists. If it does we call the CreateWebPageInstance method to get an instance of the DotNetNukeWebPage.
 
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
 MyBase.OnPreRender(e)
   
 Try
    If File.Exists(Server.MapPath(RazorScriptFile)) Then
     
      Dim instance As Object = CreateWebPageInstance()
      If instance Is Nothing Then
        Throw New InvalidOperationException( _
                         String.Format( _
                         CultureInfo.CurrentCulture, _
                         "The webpage found at '{0}' was not created.", _
                         RazorScriptFile))
     End If
    
     Dim webPage As DotNetNukeWebPage =
                                 TryCast(instance, DotNetNukeWebPage)
    
     If webPage Is Nothing Then
       Throw New InvalidOperationException( _
              String.Format( _
              CultureInfo.CurrentCulture,
              "The webpage at '{0}' must derive from DotNetNukeWebPage.",
              RazorScriptFile))
      End If
    
       webPage.SetContext(Me.ModuleContext)
       InitHelpers(webPage)
    
       Dim writer As New StringWriter
       webPage.ExecutePageHierarchy(
                  New WebPageContext(HttpContext), writer, webPage)
    
       Controls.Add(
                  New LiteralControl(Server.HtmlDecode(writer.ToString())))
     End If
     Catch ex As Exception
        ProcessModuleLoadException(Me, ex)
     End Try
    
 End Sub
Listing 1: The OnPreRender method of the RazorModuleBase class
 
We then set the WebPage’s module context by calling its SetContext method. This allows us to access the ModuleContext in Razor script. Finally we call the WebPage’s ExecutePageHierarchy method to get the rendered content, which we add as a LiteralControl to the Controls collection of the module.
 
In the above description I glossed over two important points: the RazorScriptFile property and the CreateWebPageInstance property.
The RazorScriptFile Property
The RazorScriptFile property (Listing 2) returns the virtual path of a “default” script file. In the Razor Host module, we override this property and return the currently selected script, but the default behaviour in the base class is to return “MyScript.cshtml” if the current control is “MyScript.ascx”.
 
Protected Overridable ReadOnly Property RazorScriptFile As String
 Get
    Return Me.AppRelativeVirtualPath.Replace("ascx", "cshtml")
 End Get
End Property
Listing 2: The RazorScriptFile property
The CreateWebPageInstance Method
The CreateWebPageInstance method (Listing 3) uses the RazorScritpFile property to create an instance of a DotNetNukeWebPage (Listing 3), calling BuildManager.GetCompiledType, which returns a Type and then calling Activator.CreateInstance to return an instance of the DotNetNukeWebPage.
 
Private Function CreateWebPageInstance() As Object
 Dim type As Type = BuildManager.GetCompiledType(RazorScriptFile)
 Dim instance As Object = Nothing
    
 If type IsNot Nothing Then
    instance = Activator.CreateInstance(type)
 End If
    
 Return instance
End Function
Listing 3: The CreateWebPageInstance method
 
Earlier in this article I mentioned that BuildManager knew to “build” a WebPage instance from a cshtml or vbhtml because of some code that exists in the WebPages Framework that registers the extension.
 
So why do we get an instance of DotNetNukeWebPage rather than an instance of WebPage?
 
The answer lies in the included web.config file (Listing 4) that sits in the same folder as the Razor Host Module. By default the Razor Engine will return an instance of WebPage, but this can be modified in the new system.web.webPages.razor section of web.config, where we have indicated that an instance of DotNetNukeWebPage should be returned.
 
<system.web.webPages.razor>
 <pages pageBaseType="DotNetNuke.Web.Razor.DotNetNukeWebPage">
    <namespaces>
      <add namespace="Microsoft.Web.Helpers" />
      <add namespace="WebMatrix.Data" />
    </namespaces>
 </pages>
</system.web.webPages.razor>
Listing 4: The Razor Host Module’s web.config file
Conclusion
Razor is an exciting addition to our list of technologies that we can use to create dynamic webistes. By hosting the Razor Engine in DotNetnuke, the Razor Host Module can be used to host Razor scripts in DotNetNuke 5.6.1. 
 
Take the opportunity to investigate Razor by installing the Razor Host Module and play with the included script files – or goto the DotNetNuke site (www.dotnetnuke.com) and download one of the Razor Hackathon entries.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar