Learn More





DNN Community Blog

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 - Extension Installer (Pt 2)

In my previous blog I introduced the new Extension Installer from a developer's perspective.  In this Blog I will continue that discussion by showing how the various classes interact.

Reading the Manifest.

The Installer class has a number of Constructors, but the Install Wizard invokes the following Contructor.

   63         Public Sub New(ByVal tempFolder As String, ByVal manifest As String, ByVal physicalSitePath As String, ByVal loadManifest As Boolean)

   64             _InstallerInfo = New InstallerInfo(tempFolder, manifest, physicalSitePath)

   65             If loadManifest Then

   66                 ReadManifest()

   67             End If

   68         End Sub

This Constructor instantiates an InstallerInfo instance, and then calls the ReadManifest method.

  358         Public Sub ReadManifest()

  359             InstallerInfo.Log.StartJob(Util.DNN_Reading)


  361             If InstallerInfo.ManifestFile IsNot Nothing Then

  362                 ReadManifest(New FileStream(InstallerInfo.ManifestFile.TempFileName, FileMode.Open, FileAccess.Read))

  363             End If


  365             If InstallerInfo.Log.Valid Then

  366                 InstallerInfo.Log.EndJob(Util.DNN_Success)

  367             Else

  368                 'Delete Temp Folder

  369                 If Not String.IsNullOrEmpty(TempInstallFolder) Then

  370                     Directory.Delete(TempInstallFolder, True)

  371                 End If

  372             End If

  373         End Sub

This method in turn calls a private ReadManifest method that actually does the work, by creating a FileStream from the manifest file..

  254         Private Sub ReadManifest(ByVal stream As Stream)

  255             Dim doc As New XPathDocument(stream)


  257             'Read the root node to determine what version the manifest is

  258             Dim rootNav As XPathNavigator = doc.CreateNavigator()

  259             rootNav.MoveToFirstChild()

  260             Dim packageType As String = Null.NullString

  261             If rootNav.Name = "dotnetnuke" Then

  262                 packageType = Util.ReadAttribute(rootNav, "type")

  263             ElseIf rootNav.Name.ToLower = "languagepack" Then

  264                 packageType = "LanguagePack"

  265             Else

  266                 InstallerInfo.Log.AddFailure(Util.PACKAGE_UnRecognizable)

  267             End If


  269             Select Case packageType

  270                 Case "Package"

  271                     'Parse the package nodes

  272                     ProcessPackages(rootNav)

  273                 Case "Module"

  274                     'Legacy Module

  275                     Dim modulewriter As New ModulePackageWriter(rootNav, InstallerInfo)

  276                     Dim legacyManifest As String = modulewriter.WriteManifest()

  277                     Dim legacyDoc As New XPathDocument(New StringReader(legacyManifest))


  279                     'Parse the package nodes

  280                     ProcessPackages(legacyDoc.CreateNavigator().SelectSingleNode("dotnetnuke"))

  281                 Case "LanguagePack"

  282                     'Legacy Language Pack

  283                     Dim languageWriter As New LanguagePackWriter(rootNav, InstallerInfo)

  284                     Dim legacyManifest As String = languageWriter.WriteManifest()

  285                     Dim legacyDoc As New XPathDocument(New StringReader(legacyManifest))


  287                     'Parse the package nodes

  288                     ProcessPackages(legacyDoc.CreateNavigator().SelectSingleNode("dotnetnuke"))

  289             End Select

  290         End Sub

In this method an XPathDocument is created from the FileStream.  Note, that we are using an XPathDocument rather than an XmlDocument.  This is because in a read-only scenario these classes perform better. 

The first section of this method determines whether the manifest represents a Legacy LanguagePack, a Legacy Module or a new Package.  In the Select Case statement, if the manifest represents a Legacy Module or a Legacy LanguagePack, the manifests are passed to a "PackageWriter" which reads thje legacy manifest and returns a Package manifest.  In all three cases the resulting manifests are passed to the ProcessPackages method.

  246         Private Sub ProcessPackages(ByVal rootNav As XPathNavigator)

  247             'Parse the package nodes

  248             For Each nav As XPathNavigator In rootNav.Select("packages")

  249                 Dim name As String = XmlUtils.GetNodeValue(nav, "package/name")

  250                 Packages.Add(name, New PackageInstaller(nav.InnerXml, InstallerInfo))

  251             Next

  252         End Sub

