DotNetNuke Skin Proxy Re-visited
Some years ago, I had presented a solution for dynamically loading a skin layout based on the user’s browser type. Fast-forward to the present — at the Fall 2009 OpenForce Conference in Amsterdam I had a chance to speak to Armand Datema (@nokiko) on the same topic. The conversation occurred following my session on Advanced Skinning with DotNetNuke where I presented an early prototype of “Skinfigurator,” my module for rule-based skin loading. Armand was looking for a solution to dynamically choose a skin at run-time while overcoming the pesky ContentPane issue. If you are unfamiliar with the issue, here’s a quick synopsis…
DotNetNuke skins require the presence of a container HTML element with an ID of “ContentPane” and a runat=”Server” attribute. This is fine for most skins as it’s no big deal to define an area that serves as the default location for content (i.e. ContentPane). In the case of dynamic skin proxies (i.e. skins that load a layout based on some arbitrary set of conditions) this requirement is a problem since different layouts may want the ContentPane to be located in different places within the layout HTML.
In any event, as a result of my tinkering with skin proxies I found a clean solution for this problem. Ultimately I decided not to use it for Skinfigurator (more about the how/why in another post), however the technique is still quite useful. I promised Armand I would share it with him, and although I have been slow in making this post, it’s better late than never 🙂
The solution is trivially simple and involves just one file — the skin proxy layout. Basically what this proxy does is tap into the Init event in the page life-cycle to dynamically load the skin layout control. Since this event happens prior to the point where the DotNetNuke framework skin loader does its thing, you don’t have to bother with creating ContentPane elements in the skin proxy layout. All you need is the logic for dynamically selecting which layout to display to the user. Your Here’s the code for the skin proxy layout (I called mine LayoutSelector.ascx)
<%@ Control language="vb" AutoEventWireup="false" Explicit="True" Inherits="DotNetNuke.UI.Skins.Skin" %> <script runat="server"> Protected Overrides Sub OnInit(ByVal e As System.EventArgs) Dim layout = "Portal.ascx" If (Request.Querystring("layout") <> "") Then layout = Request.Querystring("layout") + ".ascx" End If Controls.Add(LoadControl(TemplateSourceDirectory + "/layouts/" + layout)) End Sub </script>
In my example, I have the code checking for a querystring variable and loading a control based on the value provided. Of course, this is not something you will want to do in production use. More likely you will want to have conditional logic based on the user, portal, browser, tab or some other controlling factor that determines which skin layout will be loaded.
There is one other thing to be aware of that is related to usability. By default, DotNetNuke lists all layouts (i.e. user controls) it finds in a skin folder in any skin layout selector in the UI. To ensure that your proxy logic is used, you will want your proxy layout control to be the only control in the skin’s root folder. All the dynamically selectable layouts should be in a sub-folder. In my example, I use a sub-folder called “layouts.” The folder structure for your skin will look something like this:
Using this approach, the user will only be able to choose “LayoutSelector” and the Portal, Portal2 and Portal3 layouts will be hidden from the skin layout selector UI.