New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

Cambrian First Look - Framework Changes (Pt 1)

In previous "Cambrian First Look" blogs I have described the new Features Module.  This is the most obvious new component in the initial Cambrian release, but it is not the only change.  There have been quite a lot of changes/additions to the Framework in preparation for other promised Cambrian features.  In this Blog I will start previewing these Framework changes.

The first major Framework change is mainly a refactoring of existing code.  Pre-Cambrian the Container and Module injection was handled in two methods (Page_Init and InjectModule) of the Skin class (Skin.vb).  When I started to work on the changes to Admin modules which I have described in an earlier blog I found it quite hard to decipher what was happening where, as the code for the InjectModule method was ~500 lines and the code for Page_Init was ~300 lines.  It was also obvious that there was no clear "Separation of Concerns" - the Skin class was handling everything.  In order to break the code into more managable chunks I refactored the code in these two methods introducing a new Class (Pane), as shown below.

Pre-Cambrian Skin and Container both existed (they represent the "base class" for all Skins and Containers), but there was no relationship between them.  In Cambrian Skin now has a collection - Panes - of Pane objects, and Pane has a collection - Containers - of Container objects.  This represents the actual physical heirarchy.  A Skin designer places "Panes" in the skin, which can contain multiple modules - and each module has a Container wrapper.  Each Pane object has a "PaneControl" property which represents the HtmlContainerControl (div, span, td and p tags) which is the actual physical Pane.  Likewise each Container also has an HtmlContainerControl (ContentPane) which is used to contain the injected module.  In the diagram, while the ContentPane and PaneControl both point at the same HtmlContainerControl, they do not actually represent the "same" control.

Why does this help? 

This architecture helps because we can now off-load some of the processing to the appropriate class.  Thus, Skin takes care of loading the Panes, it then parses the ActiveTab and passes the modules to the appropriate Pane.   The Pane class manages loading the Container for each module and passes the module to the Container class, and finally the Container class "injects" the Module.

Lets look at Skin.vb's OnInit method

  491         Protected Overrides Sub OnInit(ByVal e As System.EventArgs)

  492             'Call base classes method

  493             MyBase.OnInit(e)


  495             Dim bSuccess As Boolean = True


  497             'Load the Panes

  498             LoadPanes()


  500             'Load the Module Control(s)

  501             If Not IsAdminControl() Then

  502                 ' master module

  503                 bSuccess = ProcessMasterModules()

  504             Else

  505                 ' slave module

  506                 bSuccess = ProcessSlaveModule()

  507             End If


  509             'Load the Control Panel

  510             InjectControlPanel()


  512             'Process the Panes attributes

  513             ProcessPanes()


  515             'Register any error messages on the Skin

  516             If Not Request.QueryString("error") Is Nothing Then

  517                 Skin.AddPageMessage(Me, CRITICAL_ERROR, Server.HtmlEncode(Request.QueryString("error")), _

  518                                     UI.Skins.Controls.ModuleMessage.ModuleMessageType.RedError)

  519             End If


  521             If Not (PortalSecurity.IsPageAdmin()) Then

  522                 ' only display the warning to non-administrators (adminsitrators will see the errors)

  523                 If Not bSuccess Then

  524                     Skin.AddPageMessage(Me, MODULELOAD_WARNING, String.Format(MODULELOAD_WARNINGTEXT, PortalSettings.Email), _

  525                                         UI.Skins.Controls.ModuleMessage.ModuleMessageType.YellowWarning)

  526                 End If

  527             End If


  529         End Sub

 This method is much more manageable than the 300 lines in Page_Init.  Partly this is due to just breaking the method up into smaller sections, but a large chunk of code has been offloaded to Pane and Container.  The method is quite straightforward.  First the Panes are loaded, next the Modules are loaded (either Master  Modules or Slave Modules) then the ControlPanel is loaded and finally the Panes are processed (this sets things like adding the Pane borders in layout mode).

if we look at the LoadPanes method.

  260         Private Sub LoadPanes()

  261             Dim ctlControl As Control

  262             Dim objPaneControl As HtmlContainerControl


  264             ' iterate page controls

  265             For Each ctlControl In Me.Controls

  266                 objPaneControl = TryCast(ctlControl, HtmlContainerControl)


  268                 'Panes must be runat=server controls so they have to have an ID

  269                 If objPaneControl IsNot Nothing AndAlso Not String.IsNullOrEmpty(objPaneControl.ID) Then

  270                     ' load the skin panes

  271                     Select Case objPaneControl.TagName.ToUpper

  272                         Case "TD", "DIV", "SPAN", "P"

  273                             ' content pane

  274                             If objPaneControl.ID.ToLower() <> "controlpanel" Then

  275                                 'Add to the PortalSettings (for use in the Control Panel)

  276                                 PortalSettings.ActiveTab.Panes.Add(objPaneControl.ID)


  278                                 'Add to the Panes collection

  279                                 Panes.Add(objPaneControl.ID, New Pane(objPaneControl))

  280                             Else

  281                                 'Control Panel pane

  282                                 _ControlPanel = objPaneControl

  283                             End If

  284                     End Select

  285                 End If

  286             Next

  287         End Sub

