DotNetNuke Error after Uploading New Skin

After uploading a new skin I developed onto a DNN 6.1.4 installation, the Host/Extensions page did not display the list of installed modules, skins, etc. anymore, but only showed the error message

Object reference not set to an instance of an object.

The current log file under \Portals\_default\Logs contained the complete stacktrace:

[date:time] [server][Thread:14][FATAL] DotNetNuke.Framework.PageBase - An error has occurred while loading page.
System.NullReferenceException: Object reference not set to an instance of an object.
   at DotNetNuke.Services.Installer.Packages.PackageController.CanDeletePackage(PackageInfo package, PortalSettings portalSettings)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.extensionsGrid_ItemDataBound(Object sender, DataGridItemEventArgs e)
   at System.Web.UI.WebControls.DataGrid.CreateItem(Int32 itemIndex, Int32 dataSourceIndex, ListItemType itemType, Boolean dataBind, Object dataItem, DataGridColumn[] columns, TableRowCollection rows, PagedDataSource pagedDataSource)
   at System.Web.UI.WebControls.DataGrid.CreateControlHierarchy(Boolean useDataSource)
   at System.Web.UI.WebControls.BaseDataList.OnDataBinding(EventArgs e)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.BindGrid(String packageType, DataGrid grid, Label noResultsLabel)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.extensionTypeRepeater_ItemDataBound(Object sender, RepeaterItemEventArgs e)
   at System.Web.UI.WebControls.Repeater.CreateControlHierarchy(Boolean useDataSource)
   at System.Web.UI.WebControls.Repeater.OnDataBinding(EventArgs e)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.BindPackageTypes()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
[date:time] [server][Thread:14][ERROR] System.Web.UI.Page - ~/Default.aspx?tabid=36&error=Object+reference+not+set+to+an+instance+of+an+object.&content=0
System.NullReferenceException: Object reference not set to an instance of an object.
   at DotNetNuke.Services.Installer.Packages.PackageController.CanDeletePackage(PackageInfo package, PortalSettings portalSettings)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.extensionsGrid_ItemDataBound(Object sender, DataGridItemEventArgs e)
   at System.Web.UI.WebControls.DataGrid.CreateItem(Int32 itemIndex, Int32 dataSourceIndex, ListItemType itemType, Boolean dataBind, Object dataItem, DataGridColumn[] columns, TableRowCollection rows, PagedDataSource pagedDataSource)
   at System.Web.UI.WebControls.DataGrid.CreateControlHierarchy(Boolean useDataSource)
   at System.Web.UI.WebControls.BaseDataList.OnDataBinding(EventArgs e)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.BindGrid(String packageType, DataGrid grid, Label noResultsLabel)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.extensionTypeRepeater_ItemDataBound(Object sender, RepeaterItemEventArgs e)
   at System.Web.UI.WebControls.Repeater.CreateControlHierarchy(Boolean useDataSource)
   at System.Web.UI.WebControls.Repeater.OnDataBinding(EventArgs e)
   at DotNetNuke.Modules.Admin.Extensions.InstalledExtensions.BindPackageTypes()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

The DNN forums contain bug reports for this error message since at least 2008 (!), but most often the entries contain no solution on how to proceed. Or at least, on how to find the offending module, skin, control, whatever that causes the CanDeletePackage() function to fail.

You’d expect that during the last 4 years, somebody would have bothered to at least log the item that’s causing the error, or fix CanDeletePackage() to handle a NULL parameter. But no.

The only useful hint I found was on this self-answered question,

2 rows in the packages table were invalid- they were pointing at packages that no longer existed.  I think maybe i renamed or deleted these skins on the file system- guess i should not do that

So I had a look at the Packages table in the DNN database, and indeed it contained a record for a skin container that I did not declare. Probably that was caused by an empty

<components></components>

section in the .dnn file? I have no idea.

I deleted the record, and was able to load the Host/Extensions page again. I uploaded and updated the previously installed skin package, and things still worked. The erroneous record did not show up again.

Handling Exception in DotNetNuke Modules menu

