27 May 2009

DNN Skin token support in a module

This article shows how to get a DotNetNuke (DNN) module to support tokens in skins. It will use, as an example, how I added support for the [CODEMODULE] skin token in our phdcc.CodeModule retail module.

Introduction: skins are used by DNN as a template for the whole site output, and a container template is used to wrap an individual module. Skins and containers can contain standard tokens such as [LOGO] - this is replaced at runtime by the logo image chosen by the site administrator.

Doing it manually

The crucial trick is to add a suitable entry to the ModuleControls table.

This table primarily contains a mapping between the blank (View), Edit and Settings control keys for a module and the actual controls that implement them, eg the "Settings" ControlKey is set to "DesktopModules/phdcc.CodeModule/Settings.ascx" for my module.

To support your new skin token, add a new row with your skin token name in the ControlKey column (eg "CodeModule" with no quotes or brackets), a NULL for the ControlTitle, your view control in ControlSrc and -2 in ControlType. For my module, the ControlSrc is "DesktopModules/phdcc.CodeModule/View.ascx"

Adding a token to a skin

People work with skins in various ways. The standard method is to have an HTML template file that is parsed into an ASCX when the skin is uploaded. If you edit the HTML file on a live site then you can click on [Parse Skin Package] to re-make the ASCX.

Anyway, add your skin token to your HTML skin somewhere, eg add "[CODEMODULE]", without quotes but with brackets - and in upper case. Either package it up and upload, or edit the live version and click on [Parse Skin Package]. The parse log should show your token being replaced. The ASCX should have your token replaced, as per the example below.

If you work with an ASCX template file, then you need to add a suitable Register directive at the top of the ASCX, eg:
<%@ Register TagPrefix="dnn" TagName="CODEMODULE" Src="~/DesktopModules/phdcc.CodeModule/View.ascx" %>
then add instances of this control later on, eg:
<dnn:codemodule runat="server" id="dnnCODEMODULE" />

Hopefully your view control should now be called whenever this skin is used. If so, hurrah! Note that the PortalModuleBase TabModuleId is set to -1 so you could use this to tell when you are being called from a skin.

Token parameters

It is useful to be able to pass parameters to your module, eg so it knows what to display. The parameters are set in the skin.xml file alongside your HTML template. For my module, I wanted to add a ControlFile parameter to tell my module what to do. I added this to skin.xml, after <Objects>

<object>
  <token>[CODEMODULE]</token>
  <settings>
    <setting>
      <name>ControlFile</name>
      <value>viewSkin.ascx</value>
    </setting>
  </settings>
</object>

You can specify several settings for your token. In this case, I set the "ControlFile" setting to value "viewSkin.ascx". Make sure [CODEMODULE] is in upper case.

To support each parameter, you must add a corresponding property to your control. So, in this case, I must add a property called "ControlFile". In VB, this could be:

Private _ControlFile As String

Public Property ControlFile() As String
  Get
    Return _ControlFile
  End Get
  Set(ByVal value As String)
    _ControlFile = value
  End Set
End Property


If you upload or re-parse your skin, you should find that the parameter has now been set in the ASCX, eg:
<dnn:codemodule runat="server" id="dnnCODEMODULE" ControlFile="viewSkin.ascx" />

When your view control is now run, the ControlFile property should have been set before your Page_Load is called. You can use this to tell when you have been called from a skin - and act accordingly.

Automatic installation

Earlier on, we added the crucial row to the ModuleControls table by hand. To set this automatically during installation, you need to add an appropriate data provider. For example, this might be called 01.00.00.SqlDataProvider. Add in SQL code to delete any existing ControlKey and add the correct one, eg:
DELETE FROM {databaseOwner}{objectQualifier}ModuleControls
  WHERE ControlKey='CodeModule'
INSERT INTO {databaseOwner}{objectQualifier}ModuleControls
  (ControlKey,ControlTitle,ControlSrc,ControlType)
  VALUES ('CodeModule',NULL,'DesktopModules/phdcc.CodeModule/View.ascx',-2)


Finally, add in an entry to your Uninstall.SqlDataProvider file to remove this entry if your module is removed:
DELETE FROM {databaseOwner}{objectQualifier}ModuleControls
  WHERE ControlKey='CodeModule'