this code is very similar to the code that used to exist at the beginning of the Page_Init event handler.

  837         Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init

  838             Dim objModules As New ModuleController

  839             Dim objModule As ModuleInfo = Nothing

  840             Dim ctlPane As Control

  841             Dim blnLayoutMode As Boolean = Common.Globals.IsLayoutMode


  843             Dim bSuccess As Boolean = True


  845             ' iterate page controls

  846             Dim ctlControl As Control

  847             Dim objHtmlControl As HtmlControl

  848             For Each ctlControl In Me.Controls

  849                 ' load the skin panes

  850                 If TypeOf ctlControl Is HtmlControl Then

  851                     objHtmlControl = CType(ctlControl, HtmlControl)

  852                     If Not objHtmlControl.ID Is Nothing Then

  853                         Select Case objHtmlControl.TagName.ToUpper

  854                             Case "TD", "DIV", "SPAN", "P"

  855                                 ' content pane

  856                                 If ctlControl.ID.ToLower() <> "controlpanel" Then

  857                                     PortalSettings.ActiveTab.Panes.Add(ctlControl.ID)

  858                                 End If

  859                         End Select

  860                     End If

  861                 End If

  862             Next

the main difference being line 279 which creates the Pane object, passing a reference to the actual Pane control, and adding the new Pane object to the Panes collection.

In addition line 282 sets the Control Panel (this code used to be at the very end of Page_Init method but makes more sense to set the ControlPanel at the same time as the other Panes are set (the Control Panel is in effect a special kind of Pane).

ProcessMasterModules and ProcessSlaveModules are essentially the same code as the rest of the old Page_Init method, just refactored into two separate methods (Separation of Concerns).  In each method ultimately there is code that Injects the module into the Pane.  For example, the last few lines of ProcessSlaveModules are as follows.

  468             ' verify that the current user has access to this control

  469             If PortalSecurity.HasNecessaryPermission(slaveModule.ModuleControl.ControlType, _

  470                                                     PortalSettings, slaveModule) Then

  471                 'try to inject the module into the pane

  472                 bSuccess = InjectModule(pane, slaveModule)

  473             Else

  474                 Response.Redirect(AccessDeniedURL(MODULEACCESS_ERROR), True)

  475             End If

InjectModule is where most of the refactoring has occurred.  This method used to be ~500 lines of code.  In Cambrian, it is quite short as all it does is hand-off the processing to the appropriate Pane.

  233         Private Function InjectModule(ByVal objPane As Pane, ByVal objModule As ModuleInfo) As Boolean

  234             Dim bSuccess As Boolean = True


  236             ' check if user has EDIT permissions for module

  237             Dim blnHasModuleEditPermissions As Boolean = PortalSecurity.HasNecessaryPermission(SecurityAccessLevel.Edit, _

  238                                                                                                PortalSettings, objModule, _

  239                                                                                                UserController.GetCurrentUserInfo.Username)

  240             If blnHasModuleEditPermissions = True AndAlso objModule.ModuleDefinition.DefaultCacheTime <> -1 Then

  241                 HasModuleEditPermission = True

  242             End If


  244             'try to inject the module into the pane

  245             Try

  246                 objPane.InjectModule(objModule)

  247             Catch ex As Exception

  248                 bSuccess = False

  249             End Try


  251             Return bSuccess

  252         End Function

This method checks if the user has the appropriate permissions and then calls the Pane objects InjectModule method (handing it the Module).

Pane.vb's InjectModule method (see below) loads the relevant Container and passes it the Module (by setting its ModuleConfiguration property)

  355         Public Sub InjectModule(ByVal objModule As ModuleInfo)

  356             Dim bSuccess As Boolean = True


  358             Try

  359                 If Not IsAdminControl() Then

  360                     ' inject an anchor tag to allow navigation to the module content

  361                     PaneControl.Controls.Add(New LiteralControl(" & objModule.ModuleID.ToString & """>"))

  362                 End If


  364                 'Load container control

  365                 Dim ctlContainer As DotNetNuke.UI.Containers.Container = LoadModuleContainer(objModule)


  367                 'Add Container to Dictionary

  368                 Containers.Add(ctlContainer.ID, ctlContainer)


  370                 'Attach Module to Container

  371                 ctlContainer.ModuleConfiguration = objModule


Setting the Containers ModuleConfiguration triggers the Container to complete the module injection.

  106         Public Property ModuleConfiguration() As ModuleInfo

  107             Get

  108                 Return _ModuleConfiguration

  109             End Get

  110             Set(ByVal value As ModuleInfo)

  111                 _ModuleConfiguration = value

  112                 ProcessModule()

  113             End Set

  114         End Property

In part 2 of this series of Blogs I will show how the Container completes the injection and introduce you to some new Interfaces and a new class.


Comment Form

Only registered users may post comments.


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Davies (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out