Early this week I was at the great OpenForce Europe conference organized by the SDN at The Netherlands.
As always it's been a great conference with lots of great sessions and plenty of opportunities to meet some old DNN friend and made some new ones! Networking (and drinking beers) is as important as good sessions, and we have had both of them!
At my session "DotNetNuke installation manifest file (.dnn) explained!" we took a deep look at all the options provided by DotNetNuke for a installing all kind of Extensions (libraries, modules, providers, skins, ...).
Extending the DotNetNuke Installer
During the session I explained the basic structure of the DNN manifest file (.dnn) and all types of currently supported packages, including the most used (modules, skins, providers) and some others less known (DashboardControl, Widget,...).
Each of those package types support several kind of Component Installers. In DNN 5.x the available component types are:
File, ResourceFile, Assembly, Script, CleanUp, Config, Module, SkinObject, AuthenticationSystem, DashboardControl, CoreLanguage, ExtensionLanguage, Skin, Container and Widget.
Most of these don't require any further discussion since the names are more or less self explanatory.
For all the details of each and any of the components take a look a the Wiki page for the package installer.
One of the hidden gems in the DotNetNuke installer is that it is also extensible. Not only you can install new DNN extensions but extend the installer itself! Isn't that great?
But why would you need such a feature if DNN already provides a long list of extensions? I will explain a quite common use case that can be easily be solved by taking advantage of this feature.
I've developed a lot of Modules that had to access external databases other than the database where DNN is installed. In many of those scenarios DNN is mainly used as a frontend to access some data repository that is kept apart from the main DNN database for better flexibility (that way the data can always be moved to a different server is performance requires so) and separation of concerns.
It is then very usual that a new version of the module that consumes that data needs to make some changes to the database (new tables, views, storeds, ...). The problem is that by using the default installer it's not possible to install scripts in separate database, instead all scripts will be executed in the main DNN database. So we don't have a way to install a module and make the changes in a single atomic installation.
Enter the ExternalScriptInstaller component!
What we need is a custom script installer that can execute scripts into an external database. The component is defined as:
What this will do is execute the script in the SqlServer database pointed by the ConnectionStringName. Obviously this ConnectionString needs to be registered on the web.config.
1: <component type="ExternalScript">
5: <script type="Install">
To support this what we only need is to implement a class that inherits from ComponentInstallerBase. In this case I inherited the class from the ScriptInstaller which in turn inherits from the ComponentInstallerBase.
1: Public Class ExternalScriptInstaller
2: Inherits ScriptInstaller
At this point all we need to do is override two methods: ReadManifest() and Install().
1: Public Overrides Sub ReadManifest(ByVal manifestNav As XPathNavigator)
3: Dim rootNav As XPathNavigator = manifestNav.SelectSingleNode(CollectionNodeName)
4: If rootNav IsNot Nothing Then
5: 'Get the ConnectionStringName
6: Dim baseNav As XPathNavigator = rootNav.SelectSingleNode("connectionStringName")
7: If baseNav IsNot Nothing Then
8: ConnectionStringName = baseNav.Value
9: End If
10: End If
14: End Sub
provides and XPathNavigator on the "
Finally the Install() method is called when the installer needs to process the component so at this point we only need to read the connectionstring from the web.config and use that information to execute the SQL scripts provided.
The only missing piece is registering this component installer in DNN so the framework knows about it. Custom installers must be defined in the Lists table using the following values:
- ListName = "Installer" (fixed)
- Value = "ExternalScript" (this is the type of the component as explained above)
- Text = DotNetNuke.Services.Installer.Installers.ExternalScriptInstaller (the fully qualified name of the class that implements the installer. In this case it only includes the class name, not the assembly, because I created the class on the App_Code folder).
To install the installer (!) you can create a library installer that copies the class to the App_Code folder and registers it on the Lists table with a sql script.
DotNetNuke provides a very rich set of features for installing new extensions. In most cases these will allow you to install all you need to. But in case you need extra power, the Custom Component Installers open a new path of flexibility for you.
If you need more information take a look at the sections in the Wiki were the manifest file is documented.