In theory a v5.0 manifest can support multiple packages and so this method loops through the packages/package elements to create a PackageInstaller class for each package and add it to the Packages collection.  I said in theory we can have mutiple packages in a manifest (and as you can see this is supported in the Installer Framework), but in practice the Install Wizard currently only supports a single package in a manifest.  The manifest fragment for the package is passed to the constructor of the PackageInstaller.  Note also that the InstallerInfo instance is also passed to the PackageInstaller.

   97         Public Sub New(ByVal packageManifest As String, ByVal info As InstallerInfo)

   98             Package = New PackageInfo(info)

   99             Package.Manifest = packageManifest


  101             If Not String.IsNullOrEmpty(packageManifest) Then

  102                 'Create an XPathDocument from the Xml

  103                 Dim doc As New XPathDocument(New StringReader(packageManifest))

  104                 Dim nav As XPathNavigator = doc.CreateNavigator().SelectSingleNode("package")

  105                 ReadManifest(nav)

  106             End If

  107         End Sub

The constructor checks that the manifest is not empty and calls its ReadManifest method.

  326         Public Overrides Sub ReadManifest(ByVal manifestNav As XPathNavigator)


  328             .....


  417             'Read Components

  418             ReadComponents(manifestNav)

  419         End Sub

The ReadManifest method is quite long as it processes the manifest elements for the package, but the last statement of the method calls the ReadComponents method.

  186         Private Sub ReadComponents(ByVal manifestNav As XPathNavigator)

  187             'Parse the component nodes

  188             For Each componentNav As XPathNavigator In manifestNav.CreateNavigator().Select("components/component")

  189                 'Set default order to next value (ie the same as the size of the collection)

  190                 Dim order As Integer = ComponentInstallers.Count


  192                 Dim type As String = componentNav.GetAttribute("type", "")


  194                 If InstallMode = InstallMode.Install Then

  195                     Dim installOrder As String = componentNav.GetAttribute("installOrder", "")

  196                     If Not String.IsNullOrEmpty(installOrder) Then

  197                         order = Integer.Parse(installOrder)

  198                     End If

  199                 Else

  200                     Dim unInstallOrder As String = componentNav.GetAttribute("unInstallOrder", "")

  201                     If Not String.IsNullOrEmpty(unInstallOrder) Then

  202                         order = Integer.Parse(unInstallOrder)

  203                     End If

  204                 End If


  206                 If Package.InstallerInfo IsNot Nothing Then

  207                     Log.AddInfo(Util.DNN_ReadingComponent + " - " + type)

  208                 End If


  210                 Dim installer As ComponentInstallerBase = InstallerFactory.GetInstaller(componentNav, Package)

  211                 If installer Is Nothing Then

  212                     Log.AddFailure(Util.EXCEPTION_InstallerCreate)

  213                 End If


  215                 ComponentInstallers.Add(order, installer)

  216                 Me.Package.InstallerInfo.AllowableFiles += ", " + installer.AllowableFiles

  217             Next

  218         End Sub

This method demonstrates one of the most important features of the new Installer.  It reads the individual component sections of the manifest and uses a Factory Class to create the appropriate ComponentInstaller (line 210).  The factory class inspects the manifest passed to it and returns a ComponentInstaller which is added to the ComponentInstallers collection.  This collection is a SortedList.  In most situations the components are added to the SortedList in the order in which they are processed in the manifest, but the manifest supports the use of an "installOrder" and "uninstallOrder" attribute which can be used to control the order of installation of the components.

Lets look at the Factory Class.

  107         Public Shared Function GetInstaller(ByVal manifestNav As XPathNavigator, ByVal package As PackageInfo) As ComponentInstallerBase

  108             Dim installerType As String = Util.ReadAttribute(manifestNav, "type")

  109             Dim componentVersion As String = Util.ReadAttribute(manifestNav, "version")


  111             Dim installer As ComponentInstallerBase = GetInstaller(installerType)


  113             If installer IsNot Nothing Then

  114                 'Set package

  115                 installer.Package = package


  117                 'Set type

  118                 installer.Type = installerType


  120                 If Not String.IsNullOrEmpty(componentVersion) Then

  121                     installer.Version = New Version(componentVersion)

  122                 Else

  123                     installer.Version = package.Version

  124                 End If


  126                 'Read Manifest

  127                 If package.InstallerInfo.InstallMode <> InstallMode.ManifestOnly OrElse installer.SupportsManifestOnlyInstall Then

  128                     installer.ReadManifest(manifestNav)

  129                 End If

  130             End If


  132             Return installer


  134         End Function

