St. Louis Day of .NETMy session today at the St. Louis Day of .NET conference was on the topic “Backwards Compatible HTML5 on ASP.NET.” Here is the slide deck and source code from the session.

Adam and Eve in Fall of ManIn an average week, I communicate on the phone, by email, through webinars and in person, with some 100 DotNetNuke business customers, prospects and partners. A majority of them use DotNetNuke in some mission-critical way. I enjoy these conversations and get many insights on their real-world experience with DotNetNuke. I have noticed that there are some common things that end-users find problematic with Core and/or third-party extensions they use with DotNetNuke. It occurred to me that it would be good to share this information since not all extension builders may be aware of the issues that sometimes arise due to the way a particular extension is built. In order to make the information easy to digest (and because lists are cool), I am going to present these observations using a “Seven Sins…” theme.

Let’s start with the Seven Sins of DotNetNuke Module Developers:

Inline Style Attributes

Sin: Using inline style attrbutes in your HTML markup is probably the single, biggest problem you can create for your end-users. Since such attributes cannot be overridden with CSS, users are forced to edit the markup, use Javascript or the StyleScrubber Widget to neutralize the impact of a module’s inline style attribute on other elements of a page.

Sin Level: Annoyance

Absolution: Avoid using in-line style attributes in your module’s static and dynamic HTML markup. Instead, create style definitions in your module.css file that target your module’s markup. While you can use “class” and “ID” attributes throughout your markup, it is best to keep your markup clean and use the various CSS selectors to achieve the desired styles. An approach that works well is to create a “wrapper” DIV for your module’s markup with a “class” attribute value that closely matches your module name (for example: <div class=”Acme-Survey-Module”>). You can then have styles in your module.css like this:

.Acme-Survey-Module div  {
       padding-bottom: 10px;
}

And since we are on the topic of style-related sins, please do not specify any font-related style attributes in your style definitions. Let the page skin’s font definitions rule. If you absolutely must change the text size, at least use “em” units instead of pixel or point units so that the text size is relative instead of absolute.

Unfiltered User Input

Sin: Assuming that user input is safe and processing or persisting it without filtering, potentially compromising security and making a user’s DotNetNuke installation, server and network vulnerable to attacks.

Sin Level: Deadly

Absolution: DotNetNuke has built-in features, including an input filter that is incredibly easy to use for filtering strings. Use it to filter user input and make your modules more secure.

Hard-coded Text

Sin: Hard-coding user interface text in HTML markup or in code, preventing localization and customization of text.

Sin Level: Aggravating

Absolution: Do not assume that everyone speaks the same language as you, and even if so, that they like the error prompt “Yo…you messed-up, fix it already.” Ensure that all user interface text follows well-established best-practices for DotNetNuke Localization.

UpdatePanel

Sin: Using an UpdatePanel instead of writing the code necessary to properly implement AJAX functionality. If your module is doing something trivial, by all means, use UpdatePanel. But for a module that is potentially going to be used in high-traffic scenarios, UpdatePanel is a no-go. UpdatePanel was intended to be a quick and easy way to implement AJAX functionality — it would not be a stretch to call the UpdatePanel solution LAJAX (lazy AJAX). Unfortunately, it has become the standard way in which most ASP.NET developers implement AJAX functionality. For every AJAX request, UpdatePanel causes the entire page life-cycle to be repeated on the server. As a result, a module that has lots of AJAX functionality implemented using UpdatePanel is going to cause significant performance issues and unnecessary resource utilization when used in a high-traffic environment.

Sin Level: Aggravating

Absolution: As I mentioned, if your module is doing something trivial, UpdatePanel is a reasonable solution. For anything else, it’s better to use ASP.NET AJAX or jQuery on the client-side making requests to an HttpHandler or Webservice to obtain the needed information for the UI in JSON or XML format. Here’s an example of how this can be done in a DotNetNuke module.

Non-Semantic Markup

Sin: Using HTML markup for layout, instead of CSS, provides no semantic cues to user agents (browsers) as to the intended purpose of content. Most online discussions about semantic HTML markup degrade very quickly into near-religious arguments about TABLE versus DIV element. Such discussions are akin to throwing the baby out with the bath-water. Semantic markup is not solely about using tables for layout. The bigger issue is that HTML was never intended to convey presentation fidelity to the degree seen on modern web pages. Its primary purpose was to give semantic meaning to text so that user agents could then render the text in the most appropriate way for that agent. When markup is created with an emphasis on layout, it strays from this purpose. For instance, if your module markup uses an H1 element for content that is not the primary headline on the page, your markup is non-semantic and is misguiding user agents such as screen readers.

