25 June 2009

DNN Event Viewer spider errors

If you are getting "Page Load Exception" errors in the DotNetNuke (DNN) [Admin][Event Viewer] with the UserAgent indicating a web crawling spider, then you need this fix:

The fix: Get the Browser Caps - v2 by Oliver Hine. Unzip the file App_Browsers.zip to get the file OtherSpiders.browser - put this in the App_Browsers directory of your DNN site. Note that your DNN site will automatically restart, so perhaps do this at a quiet time.

Alternatively (if you want), you should be able to stop these spiders accessing your site using a suitable robots.txt file.

This patch is needed for DNN 4.9.4 and possibly earlier. I think that it may be fixed in DNN 5.1+.

Spiders fixed: This file has fixes for these spiders: Baiduspider, Yandex, ia_archiver, Sphere, Feedfetcher-Google, Yanga, worio, zibber, twiceler, voliabot, aisearchbot, robotgenius and R6_FeedFetcher.

Spiders not fixed: However it doesn't fix TweetmemeBot - I failed to add support for this. Read this Tweetmeme.

Update: doesn't seem to work for Twiceler or voilabot.

What's happening: DNN code is asking ASP.NET for details of the browser capabilities. ASP.NET gets this from the UserAgent string. DNN asks for the major and minor version numbers of the browser and causes an exception if these cannot be determined from the UserAgent. ASP.NET uses various .browser files (in the framework directories and in the web app's App_Browsers directory) to work out what browsers can do. As the UserAgent provided by these spiders doesn't follow a standard regex pattern, ASP.NET cannot get the major and minor versions.

Thanks to: Barry Sweeney for his prompt help on Twitter - and Oliver Hine of course.

Below is the DNN exception that I get before applying this fix:





Message: DotNetNuke.Services.Exceptions.PageLoadException: Value cannot be null. Parameter name: String ---> System.ArgumentNullException: Value cannot be null. Parameter name: String at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) at System.Web.Configuration.HttpCapabilitiesBase.get_MajorVersion() at DotNetNuke.UI.Utilities.ClientAPI.BrowserSupportsFunctionality(ClientFunctionality eFunctionality) at DotNetNuke.UI.Utilities.ClientAPI.get_ROW_DELIMITER() at DotNetNuke.UI.Utilities.ClientAPI.RegisterClientVariable(Page objPage, String strVar, String strValue, Boolean blnOverwrite) at DotNetNuke.UI.Skins.Controls.Search.Page_PreRender(Object sender, EventArgs e) at System.Web.UI.Control.OnPreRender(EventArgs e) at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) --- End of inner exception stack trace ---

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'

14 April 2009

DNN development and production sites

Bro John wants me to write up how he does DotNetNuke (DNN) upgrades and site copies. As a preamble, he wrote this:
=============
I would think anyone with a production site would want three copies
A - a live site
B - a transition site
C - a development site

also
D - a trash site

As most of my stuff is now in phdcc.CodeModules, I develop these until they work on C, I copy these to D to see if they still work in a different environ. I then copy them to A and test.

I would prefer to have a B which means I can shut down A and have users running on B and then swap back to A later if for example I do a DNN version upgrade.

Doing a DNN version upgrade on a live site is just asking for trouble.

If this sort of stuff is not sorted in DNN properly then I wouldn't consider it a solid environment to do anything serious in.

I am trying to be careful about keeping all database/email server specific stuff in a few files. Hard links to other pages on the site are OK if you copy the entire site correctly.

Maybe bigger players have other tricks they pull. Maybe they swap DNS pointers. However that takes time to permeate and produces horrid cahcing problems.