The public GetInstaller method first calls a private method that determines which installer to instantiate.

   54        Public Shared Function GetInstaller(ByVal installerType As String) As ComponentInstallerBase

   55             Dim installer As ComponentInstallerBase = Nothing


   57             Select Case installerType

   58                 Case "File"

   59                     installer = New FileInstaller()

   60                 Case "Assembly"

   61                     installer = New AssemblyInstaller()

   62                 Case "ResourceFile"

   63                     installer = New ResourceFileInstaller()

   64                 Case "AuthenticationSystem", "Auth_System"

   65                     installer = New AuthenticationInstaller()

   66                 Case "Script"

   67                     installer = New ScriptInstaller()

   68                 Case "Config"

   69                     installer = New ConfigInstaller()

   70                 Case "Cleanup"

   71                     installer = New CleanupInstaller()

   72                 Case "Skin"

   73                     installer = New SkinInstaller()

   74                 Case "Container"

   75                     installer = New ContainerInstaller()

   76                 Case "Module"

   77                     installer = New ModuleInstaller()

   78                 Case "Language", "LanguagePack"

   79                     installer = New LanguageInstaller()

   80                 Case "SkinObject"

   81                     installer = New SkinControlInstaller()

   82                 Case Else

   83                     'Installer type is defined in the List

   84                     Dim listController As New Lists.ListController()

   85                     Dim entry As ListEntryInfo = listController.GetListEntryInfo("Installer", installerType)


   87                     If entry IsNot Nothing AndAlso Not String.IsNullOrEmpty(entry.Text) Then

   88                         'The class for the Installer is specified in the Text property

   89                         installer = CType(Reflection.CreateObject(entry.Text, "Installer_" + entry.Value), ComponentInstallerBase)

   90                     End If

   91             End Select


   93             Return installer


   95         End Function

If we look at this method we can see the list of Installers which are provided.  In the "Case Else" block however we can see that this ComponentInstaller system is extensible.  This allows, developers to add their own ComponentInstallers to a DotNetNuke installation by registering them in the Lists Module.  Thus a developer could create a module that uses templates for example.  The developer would include code in the module for a TemplateComponentInstaller, and would register the installer into the Lists module.  They could then provide their own "Install Template" functionality to add new templates.

Each component has its own ReadManifest which is called by public GetInstaller method.  For example the ConfigInstallers ReadManifest is shown below.

  172         Public Overrides Sub ReadManifest(ByVal manifestNav As XPathNavigator)

  173             Dim nav As XPathNavigator = manifestNav.SelectSingleNode("config")


  175             'Get the name of the target config file to update

  176             Dim nodeNav As XPathNavigator = nav.SelectSingleNode("configFile")

  177             Dim targetFileName As String = nodeNav.Value

  178             If Not String.IsNullOrEmpty(targetFileName) Then

  179                 _TargetFile = New InstallFile(targetFileName, "", Me.Package.InstallerInfo)

  180             End If


  182             'Get the Install config changes

  183             nodeNav = nav.SelectSingleNode("install")

  184             _InstallConfig = nodeNav.InnerXml


  186             'Get the UnInstall config changes

  187             nodeNav = nav.SelectSingleNode("uninstall")

  188             _UnInstallConfig = nodeNav.InnerXml

  189         End Sub

This blog has stepped though the code for reading the manifest in the new Extension Installer.  Once this process is complete, the Installer class contains a Dictionaryof PackageInstallers, and each PackageInstaller contains a SortedList of ComponentInstallers.  In my next blog in this series I will show how a similar approach is used to "Install" and "UnInstall" a Package.





There are currently no comments, be the first to post one.

Comment Form

Only registered users may post comments.


2sic Daniel Mettler (124)
Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (22)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (203)
Chris Paterra (55)
Clinton Patterson (28)
Cuong Dang (21)
Daniel Bartholomew (2)
Dave Buckner (2)
David Poindexter (3)
David Rodriguez (2)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (6)
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 (270)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (52)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (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)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (37)
Will Strohl (163)
William Severance (5)
Try Evoq
For Free
Start Free Trial
a Demo
See Evoq Live
Need More Information?