DNN Community Blog

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.


DDRMenu Razor Templates in DotNetNuke 7.x

One of the lesser known, but very powerful template options of the DotNetNuke DDR Menu, originally created by Mark Allan, is Razor. A while back, Joe Brinkman had a very interesting blog post, combining razor templating with taxonomy to come to very advanced menu layouts.

Sadly, DotNetNuke 7.0 introduced a breaking change in the way Razor was supported that specifically impacted the DDR Menu implementation of it. Although the issue had been on our radar for some time, it was not a very easy one to fix… Well.. the wait is over, in DotNetNuke 7.0.4, this is finally fixed.

Also, this fix has been released to the CodePlex DDRMenu project, as version 2.0.3

Symptoms and causes

How would you know you are using a skin that depends on a Razor menu template? Typically, you would get any of these 3 errors:

  1. The "[email protected](node)" element was not closed. All elements must be either self-closing or have a matching end tag.
  2. Could not load file or assembly 'System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
  3. error CS0103: The name 'Model' does not exist in the current context

Two different issues are were involved with these error. First, DotNetNuke 7 changed from referencing System.Web.Pages 1.0 to version 2.0, where small changes in the Razor parser caused this to no longer work:

<[email protected] class="[email protected]">

Correct new markup would be:

<ul @topLevelId class="[email protected]">

(space between ul and @topLevelId).

Whilst this is an easy one to fix, the second issue was not. As it turned out, DDRMenu had a hard dependency on System.Web.Pages 1.0, and did not use the built in DotNetNuke wrapper methods for Razor.

This is the way the old code instantiated a WebPage:

This code was called by the Render method of the RazorTemplateProcessor:

In this case, the razor object is instantiated using reflection:

The problem with this code is that it uses MVC directly and is not using the DotNetNuke Razor engine, so when we upgraded to .NET 4, WebAPI and removed MVC from the installation, this code broke. The easiest way to fix this is to use the core Razor engine, which would make this more robust for potential future changes as well.

New Code

The old Razor class is now obsolete and everything is now done inside the RazorTemplateProcessor

The new Render method now looks like this:

This directly calls the DotNetNuke Razor Engine using the Render method that supports Generics (razorEngine.Render<dynamic>(writer, model);). This allows us to directly pass in the Model which will subsequently be passed into the WebPage.

This is the DotNetNuke.Web.Razor.RazorEngine.Render<T> method:

image

As you can see, the Model is passed in and also to the WebPage.

Impact on templates

Sadly all of this means that the DDRMenu Razor templates need to be updated as well. In order to make the Model available inside the Razor script properly, this needs to be added at the top of the script:

@using System.Dynamic;
@inherits DotNetNuke.Web.Razor.DotNetNukeWebPage<dynamic>

We are simply telling the script that it inherits from the generic DotNetNukeWebpage.

The other breaking change is not caused by us, but by the switch from Webpages 1.0 to Webpages 2.0. Apparently, under webpages 1.0 this was valid markup:

<[email protected] class="[email protected]">

In webpages 2.0 that is not valid anymore, and should be this:

<ul @topLevelId class="[email protected]">

Sample Template

This is a sample to show the impact. This template is taken from the DDRMenu Template examples project The old template (pre-DNN 7) lines are highlighted to show problems:

@using DotNetNuke.Web.DDRMenu;
@{ var root = Model.Source.root; }

@helper RenderNodes(IList<MenuNode> nodes) {
    if (nodes.Count > 0) {
        < ul>
            @foreach (var node in nodes) {
                var cssClasses = new List<string>();
                if (node.First) { cssClasses.Add("first"); }
                if (node.Last) { cssClasses.Add("last"); }
                if (node.Selected) { cssClasses.Add("selected"); }
                var classString = new HtmlString((cssClasses.Count == 0) ? "" : (" class=\"" + String.Join(" ", cssClasses.ToArray()) + "\""));
                <[email protected]>
                    @if (node.Enabled) {
                        <a href="@node.Url">@node.Text</a>
                    } else {
                        @node.Text
                    }
                    @RenderNodes(node.Children)
                </li>
            }
        </ul>
    }
}
@RenderNodes(root.Children)

Updated template for DotNetNuke 7 (highlighted lines are the lines with additions / changes):

@using DotNetNuke.Web.DDRMenu;
@using System.Dynamic;
@inherits DotNetNuke.Web.Razor.DotNetNukeWebPage<dynamic>

@{ var root = Model.Source.root; }

@helper RenderNodes(IList<MenuNode> nodes) {
    if (nodes.Count > 0) {
        < ul>
            @foreach (var node in nodes) {
                var cssClasses = new List<string>();
                if (node.First) { cssClasses.Add("first"); }
                if (node.Last) { cssClasses.Add("last"); }
                if (node.Selected) { cssClasses.Add("selected"); }
                var classString = new HtmlString((cssClasses.Count == 0) ? "" : (" class=\"" + String.Join(" ", cssClasses.ToArray()) + "\""));
                <li @classString>
                    @if (node.Enabled) {
                        <a href="@node.Url">@node.Text</a>
                    } else {
                        @node.Text
                    }
                    @RenderNodes(node.Children)
                </li>
            }
        </ul>
    }
}
@RenderNodes(root.Children)

 

Comments

Kevin Forte
Erik,

Thank you for this post. I am using this Razor script for my menu and it seems to be working great. Can you tell me how to access the old "Icon" and "IconLarge" properties from the DDR menu? I tried this:

@node.Text

And this did not work.

Any assisstance would be very helpful.
Kevin Forte Friday, January 17, 2014 4:50 PM (link)
Behnam Emamian
tnx, compact and perfect ;-)
Behnam Emamian Friday, June 5, 2015 2:54 AM (link)

Comment Form

Only registered users may post comments.

NewsArchives


Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (32)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (209)
Chris Paterra (55)
Clinton Patterson (40)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (154)
Dave Buckner (2)
David Poindexter (4)
David Rodriguez (3)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (10)
George Alatrash (6)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (165)
William Severance (5)

Content Layout

Subscribe to DNN Digest

DNN Digest is our monthly email newsletter. It highlights news and content from around the DNN ecosystem, such as new modules and themes, messages from leadership, blog posts and notable tweets. Keep your finger on the pulse of the ecosystem by subscribing.  


Copyright 2017 by DNN Corp Terms of Use Privacy
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out