For development, I use a couple of DNN 6 installations. Today I tried to import and use a newly created module, when the Modules menu displayed the following error text at the bottom:

An error has occurred.
DotNetNuke.Services.Exceptions.ModuleLoadException: An entry with the same key already exists. ---> System.ArgumentException: An entry with the same key already exists.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.SortedList`2.Add(TKey key, TValue value)
   at DotNetNuke.Entities.Modules.DesktopModuleController.GetPortalDesktopModules(Int32 portalID)
   at DotNetNuke.Web.UI.WebControls.DnnModuleComboBox.GetPortalDesktopModules()
   at DotNetNuke.Web.UI.WebControls.DnnModuleComboBox.BindAllPortalDesktopModules()
   at DotNetNuke.UI.ControlPanel.AddModule.LoadModuleList() in c:\projects\dnn060104\admin\ControlPanel\AddModule.ascx.cs:line 395
   at DotNetNuke.UI.ControlPanel.AddModule.LoadAllLists() in c:\projects\dnn060104\admin\ControlPanel\AddModule.ascx.cs:line 327
   at DotNetNuke.UI.ControlPanel.AddModule.OnLoad(EventArgs e) in c:\projects\dnn060104\admin\ControlPanel\AddModule.ascx.cs:line 133
   --- End of inner exception stack trace ---

Digging through the DNN6 source code, I found the DesktopModuleController class in DotNetNuke_Community_06.01.04_Source \Library \Entities \Modules \DesktopModuleController.cs, where the GetPortalDesktopModules(int) method converts the results of GetPortalDesktopModulesByPortalID(int) into a SortedList<string, PortalDesktopModuleInfo>, the Add() method of which raised an exception, because a FriendlyName collision.

To find the offending modules, use SSMS to navigate to your DNN database, right-click the DesktopModules table and select “Edit top (n) records”. Press the SQL toolbar button and modify the SELECT statement to ORDER BY FriendlyName.

SELECT     TOP (200) *
FROM         DesktopModules 
ORDER BY FriendlyName

Rename one of the FriendlyName values causing the problem.

Next, update the web.config to cause DNN to reload its caches.

Problem fixed 😉

Listing all DotNetNuke Pages with Settings and Permissions

To document the pages of a DNN installation, we can access the relevant tables in a DNN database:

Tabs Pages
Roles DNN Roles
Permission Permitted action
TabPermission Assignment of role privileges per page

The SQL statement consists of sub-selects to

  • concatenate the permissions for a role and a page
  • concatenate the role permissions in a page
SELECT TabID, TabName, Title, [PageHeadText], TabPath, 
  IsVisible AS InMenu,
  -- IsSecure, Url, (etc. as needed) 
  SkinSrc, ContainerSrc,
  SUBSTRING((  
    SELECT '; ' 
      + ISNULL(r.RoleName, CONVERT(NVARCHAR, tp.RoleID)) + ' [' 
      + SUBSTRING((
          SELECT ', ' + PermissionName 
          FROM TabPermission tpSub 
          INNER JOIN Permission p 
            ON p.PermissionID = tpSub.PermissionID
          WHERE tpSub.TabID = tp.TabID 
            AND tpSub.RoleID = tp.RoleID
          FOR XML PATH('')), 
        3, 1000)
      + ']'
    FROM TabPermission tp
    LEFT OUTER JOIN 
      (SELECT RoleID, RoleName FROM Roles
      UNION ALL SELECT -1, 'All Users'
      UNION ALL SELECT -2, 'Registered'
      UNION ALL SELECT -3, 'Unauthorized') r 
        ON tp.RoleID = r.RoleID

    WHERE tp.TabPermissionID IN
      (SELECT MIN(TabPermissionID) 
      FROM  TabPermission tpMin 
      WHERE tpMin.TabID = Tabs.TabID
      GROUP BY tpMin.RoleID
      )
    ORDER BY tp.RoleID
    FOR XML PATH('')), 3, 1000) AS Permissions
FROM Tabs
WHERE PortalID = 0
AND IsDeleted = 0
--AND Level = 0
ORDER BY TabPath

The negative values for RoleID are from an answer on SO.

The query result looks like this (new installation, Admin pages excluded):

TabName TabPath M. SkinSrc ContainerSrc Permissions
About Us //AboutUs 0 [G]Skins/ DarkKnight/ 2-Column-Right-Mega-Menu.ascx [G]Containers/ DarkKnight/ SubTitle_Grey.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]
Collaborate Details //CollaborateDetails 0 [L]Skins/ Collaborate/ Details.ascx [L]Containers/ Collaborate/ Simple.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]
Collaborate Home //CollaborateHome 0 [L]Skins/ Collaborate/ Home.ascx [L]Containers/ Collaborate/ Simple.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]
Getting Started //GettingStarted 1 [G]Skins/ DarkKnight/ Home-Mega-Menu.ascx [G]Containers/ DarkKnight/ SubTitle_Grey.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]
Home //Home 0 [G]Skins/ DarkKnight/ Home-Mega-Menu.ascx [G]Containers/ DarkKnight/ SubTitle_Grey.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]
News & Promotions //NewsPromotions 0 [G]Skins/ DarkKnight/ 2-Column-Right-Mega-Menu.ascx [G]Containers/ DarkKnight/ SubTitle_Grey.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]
Our Services //OurServices 0 [G]Skins/ DarkKnight/ 2-Column-Right-Mega-Menu.ascx [G]Containers/ DarkKnight/ SubTitle_Grey.ascx All Users [View Tab]; Administrators [View Tab, Edit Tab]

(Some column are omitted to fit page width)

Add or remove columns and conditions as required for your purpose.

Automatically updating Custom DotNetNuke Modules using Selenium IDE for Firefox

If you develop DNN modules and need to support several installations in sync, any automated help is welcome.

I tried to use Selenium to automate Firefox to upload module packages into a DNN installation. (I did not find any references as to whether DNN has a built-in update mechanism for custom modules). Download Selenium IDE and press Record.

The result is a Selenium Test Case that performs the following operations

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="selenium.base" href="http://localhost/" />
<title>dnn2ml update BGT.Flash</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">dnn2ml update BGT.Flash</td></tr>
</thead><tbody>
<tr>
	<td>open</td>
	<td>http://localhost/dnn/GettingStarted/tabid/83/ctl/Login/Default.aspx?returnurl=%2fdnn%2fMain.aspx</td>
	<td></td>
</tr>

Retrieve the login URL by right-clicking the Login button and copying the URL. The tabid usually changes between installations.

<tr>
	<td>type</td>
	<td>id=dnn_ctr_Login_Login_DNN_txtUsername</td>
	<td>host</td>
</tr>
<tr>
	<td>type</td>
	<td>id=dnn_ctr_Login_Login_DNN_txtPassword</td>
	<td>PASSWORD</td>
</tr>

Edit Host (Superuser) username and password

<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Login_Login_DNN_cmdLogin</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>//div[@id='dnn_cp_RibbonBar_adminMenus']/ul/li[2]/div/ul/li/ul/li[10]/a/span</td>
	<td></td>
</tr>

I  added these two steps to change to Edit mode (I have no idea how the View/Edit mode is set right after login). This will cause a timeout if DNN is already in Edit mode.

<tr>
	<td>select</td>
	<td>id=dnn_cp_RibbonBar_ddlMode</td>
	<td>label=Edit</td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>css=option[value="EDIT"]</td>
	<td></td>
</tr>

Invoke Install Extension Wizard

<tr>
	<td>click</td>
	<td>link=Install Extension Wizard</td>
	<td></td>
</tr>
<tr>
	<td>type</td>
	<td>id=dnn_ctr_Install_wizInstall_cmdBrowse</td>
	<td>C:\path\to\MyModule\packages\MyModule_00.00.01_Source.zip</td>
</tr>

Select package file to be uploaded

<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Install_wizInstall_StartNavigationTemplateContainerID_nextButtonStart</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Install_wizInstall_chkRepairInstall</td>
	<td></td>
</tr>

This is for updating modules, so we need to check the Repair flag

<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Install_wizInstall_StepNavigationTemplateContainerID_nextButtonStep</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Install_wizInstall_StepNavigationTemplateContainerID_nextButtonStep</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Install_wizInstall_StepNavigationTemplateContainerID_nextButtonStep</td>
	<td></td>
</tr>
<tr>
	<td>click</td>
	<td>id=dnn_ctr_Install_wizInstall_chkAcceptLicense</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=dnn_ctr_Install_wizInstall_StepNavigationTemplateContainerID_nextButtonStep</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>//body[@id='Body']/div[4]/div/a[2]</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=dnn_LOGIN1_loginLink</td>
	<td></td>
</tr>
<tr>
	<td></td>
	<td></td>
	<td></td>
</tr>
</tbody></table>
</body>
</html>

Logout after upload.

Note that this simple script expects the browser to be logged out in DNN.

The sample may be made more stable by using conditions such as provided by Selenium IDE Flow Control.

DNN 6.0 “The Child Site Name You Specified Already Exists”

Just tried to test the DotNetNuke 6.0.4 “export/import templates” functionality in preparation of the deployment process.

To export your site, go to Host/Site Management, hover over Manage, and select the Export Site Template menu. Select the site, enter template file name and description, check “Include Content”, and hit Export Template. (source)

This will create the files mytemplatefilename.export.data.template (XML file) and mytemplatefilename.export.data.template.resources (zip file) in the DNN directory Portals/_default.

To import, click the Add New Site menu on the Manage button. Select the saved template from the dropdown box. (source)

Attention: If you happen to enter a trailing slash in the Site Alias field, DNN displays the following error message:

The Child Site Name You Specified Already Exists. Please Choose A Different Child Site Name.

Which is funny, because you cannot enter a site name. Nowhere. Ever. It’s always Site Alias, Title, but never Name.

Fortunately, I found this bug report stating exactly the problem I had. I had already checked file system permissions etc., but did not find anything obvious. Removing the trailing slash allowed me to create the cloned site.

Content Localization in Multi-Language DotNetNuke 6

When we talk about multi-language support by applications (also known as NLS, globalization, localization, internationalization), we need to distinguish between localization of the application and localization of the content (at least).

Enabling

DotNetNuke language packs take care of localization of the application. Once installed and enabled, the application (i.e. the menus, dialogs, prompts, labels) are displayed in the selected language (only if a translation is provided by the LP, of course).

To enable multi-language support in DNN, log in and navigate to

  • Admin/Languages: Install Language Pack
  • Enable Browser Language Detection (if desired)
  • In Edit Mode: Manage/Settings/Portal Language Settings:
  • Enable Language Parameter in URLs? check

This causes DNN to detect the desired language by parsing the URL string for a locale identifier, such as /en-us/ etc. at the begin of the local URL path. Additionally, the ?language=[locale identifier] query string parameter can be used to set the language. Note that the language in the path overrides the language parameter.

Warning: the language parameter is case-sensitive: http://www.dotnetnuke.com/Resources/Forums/forumid/77/threadid/453757/scope/posts.aspx#tblCommand_453757

Once LPs are installed and configured, there are two ways to implement multi-language content in DNN:

Content Localization

Content localization is a built-in functionality of DNN6, and can be enabled by navigating to

  • Host/Host settings/Other settings/Allow Content Localization: check
  • Admin/Languages: Enable Localized Content

Warning: This action is NOT REVERSIBLE, and DNN warns you about that.

Once Content Localization is enabled, the system keeps track of all pages and modules and their translation status. Initially, every page is copied to every installed language. Translations can be edited and published on page and module level. See this DNN video demonstration of the Content Localization feature.

Localization-aware Modules

The other method to implement multi-language capabilities is to develop and/or install ML-aware modules, such as the Nuntio Content HTML module.

It allows editing language-related content for all enabled languages in a single module. Content display is controlled by the currently selected language in DNN.

Moving / Copying DNN6 Installations

Before you start actually moving or copying a DotNetNuke 6 installation, you should prepare the DNN database to recognize the new host and application name.

Under Admin/Site Settings/Site Aliases click on Add New Alias, and add the new hostname/applicationname (or just hostname, if the new installation is directly under web root). (found on SO)

Next, copy the DNN directory to the new location, and add a DNN web application in IIS pointing to the new location. Make sure the IIS user (typically IUSR) is allowed to access the file system directories.

In SQL Server Management Studio, backup the original database. Create a new database and restore from the backup. If the database is restored to a different server, make sure the DNN user is able to access the new database.

Adjust the 2 connection strings to point to the new database.

Everything should run fine by now.

If you somehow forgot to add the new host aliases to DNN database, the DNN web application will not startup and present an error message indicating that the host/application names do not match any portals.

Locate the new database in SSMS, and open the PortalAlias table in edit mode.

Update the column HTTPAlias of the migrated portal(s) to the root URL of the new DNN installation.

Open the PortalSettings table in edit mode and change the SettingValue column of SettingName = ‘DefaultPortalAlias’ for the portal(s) to the same values.

Restart the web application. DNN should run now.

Lessons Learned Developing DNN Modules

Jumping into DNN development, here’s a couple of things I learned from developing my first modules.

Module Path

There are a couple of variables a module can derive its file system location (and thus relative URL paths) from:

ControlPath /dnn/DesktopModules/MyModule/
ModulePath /dnn/DesktopModules/MyModule/
Request.ApplicationPath /dnn
Request.CurrentExecutionFilePath /dnn/Default.aspx
ModuleConfiguration.ControlSrc DesktopModules/MyModule/View.ascx

If you need to reference files in the file system, or URLs relative to the module’s installation path, this statement

var IncludePath = ModuleConfiguration.ControlSrc
  .Replace("/View.ascx", "/");

gives you the module’s base directory.

Packaging

You can freely edit the module’s .dnn file to edit the components of the installation.

For example, if the module has no Edit or Settings dialog, remove Edit.ascx* and/or Settings.ascx* from the .dnn file under

component/
  desktopModule/
    moduleDefinitions/
      moduleDefinition/
        moduleControls

as you remove or exclude them from the DNN project (.csproj file).

If you do not have an Edit form, disable the registration of the Edit form in the ModuleActions getter of View.ascx.cs.

For layout definition and CSS classes in your Edit.ascx, see the HTML\EditHtml.ascx that comes with DNN.

View/Edit Mode

If your View.ascx should behave differently depending on whether it is displaying in View mode or Edit mode, use the following markup to distinguish the two modes:

<%  if (!DotNetNuke.Common.Globals.IsEditMode())  {   %>
      <!-- markup for edit mode -->
<%  } else { %>
      <!-- markup for view mode -->
<%  } %>

Closing Forms

To close a dialog, simply redirect to the current page:

protected void cmdUpdate_Click(object sender, EventArgs e)
{
  try
  {
    UpdateSettings();
    Response.Redirect(Globals.NavigateURL(), true);
  }
  catch (Exception exc)
  {
    Exceptions.ProcessModuleLoadException(this, exc);
  }
}

JavaScript

DNN uses a combination of JavaScript libraries

Embedding DNN pages in iframes

A typical DNN page contains menu, header, content panes, and a footer. The default skin of DNN6 is called DarkKnight and provides a page skin called „Host: DarkKnight – popUpSkin“ with just a single content pane and no headers, footers, borders, etc., and is thus perfect for embedding DNN pages inside an <iframe> either in the same DNN installation or from outside.

Display Time Range

You can set the period to display a page using the Start Date and End Date settings in the Advanced Settings section of the Module Settings.
Start and End Dates are only settable on a day level, there is no built-in way to display modules based on time of day.

Automatic Refresh

Automatic Refresh is supported on page level, but not on module level.

A little warning: the refresh interval is also active in Edit mode, so if you set the interval too short, you may not be able to set it to a longer interval directly in the page. You need to navigate to Host/Page Management and reset the refresh interval.

Getting Started with DotNetNuke 6, Windows 7, and IIS7.5

I wrote about DotNetNuke as a web application earlier this year, but did not document how to get started developing DotNetNuke modules. Since a simple web search delivers unfiltered results and information on versions 3 thru 6, let’s catch up on the infrastructure needed for DotNetNuke 6::

  • Download the MSBuild Community Tasks, an extension to the VS build utility
  • Download DNN (New Install or VS Starter Kit) (I am working with 6.1.4, in the meantime 6.1.5 has been released)
  • Download the DNN Module Development Template and follow the installation instructions
  • Install the MSBuild tasks
  • Unzip DotNetNuke.zip into a new directory (under wwwroot or under your development directory)
  • Right click on the DNN directory and assign full control for the IIS_IUSRS user (this is the system user that IIS typically uses to access the file system)

In IIS Manager

  • create a web application pointing to the DNN directory
  • select the web application
  • open the .Net Compilation feature, and set Debug = true
  • in the Basic Settings dialog, make sure an Application Pool with .Net 2.0 Framework is assigned (Integrated Mode seems to work find)
  • select Deactivate Ping in Application Pool to avoid timeouts during debugging

The debugging-related settings are necessary to avoid the error message

The web server is not configured correctly. See help for common configuration errors. Running the web page outside of the debugger may provide further information.

when trying to debug from Visual Studio.

Use SQL Server or SQL Server Express as your database server and

  • make sure SQL Authentication is enabled
  • create a database for DNN
  • create a login for the DNN user
  • create a user in the DNN database with the newly created login
  • update the TWO connection strings in the DNN web.config file

Point your browser to the web application to make sure the settings are correct, and step thru the installation procedure of DNN.

Start Visual Studio (both 2008 and 2010 seem to work with the project template). To create a DNN module, execute:

  • Create project “DotNetNuke C# Compiled Module” inside DesktopModules directory of the DNN installation
  • Adjust project settings according to generated Documentation.html
  • See also the original blogs here and here describing the usage of the template.
  • Edit the project’s .dnn file (copyright, license, readme, etc.)
  • Implement the module’s functionality
  • Switch to Release mode for 1st deployment and Build
  • Build in release mode will generate two zip files in the packages directory, namely [modulename][version]_Install.zip and [modulename][version]_Setup.zip
  • In DNN, login as Host, select Host menu, Extensions, hover over the Manage button, start the Install Extension Wizard, and upload the Source.zip from the DesktopModules/MyModule/packages directory
  • Installation should not produce any error messages. Click Return.
  • Since the uploaded files overwrite the original source files, VS will alert you with a “project changed” message.

That’s it!

Modules can be assigned to categories, but this assignment is performed by the DNN administrator, not the developer.

  • In the Admin menu, select Taxonomy
  • Click the Edit icon on entry Module_Categories
  • Press “Add Term” to add new module category, input the category name, and hit “Create New Term”
  • Press “Update” to save
  • In Host/Extensions,
  • Click the Edit icon on module
  • Select the desired Module Category
  • Scroll down and press “Update Extension”

Online Help and Computer-Based Training using DotNetNuke

A project that I am working on deals with DotNetNuke 6 as online help and/or Computer-based Training software (CBT) for an existing web application.

Both the application and DotNetNuke manage registered users and their privileges, and DNN handles the content management for the online help contents.

Note that a couple of years ago I wrote about Wikis for online help, but a Wiki (typically) does not allow for user-specific or role-specific content to be displayed, and was found as insufficient for this scenario.

The solution that we came up with was that both applications are synchronized via a custom Web Service that (mainly) matches the logins of both applications and logs application access.

Each application needed to be extended by a hyperlink mechanism that calculates the web address of the corresponding page in the other application (usually the landing page with some parameters), nicknamed “jumper” in the chart below, and the landing page itself which performs a login based on a session ID in the URL string, and redirects to the application or content page, also encoded in a URL parameter.

The DotNetNuke modules were developed using the DotNetNuke Module Development Template on CodePlex.