Sin Level: Annoyance

Absolution: I don’t view the issue of semantic HTML markup as a black-and-white one. I think it’s OK to take occasional liberties with markup to save time, after all time is money. But every developer should make a good-faith attempt to produce more semantic markup. Take a look at your module code and see if there are opportunities to replace HTML markup used for layout with CSS (without giving yourself a case of DIVitis). Low hanging fruit includes H*, B and I tags and input forms. For forms, try ditching the all-too-common TABLE found in most DotNetNuke module forms and replacing it with the FIELDSET element along with some CSS. For a good example of this, refer to the DotNetNuke Blueprint project.

Custom Settings

Sin: Using a custom, non-standard user interface for managing module settings.

Sin Level: Aggravating

Absolution: DotNetNuke provides a simple and automatic mechanism for module developers to implement a user interface for managing module settings. Developers should not circumvent it as it adversely impacts usability of the platform. It takes time for users to get familiar and comfortable with standard behaviors. Each time a user encounters a module that ignores the standard and does something custom with the user interface, it causes the user to get frustrated with the platform (i.e. DotNetNuke). This is because, contextually, he/she has no knowledge of the separation between the platform and the module. As far as the user is concerned, DotNetNuke appears to have an inconsistent user interface (which it well might, but that’s another discussion). Why force the user to click on a custom link or button to configure your module’s settings? Let the framework do its thing and inject your settings control at the end of the standard module settings so that the user can customize module settings in the same way, no matter which module they are using.

No script tokens

Sin: Using “dbo” in module SQL scripts instead of {objectQualifier} and {databaseOwner}. If your module SQL scripts use “dbo” for the object owner your module cannot be easily installed in environments where the SQL connection uses a different database owner. If your module SQL scripts do not use the {objectQualifier} prefix for database objects, your module cannot be used in shared database scenarios.

Sin Level: Aggravating

Absolution: Follow DotNetNuke best practices for SQL scripts by using {objectQualifier} and {databaseOwner} prefixes in your module’s SQL scripts to ensure that your module can be installed in any environment where DotNetNuke is able to function.

There you have it…the seven sins of DotNetNuke module developers. What other cringe-worthy things have you seen in DotNetNuke modules?

Next up, the seven sins of DotNetNuke Skin Designers.

Here’s an illustration depicting a common scenario for DotNetNuke Deployment in the Enterprise. Click the image for a higher-res version. If you find the image helpful, feel free to use it as needed without any restrictions or attribution requirements.

DNN-Deployment-Scenario

If you are using IIS7, it’s very easy to ensure that all requests always go to your preferred canonical URL. It’s a two step process:

Step 1: Install the UrlRewrite module for IIS: http://www.iis.net/expand/URLRewriteIIS Rewrite

Step 2: Add the following rule to your applications web.config file:

&lt;configuration&gt;
 &lt;system.webServer&gt;
    &lt;rewrite&gt;
      &lt;rules&gt;
        &lt;clear /&gt;
        &lt;rule name=&quot;Redirect from www&quot; stopProcessing=&quot;true&quot;&gt;
           &lt;match url=&quot;.*&quot; /&gt;
           &lt;conditions&gt;
              &lt;add input=&quot;{HTTP_HOST}&quot; pattern=&quot;^www.yoursite.com$&quot; /&gt;
           &lt;/conditions&gt;
           &lt;action type=&quot;Redirect&quot; url=&quot;http://yoursite.com/{R:0}&quot; redirectType=&quot;Permanent&quot; /&gt;
        &lt;/rule&gt;
     &lt;/rules&gt;
   &lt;/rewrite&gt;
 &lt;/system.webServer&gt;
&lt;/configuration&gt;

IMPORTANT: When using the above code, take care to merge it with your existing web.config without duplicating any existing elements.

I like my URL’s to be in the format http://mysite.com. If you prefer http://www.mysite.com, simply remove “www.” from the <add> element and add it to the <action> element’s “url” attribute.

