Slide deck from my session on advanced skinning with DotNetNuke at SDN/OpenForce in Fall 2009.
View more presentations from Nik Kalyani.

DotNetNuke has powerful skinning support, but skin designers are often frustrated by their inability to customize various images with static links in the portal. In this post, I’ll suggest a technique and some code to enable skinning of such images.

Let’s first understand the problem. Say you have a DNN site and you have created a custom skin. You have tweaked the CSS to perfection and everything looks great. That is, until the user switches to Edit mode. At that point, various icons used by the framework are displayed on the page. These include icons for help, action icons such as Save, Cancel and Delete. If you want to change these images, your only option is to replace them at their default location. Unfortunately, this is not a long-term solution. Firstly, the change will affect all portals on that instance of DNN. Secondly, the next time you upgrade, the images will be over-written.

An ideal solution would be for you to include the desired images with your skin and have the framework use your images instead of the default. In order to do this on the server-side, there would be several changes needed to the DNN Core. And even if these changes were made, chances are the result would not be very performant.

My solution, called ImageSwap, is to leverage jQuery to create a simple, but effective client-side solution to the problem. The solution is something like this:

Step 1: Select all IMG elements in the page whose “src” attribute has a value containing a known path (example: images/)

Step 2: Iterate through the selected elements, and for each element:

a) Check if a similar named image exists in a sub-folder of the current skin

b) If No, then do nothing

c) If Yes, then change the “src” attribute to the URL of the image in the skin folder

That’s it….in two simple steps we have a solution to skin portal images.

To implement this solution, you will need to add some code to your skin. I have provided the requisite code for DNN4/DNN5. I’ll embed this code into a DNN5 Widget and post it here soon to make it even easier to use.

Implementation of ImageSwap

Step 1: Decide which portal images you want to replace, then create a sub-folder in your skin folder with the replacement images. For example, you might create a sub-folder named “portal/images.” Take care to name the files exactly the same as the originals.

Step 2: Add the following HTML to each layout file in your skin:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>

Step 3: Embed the following script in your skin:


var searchPath = "/images/";
var replacePath = "<%= SkinPath %>portal/images/";
(function($)
{
 $("img[src*='" + searchPath + "']").each(

 function()
 {
 var frags = $(this).attr("src").split("/");
 var oldsrc = $(this).attr("src");
 $(this).error(
 function()
 {
 $(this).attr("src", oldsrc);
 });
 $(this).attr("src", replacePath + frags[frags.length-1]);
 }
 );

})(jQuery);

You will want to change the values for “replacePath” so it corresponds to the actual folder name you use in your skin for the replacement images. If you are using DNN5, you can delete the script reference for jQuery as it is automatically referenced by DNN as long as widgets are enabled (the default).

I have attached a ZIP file that contains an HTML file that demonstrates this approach. In the file, there are several color icons rendered using the IMG element. In another folder, I have grayscale images of all but two of the icons. When you display the page in your browser, you will see that all but two of the color icons are replaced with their grayscale versions, demonstrating how the ImageSwap script functions.

If you use the script, please leave a comment about your experience so I can improve the script and the ImageSwap widget that I will be creating for DNN5.

Navin Pathuru asks:

Currently we have a requirement where our client wants us to do something about loading Dnn in Netscape 4.x version. We know Dnn uses a lot of client side javascript so there are going to be problems with this. But we just
want to do our best. The approach we want to take is to figure out the browser version and load a different
skin one which has really minimal graphics when we detect it is Netscape 4.x. I am wondering if you will
be able to write your thoughts about this approach in your blog.

Nik’s response:

Navin, this is a great question. Before I answer it, I do want to point out that most of the client-side Javascript you see on a DNN page is emitted by ASP.Net, the menu and other modules. DNN itself does not generate a lot of client-side script, especially if you disable the Client API.

Now, to answer your question. Since we have the DNN source code, the dime answer is change the core to suit your needs. But this is neither fun nor practical. Let’s go on to the dollar answer.

DNN uses the tab settings (inherited or set directly) to determine which skin is to be displayed. You can override this with querystring parameters to force a certain skin to be displayed. Also, due to its IBuySpy legacy, DNN has the rather unfortunate artifact called the Admin skin which forces a skin switcheroo whenever a non-View module control is displayed. (I think this will go away as part of DNN’s continued evolution.)

So, if there is no way to dynamically switch the skin, how is this problem to be solved. After noodling about this for a minute, it occurred to me. Although DNN allows skins to be intelligent, most skins are dumb, i.e. they intersperse some DNN controls, HTML, images and CSS to render a page layout. Most skins do not take advantage of their ability to do anything ASP.Net allows by virtue of their being usercontrols. This is precisely what we can do to solve the problem at hand.

The solution consists of a single file that I’ll call a “Skin Proxy.” The Skin Proxy can be used to dynamically select a skin based upon any user, portal, tab or browser properties. In my example, I have used a browser property since that was the focus of Navin’s question. Basically, the Skin Proxy creates a string based on some browser properties and then attempts to load a control (i.e. a skin) with a name matching the string.

SkinProxy.ascx

<%@ Control language="c#" %>
<script>// <![CDATA[

protected void Page_Load(object s, EventArgs e)
{
string browser = Request.Browser.Browser.ToLower();
string version = Request.Browser.MajorVersion.ToString();</font>

     if (Request.QueryString["debug"] != null)
Response.Write(browser + version + ".ascx");
else
{
try
{
this.Controls.Add(this.LoadControl(browser + version + ".ascx"));
}
catch
{
this.Controls.Add(this.LoadControl("Default.ascx"));
}
}
}
// ]]></script>

In this code, the first couple of lines get the browser and browser major version into string variables. Then, an attempt is made to load a control whose name corresponds to the concatenated browser and version. If this is unsuccessful, a control named “Default.ascx” is loaded. (If you add a querystring variable named “debug” the script reports the name of the control that would be loaded instead of actually loading it. This is helpful for figuring out what to name your control.)

I tested this out with IE and Firefox and everything seemed to work OK. Please do post here if you find problems with the script.

© 2012 TechBubble Suffusion theme by Sayontan Sinha