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:

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

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.

Widgets

Continuing my series on DotNetNuke Widgets, here is Part 3 where I provide insights into how you can develop your own Widgets for DotNetNuke. If you haven’t already done so, read Part 1 (overview of DotNetNuke Widgets) and Part 2 (DotNetNuke Widgets reference) to better understand the concepts explored in this post. Let’s get started.

Location and Naming Conventions

Widgets are located in two places: ~/Resources/Widgets/DNN for Core Widgets and ~/Resources/Widgets/User/<CompanyName> for user Widgets. The Widget file names are <WidgetName>.js for Core Widgets and <CompanyName>.Widgets.<WidgetName>.js for user Widgets.

Widget Anatomy

Let’s walk through the three basic sections of code that constitute a Widget:

Namespace, Inheritance and Constructor

Using the ASP.NET AJAX library, we register the namespace for our widget and define its inheritance from the BaseWidget class. Finally, we define the constructor.

Type.registerNamespace(&quot;YourCompany.Widgets&quot;);
YourCompany.Widgets.SampleWidget.inheritsFrom(DotNetNuke.UI.WebControls.Widgets.BaseWidget);
YourCompany.Widgets.SampleWidget = function(widget)
{
    YourCompany.Widgets.SampleWidget.initializeBase(this, [widget]);
}

Render Method

Every Widget must implement the render() method. Typically, this method will follow a pattern consisting of two steps: (1) enumerate the parameters specified in the Widget declaration (i.e. <param> elements) and assign them to local variables, (2) do some processing based on the parameters and call the render() method. When you call the render() method in Step 2, you pass in a DOM element that you create. The framework will replace the <object> element with which the Widget was defined with your DOM element and assign it the original ID that the <object> element was given. What your code does during Step #2 in order to populate the contents of the DOM element you create or manipulate other elements of the page is entirely up to you. Your Widget has access to any DOM element and can make calls to any other custom scripts, ASP.NET AJAX library functions, jQuery plugins etc.

YourCompany.Widgets.SampleWidget.prototype =
{
   render:
   function()
   {
      var params = this._widget.childNodes;
      if (params != null)
      {
          // Do something
      }

      var div = document.createElement(&quot;div&quot;);
      // Do some work here to add content to the div
      YourCompany.Widgets.SampleWidget.callBaseMethod(this, &quot;render&quot;, [div]);
   }
}

Registration and Rendering

The last thing you have to do in your Widget is to register its class and tell the Widget framework that it can render all instances of your Widget present on the page.

YourCompany.Widgets.SampleWidget.registerClass(&quot;YourCompany.Widgets.SampleWidget&quot;, DotNetNuke.UI.WebControls.Widgets.BaseWidget);
DotNetNuke.UI.WebControls.Widgets.renderWidgetType(&quot;YourCompany.Widgets.SampleWidget&quot;);

The above is all you need in order to create the basic scaffolding for a Widget. Add your custom code, save the file following the expected name and location conventions and you can start using your Widget right away (you’ll have to do some more work to package the Widget if you want it to be installable using the DotNetNuke Extension Wizard).

If you would like to see the code for a functional user Widget, you can download and install the Module Print Widget. The module displays a dropdown that allows a page viewer to select from a list of modules on the page and print any single module thus eliminating the need to display a print icon on every module.

Stay tuned for the last part in this series where I take you step-by-step through the process of building a Product Catalog Widget that makes use of third-party jQuery plugins, injects stylesheets dynamically and renders a nice UI using the jQuery UI extensions.

If you are like me and want your websites to have a clean, unblemished look, then most likely you have either turned off the Print functionality in your DotNetNuke module settings or just use containers that don’t display the icon. While this does make the site look cleaner, it also takes away the functionality. I gave this some thought and concluded that a Widget would be a great way to provide the functionality for printing the content of modules on a page. I envisioned a selector that the user would click on that would allow them to print the content for a single module. A skin designer could embed the Widget directly into the skin, or a site administrator could selectively add it to pages using an HTML module.

Module Print Widget

A couple hours of hacking later I present to you the ModulePrintWidget for DotNetNuke. It’s simple and easy to use. When added to a page, it creates a drop-down list of each module on the page. A user can select a module from the list to see a preview, then click a Print icon to print the contents of that module. Here’s a short 45-second YouTube video that demonstrates the Widget in action: TechBubble ModulePrintWidget

You can download the Widget package at the link below (install as Superuser from the Extensions page):

You can also see a live demo of the ModulePrintWidget.

To use the Widget, add the following HTML so that it appears once on a page. You can add the markup anywhere that HTML is supported (module, skin, skin object, container):

&lt;object id=&quot;MyWidget&quot; codetype=&quot;dotnetnuke/client&quot; codebase=&quot;TechBubble.Widgets.ModulePrintWidget&quot;&gt;&lt;/object&gt;

If you want to customize the appearance and language, use the optional parameters:

&lt;object id=&quot;MyWidget&quot; codetype=&quot;dotnetnuke/client&quot; codebase=&quot;TechBubble.Widgets.ModulePrintWidget&quot;&gt;
 &lt;param name=&quot;selectorCssClass&quot; value=&quot;Class-to-style-dropdown&quot; /&gt;
 &lt;param name=&quot;selectText&quot; value=&quot;Default-text-for-dropdown&quot; /&gt;
&lt;/object&gt;
© 2012 TechBubble Suffusion theme by Sayontan Sinha