This is the last post in my four-part series on DotNetNuke Widgets. Here’s a review of the other posts in this series:

  • Part 1 – Overview of DotNetNuke Widgets
  • Part 2 – DotNetNuke Widgets reference
  • Part 3 – Insights into how you can develop your own Widgets for DotNetNuke

In this post, I’ll walk you through the code of the TechBubble CatalogWidget. This Widget displays a carousel of Product thumbnail images. You can click on any thumbnail to see a larger version of the image. You can then move your cursor over any portion of the larger image to see a zoomed in image of the area below the cursor. I created this Widget to convey multiple concepts:

  • Passing data through Widget markup parameters
  • Using a Widget to integrate multiple jQuery plugins seamlessly
  • Using behavior injection to dynamically add event-handling to UI elements
  • Dynamically injecting stylesheets and scripts into the page
  • Using jQuery UI themes

Let’s start by looking at a mockup of the Widget (created using my favorite mockup tool – Balsamiq).

CatalogWidget Mockup

In order to implement this UI, I decided to use two jQuery plugins:

(These are arbitrary choices…I am sure that there are other plugins that would work equally well or better.)

I have setup two live demos of the Widget so you can get a feel for the Widget’s UI:

You can download the Widget at the link below and follow along with the technical discussion that follows. I have also included a short slideshow that shows a screen grab of the installation process and the two demos.

[smooth=id:1;]

Here are the properties that supported by the CatalogWidget:

photoUrl (required) – A URL to the location where the images for the Widget will be found. The Widget uses the following convention for image names: {label}.{extension} for hi-res image displayed in zoom area, {label}_thumb.{extension} for thumbnail image and {label}_small.{extension} for small image displayed when thumbnail is clicked. {label} and {extension} are explained in the description for other parameters.

theme (optional, default=ui-lightness) – Name of the jQuery UI theme that should be loaded from Google’s CDN for jQuery themes. The list of supported themes can be found on the jQuery Themeroller page. The theme name should match the defined name on the Themeroller page, with spaces in the name replaced with the hyphen character.

carouselWidth (optional, default=500) – Width of the carousel used to display the thumbnails in pixels. The number of thumbnails displayed will depend on the width of each thumbnail and the value of this parameter.

zoomWidth (optional, default=250) – Width of the zoomed image area in pixels.

zoomHeight (optional, default=250) – Height of the zoomed image area in pixels.

moreInfoHandler (optional) - Name of a function that will be called with a parameter of {label} when the information icon that appears to the right of the product name link is clicked. If this parameter is not specified, the information icon will not be displayed.

extension (optional, default=jpg) – The file extension for all images.

Product Data – In addition to these parameters, the Widget supports an unlimited number of additional product data parameters. Any parameter specified other than the above, is treated as product data. The value of the “name” attribute of the parameter will be used for the product {label} and the value of the “value” attribute will be used for the product’s descriptive name. Example: <param name=”ABC1000″ value=”ABC 1000 Super Duper Product” />.

OK, now we have that out of the way, let’s get started with building the Widget. The Widget is going to exist in the namespace “TechBubble.Widgets,” so by convention, the Widget file will be named “TechBubble.Widgets.CatalogWidget.js” and it will be located in ~/Resources/Widgets/User/TechBubble. All resources for this Widget will be in ~/Resources/Widgets/User/TechBubble/CatalogWidgetResources. Here’s the code for namespace registration and the Widget constructor:

Type.registerNamespace(&quot;TechBubble.Widgets&quot;);
TechBubble.Widgets.CatalogWidget = function(widget)
{
    TechBubble.Widgets.CatalogWidget.initializeBase(this, [widget]);
    this.catalogResourcesUrl =
                    $dnn.baseResourcesUrl +
                    &quot;Widgets/User/TechBubble/CatalogWidgetResources/&quot;;
    this.photoUrl = &quot;&quot;;
    this.theme = &quot;ui-lightness&quot;;
    this.carouselWidth = &quot;800&quot;;
    this.zoomWidth = &quot;250&quot;;
    this.zoomHeight = &quot;250&quot;;
    this.moreInfoHandler = &quot;&quot;;
    this.extension = &quot;jpg&quot;;
    this.products = [];
    this.hasProducts = false;

    if (!TechBubble.Widgets.CatalogWidget.Initialized)
    {
        $.getScript(this.catalogResourcesUrl +
                    &quot;scripts/jQuery-ui-1.7.2.custom.min.js&quot;);

        $.getScript(this.catalogResourcesUrl +
                    &quot;scripts/jquery.metadata.js&quot;);

        TechBubble.Widgets.CatalogWidget.Initialized = true;
    }
}
TechBubble.Widgets.CatalogWidget.inheritsFrom(
            DotNetNuke.UI.WebControls.Widgets.BaseWidget);
