Building Websites with DotNetNuke 5 — Save 50%
Quickly build and deploy your own feature-rich website with DotNetNuke 5, VB.NET, and C#
In this article series by Ian Lackey and Michael Washington, we will be exploring the core functionality of the DotNetNuke architecture. We will be using the Source Code version of DotNetNuke 5.2.2 that can be downloaded from the DotNetNuke CodePlex site. We will start with an overview of the architecture, touching on key concepts employed by DotNetNuke. After this, we will examine some of the major sections that make up the framework. Finally, after we learn about the objects that make up the core, we will follow a request for a page through this process to find out how each page is dynamically created.
The global files
The Global.asax.vb and Globals.vb files share similar names but the parts they play in DotNetNuke are vastly different. The Global.asax.vb is used by DotNetNuke to handle application-level events raised by the ASP.NET runtime. The Globals.vb file, on the other hand, is a public module (which is the same as a static class in C#) that contains global utility functions. Before we take a look at these fi les, we first want to look at what object is being passed around in these transactions.
Much of the logic that used to reside in the Global.asax.vb fi le has now been abstracted to the HTTP modules. We will look into the code that remains.
When the fi rst request is made to your application (when the fi rst user accesses the portal), a pool of HttpApplication instances are created and the Application_Start event is fi red. This will (theoretically) fire just once and on the first HttpApplication object in the pool. When there is inactivity on your portal for a certain amount of time, the application (or application pool) will be recycled. When the pool is recycled, your application will restart (and this event will fi re again) when the next request is made for your application.
As the new version of DotNetNuke uses the .NET website structure, you will find the Global.asax.vb fi le in the App_Code folder.
In the Application_Start, we are loading all of the confi gured providers to ensure they are available to the rest of the framework when needed. These are performed in the Application_Start because we want them to be called only once.
Private Sub Application_Start(ByVal Sender As Object, ByVal E As
If Config.GetSetting("ServerName") = "" Then
ServerName = Server.MachineName
ServerName = Config.GetSetting("ServerName")
ComponentFactory.Container = New SimpleContainer()
'Install most Providers as Singleton LifeStyle
(New ProviderInstaller("data", GetType(DotNetNuke.Data.DataProvider)))
(New ProviderInstaller("caching", GeType(Services.Cache.
(New ProviderInstaller("logging", GetType(Services.Log.EventLog.
(New ProviderInstaller("scheduling", GetType(Services.Scheduling.
(New ProviderInstaller("searchIndex", GetType(Services.Search.
(New ProviderInstaller("searchDataStore", GetType(Services.Search.
(New ProviderInstaller("friendlyUrl", GetType(Services.Url.
(New ProviderInstaller("members", GetType(DotNetNuke.Security.
(New ProviderInstaller("roles", GetType(DotNetNuke.Security.Roles.
(New ProviderInstaller("profiles", GetType(DotNetNuke.Security.
(New ProviderInstaller("permissions", GetType(DotNetNuke.Security.
(New ProviderInstaller("outputCaching", GetType(DotNetNuke.Services.
(New ProviderInstaller("moduleCaching", GetType(DotNetNuke.Services.
Dim provider As DotNetNuke.Security.Permissions.
PermissionProvider = _
If provider Is Nothing Then
(Of DotNetNuke.Security.Permissions.PermissionProvider) _
'Install Navigation and Html Providers as NewInstance
Lifestyle (ie a new instance is generated each time the type is
requested, as there are often multiple instances on the page)
(New ProviderInstaller("htmlEditor", _
(New ProviderInstaller("navigationControl", _
In previous versions of DotNetNuke, there was a great deal happening in this method. However, this code has been moved into various methods inside of the Initialize class. This was done to support the integrated pipeline mode of IIS 7.
If you would like to take a look at what is happening inside of the Initialize class, it can be found in the \Common folder of the DotNetNuke.Library project.
The Application_BeginRequest is called for each request made to your application. In other words, this will fi re every time a page (tab), or other web request handlers such as a web service or an ashx handler, is accessed in your portal. This section is used to implement the scheduler built into DotNetNuke. Starting in version 2.0, two items, "users online" and "site log", require recurring operations. Also in this method is the call to the Initialize.Init() method that was moved out of the Appli cation_Startup method as mentioned previously.
You can fi nd out more about the scheduler by looking at the DotNetNuke Scheduler.pdf document (only if you download the documentation pack). Also note that, there is a host setting that defi nes the running mode of a scheduler, you can check for a scheduler run on every request to your portal, or run the scheduler in a timer mode.
Private Sub Global_BeginRequest(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.BeginRequest
Dim app As HttpApplication = CType(sender, HttpApplication)
Dim Request As HttpRequest = app.Request
If Request.Url.LocalPath.ToLower.EndsWith("scriptresource.axd") _
OrElse Request.Url.LocalPath.ToLower.EndsWith("webresource.axd") _
OrElse Request.Url.LocalPath.ToLower.EndsWith("gif") _
OrElse Request.Url.LocalPath.ToLower.EndsWith("jpg") _
OrElse Request.Url.LocalPath.ToLower.EndsWith("css") _
OrElse Request.Url.LocalPath.ToLower.EndsWith("js") Then
' all of the logic which was previously in Application_Start
' was moved to Init() in order to support IIS7 integrated pipeline
' ( which no longer provides access to HTTP context within
'run schedule if in Request mode
The Globals.vb file
As part of the namespace-reorganization effort associated with DotNetNuke version 3.0, general utility functions, constants, and enumerations have all been placed in a public module (as just mentioned, module here refers to VB.NET module keyword, not a DotNetNuke module) named Globals . As items in a .NET module are inherently shared, you do not need to instantiate an object in order to use the functions found here. In this module, you will find not only global constants, as shown in the following code:
Public Const glbRoleAllUsers As String = "-1"
Public Const glbRoleSuperUser As String = "-2"
Public Const glbRoleUnauthUser As String = "-3"
Public Const glbRoleNothing As String = "-4"
Public Const glbRoleAllUsersName As String = "All Users"
Public Const glbRoleSuperUserName As String = "Superuser"
Public Const glbRoleUnauthUserName As String =
Public Const glbDefaultPage As String = "Default.aspx"
Public Const glbHostSkinFolder As String = "_default"
Public Const glbDefaultControlPanel As String = "Admin/
Public Const glbDefaultPane As String = "ContentPane"
Public Const glbImageFileTypes As String = "jpg,jpeg,jpe,gif,
Public Const glbConfigFolder As String = "\Config\"
Public Const glbAboutPage As String = "about.htm"
Public Const glbDotNetNukeConfig As String = "DotNetNuke.
Public Const glbSuperUserAppName As Integer = -1
Public Const glbProtectedExtension As String = ".resources"
Public Const glbEmailRegEx As String = "\b[a-zA-Z0-9._%\-
Public Const glbScriptFormat As String = "<script type=""text/
but a tremendous number of public functions to help you do everything, from retrieving the domain name, as shown:
Public Function GetDomainName(ByVal Request As HttpRequest, ByVal
ParsePortNumber As Boolean) As String
to setting the focus on a page:
Public Sub SetFormFocus(ByVal control As Control)
This one file contains a wealth of information for the developer. As there are more than 3070 lines in the fi le and the methods are fairly straightforward, we will not be stepping through this code.
The Globals.vb fi le can now be found in the DotNetNuke.Library project in the \Common folder.
Putting it all together
We have spent some time looking at some of the major pieces that make up the core architecture. You might be asking yourself how all this works together. In this section, we will walk you through an overview version of what happens when a user requests a page on your portal.
A shown in the preceding diagram, when a user requests any page on your portal, the HTTP Modules that have been declared in the web.config file are hooked into the pipeline. Typically, these modules use the Init method to attach event handlers to application events.
The request then goes through the Global.asax page. As just mentioned, some of the events fi red here will be intercepted and processed by the HTTP modules, but the call to run the scheduler will happen in this fi le.
Next, the page that was requested, Default.aspx, will be processed. As we stated at the beginning of this article, all requests are sent to the Default.aspx page and all the controls and skins needed for the page are created dynamically by reading the tabID from the requested URL. So let's begin by looking at the HTML for this page.
eBook Price: $26.99
Book Price: $44.99
The HTML of the page is pretty simple and straightforward. The attributes at the top of the page tell us that the HTML page inherits from the DotNetNuke.Framework. CDefault class, which is found in the Default.aspx.vb code-behind page. We will be examining this class soon.
<%@ Page Language="vb" AutoEventWireup="false" Explicit="True"
The title and meta tags are populated dynamically from variables in the PortalSettings class via a method in the code-behind fi le:
<head id="Head" runat="server">
<meta content="text/html; charset=UTF-8" http-equiv="Content-
<meta content="text/css" http-equiv="Content-Style-Type"/>
<meta id="MetaRefresh" runat="Server" http-equiv="Refresh"
<meta id="MetaDescription" runat="Server" name="DESCRIPTION" />
<meta id="MetaKeywords" runat="Server" name="KEYWORDS" />
<meta id="MetaCopyright" runat="Server" name="COPYRIGHT" />
<meta id="MetaGenerator" runat="Server" name="GENERATOR" />
<meta id="MetaAuthor" runat="Server" name="AUTHOR" />
<meta name="RESOURCE-TYPE" content="DOCUMENT" />
<meta name="DISTRIBUTION" content="GLOBAL" />
<meta id="MetaRobots" runat="server" name="ROBOTS" />
<meta name="REVISIT-AFTER" content="1 DAYS" />
<meta name="RATING" content="GENERAL" />
<meta http-equiv="PAGE-ENTER" content="RevealTrans(Duration=0,Tra
After the meta tags, placeholders are set to hold CSS and favicons. These are declared in this manner so that the actual fi les can be determined by the skin being used on the site. This is followed by a script declaration for the fi le; this declaration is responsible for the module drag-and-drop capability of DotNetNuke 3.0 and later.
<style type="text/css" id="StylePlaceholder" runat="server"></style>
<asp:placeholder id="CSS" runat="server" />
The body of the HTML is relatively bare. The important code in this section is the SkinPlaceholder, used to inject the selected skin into the body of the page.
<body id="Body" runat="server" >
<dnn:Form id="Form" runat="server" ENCTYPE="multipart/form-data" >
<asp:Label ID="SkinError" runat="server" CssClass="NormalRed"
<asp:PlaceHolder ID="SkinPlaceHolder" runat="server" />
<input id="ScrollTop" runat="server" name="ScrollTop"
<input id="__dnnVariable" runat="server" name="__dnnVariable"
Now we will venture into the code-behind class for this fi le. If you look past the Imports statements, you will see that this class inherits from the DotNeNuke. Framework.PageBase base class.
Partial Class DefaultPage
Its base class handles the localization for the page and of course, as this is a web page, inherits from System.Web.UI.Page.
The first procedure that is run in the page is the Page_Init method. Most of the action required to generate the response to our request resides in this section. As shown in the next diagram, the first action the Page_init method takes is to call the InitializePage, which generates the information to fill the meta tags. After initializing the page, the Page_Init method begins configuring the skin by setting the DOCTYPE for the page and verifies that the requested page is enabled.
If you look at a couple of pieces of code, we can see some of the fi les we looked at earlier in use. In the InitializePage method, we make use of both the PortalSettings class and the Current property of the HttpContext object to retrieve the TabName:
objTab = objTabs.GetTabByName(Request.QueryString("TabName"),
Once the properties of the page are retrieved, the process of loading the skin begins. The process starts by retrieving a skin control from the GetSkin method of the Skin object that will do the work of loading the skin.
' load skin control
Dim ctlSkin As DotNetNuke.UI.Skins.Skin = _
After determining whether the request is a skin preview, the code moves on to load the skin. There are three possible outcomes when loading the skin: it is for an admin page, it is for a regular page, or there was an error and it loads the default skin. Regardless of which section is invoked, the skin is loaded using the LoadSkin method.
ctlSkin = LoadSkin(Page, skinSource)
This method reads the physical path of the skin control and loads it into our ctlSkin variable. And fi nally, there are calls to ManageStyleSheets and ManageFavicon, and the control is added to the page by using the SkinPlaceholder that we looked at earlier in the HTML page:
' add CSS links
' add skin to page
' add CSS links
' add Favicon
' ClientCallback Logic
At this point, you may be thinking to yourself, "I understand how the skin is dynamically added to the page for a user's request, but how are the modules dynamically added?" Well, to get the answer to that question, we will need to look at the skin control itself. You can fi nd the skin control (skin.vb) in the UI\Skins folder of the DotNetNuke.Library project. We will not look at this entire class, but if you look closely at the OnInit method, which is called when the control is instantiated, you will see how the modules are created. First, all of the panes for the skin are loaded into the Skin object's Panes property. Then a check is made to determine if the requested page is an admin/edit view. Based on this information, one of two methods are called to load the modules for the request.
'Load the Module Control(s)
If Not IsAdminControl() Then
' master module
bSuccess = ProcessMasterModules()
' slave module
bSuccess = ProcessSlaveModule()
These methods determine which modules should be loaded and verify that the requesting user has access to the module(s), determines which pane to place the module, and fi nally inject the module into the skin.
'try to inject the module into the pane
bSuccess = InjectModule(pane, objModule)
This procedure will be repeated for all of the modules associated with that page, and the request is fi nally completed and presented to the user. Of course, we did not cover every piece of code that is called in the process, but hopefully have given you a path to follow to continue researching the core architecture on your own.
In this article, we have taken a look at how the core of DotNetNuke works. We looked at a general overview, examined important pieces of the framework, and finally followed a request through its paces.
If you have read this article you may be interested to view :
- Understanding the DotNetNuke Core Architecture
- Users, Roles, and Pages in DotNetNuke 5
- Users, Roles, and Pages in DotNetNuke 5- An Extension
eBook Price: $26.99
Book Price: $44.99
About the Author :
Michael Washington is a Website developer and an ASP.NET, C#, and Visual Basic programmer. He is a DotNetNuke Core member and has been involved with DotNetNuke for over 3 years. He is the author of numerous DotNetNuke modules and tutorials. He is one of the founding members of the Southern California DotNetNuke Users group. He has a son, Zachary and resides in Los Angeles with his wife Valerie.