TechBubble.Widgets.CatalogWidget.registerClass(
            &quot;TechBubble.Widgets.CatalogWidget&quot;,
            DotNetNuke.UI.WebControls.Widgets.BaseWidget);

In the constructor, we define default values for the various properties (parameters) that the Widget supports. In addition the variable TechBubble.Widgets.CatalogWidget.Initialized is used as a flag to prevent the jQuery UI and jQuery metadata plugin from being loaded. A nice optimization for this code would be to check for the actual existence of the plugin, instead of using a flag. In addition to the constructor, the calls to inheritsFrom() and registerClass() are standard calls for every Widget for setting inheritance and registering the class.

TechBubble.Widgets.CatalogWidget.prototype =
{
    render:
        function()
        {
            // Parse widget parameters
            this._getParams();

            // Load some sample data to display if none
            // has been specified
            if (!this.hasProducts)
                this._getSampleData();

            // Create a DIV element and swap out the Widget's
            // defining &lt;object&gt; element with it by calling
            // the base render() method (can't use jQuery
            // shortcut to create element)
            var div =document.createElement(&quot;div&quot;);
            div.setAttribute(&quot;style&quot;,&quot;width:&quot; + this.carouselWidth + &quot;px&quot;);
            TechBubble.Widgets.CatalogWidget.callBaseMethod(this, &quot;render&quot;, [div]);

            this._renderCarousel();

            this._renderZoomer();
        },

        _getParams:
        function()
        {
		// Enumerate and retrieve parameters
		// . . . code omitted
        },

        _renderCarousel:
        function()
        {
		// Render the Carousel plugin
		// . . . code omitted
        },

        _renderZoomer:
        function()
        {
		// Render the Zoomer plugin
		// . . . code omitted
        },

        _injectStyleSheet:
        function(name, isTheme)
        {
		// Inject a stylesheet into the page
		// . . . code omitted
        },

        _getThumbnailList:
        function(list, photoUrl)
        {
            return(productList);
        },

         _getSampleData:
         function()
         {
		// Use and display Widget with sample data
		// . . . code omitted
         }
}

Next, let’s review the code for the Widget’s prototype where the required method render() is declared along with various private methods (prefixed with underscore). In the render() method, we start by getting the parameters. Then, if no products are defined, we use some sample product information. Next, we swap out the <object> element defining the Widget with a <div> element. This <div> element will become the parent container for the rest of the Widget which is rendered in the _renderCarousel() and the _renderZoomer() methods. Next in the code are a number of helper methods. I won’t go into a line-by-line explanation, but highlight a few things I think are noteworthy:

Script Injection: I use the jQuery method $.getScript(scriptUrl, anonymous function) in several places. This is a useful method when you want to inject a script into the DOM and want to ensure that any dependent code is not executed until the asynchronous loading of the script is completed. By putting all the dependent code in an anonymous function defined in the second parameter of this method I could achieve this goal.

Behavior Injection: In _renderZoomer(), I iterate through each thumbnail in the carousel and use the shortcut for the jQuery bind() method $(this).click() to inject a click handler for each of the thumbnail images. This is cleaner and more efficient than adding an onClick attribute when defining the HTML markup for each thumbnail.

DotNetNuke.UI.WebControls.Widgets.renderWidgetType(&quot;TechBubble.Widgets.CatalogWidget&quot;);

We wrap-up the Widget code by telling the Widget framework to render all Widgets of the type TechBubble.Widgets.CatalogWidget.

This also wraps-up my series on DotNetNuke Widgets. I hope you found the content and examples useful and are inspired to build your own DotNetNuke Widgets.

© 2012 TechBubble Suffusion theme by Sayontan Sinha