IT Crossing
Sunday, February 05, 2012 | Register | Login 
Minimize
 Creating RESTful DNN URLs (Caution: Requires Changes to the Core)

 

Update: Since writing this, I've had the chance to use Bruce Chapman's Url Master and I would recommend purchasing and using this product instead of trying to write your own URL rewriter.  Unless you're just interested in the mechanics of modifying the UrlRewriteModule code in DotNetNuke, which you may have an interest in if you're trying to make custom changes to your URLs that are not possible with a product like Url Master, you are better off buying Bruce's product or using the free version he's made available on his site. 

Caution: The following resource requires making changes to the DotNetNuke core.  Making changes to the core can make it difficult to perform future upgrades.  The video and code are provided as is with no claims regarding their usefulness within your environment.  Of course, we hope it proves to be helpful and saves you some time!

The following video demonstrates how to change the DNN core to support more restful URLs.  The code from the video is included below and we welcome your comments.  Please leave comments in the corresponding blog entry (Coming soon!). 

Just Start the Video Please!

Note also, that the changes in this video depend on the use of the IIRF ISAPI filter.  The relevant code from a sample IIRF ini file is included below.

Code Snippets from Video

Code at beginning of RewriteUrl in UrlRewriteModule.vb

Note that the code below does not include the end if which should be added to the very end of the RewriteUrl procedure.  Also note that the code has been updated from the original version seen on the video.  Child portals were no longer distinguishable from RESTful URLs and so code was added to the beginning to check if the URL being requested exists in the internal portal alias cache maintained by DNN.  The code was also updated to ensure that calls to retrieve the TabId are successful in older version of DNN.  The following code was tested in DNN 4.5.

'ITC Custom Code - DW 11/16/2007 - Added to allow access to profiles without default.aspx.
'   Note: this requires that the a rewrite rule such as follows is added to the ini file
'   for the IIRF Isapi Rewriter.
'   RewriteRule  ^((/([^/.\?]+))*)/*$        /default.aspx?itctabpath=$1
 
' This regex pattern will send a username variable, which we will either need to look up 
' (assuming we need to find a users page if users each have their own page) or at minimum, just 
' short circuit the rest of this page since the URL is alreay in a form which the BeginRequest
' procedure can process.
If (Not Request.QueryString("itctabpath") Is Nothing) Then
    ' For now, we don't need to do anything here.  If the system is updated so that each user
    ' has their own page, then we'll need to look up the TabId for the user's page here.
    Dim itcTabPath As String = Request.QueryString("itctabpath").ToString()
    Dim objTC As New TabController
    Dim intTabID As Integer
    Dim PortalAlias As String = GetDomainName(Request, True)
    Dim pc As PortalController = New PortalController
    Dim portalID As Integer
    Dim objTabInfo As DotNetNuke.Entities.Tabs.TabInfo
    Dim objPortalAliasInfo As PortalAliasInfo = Nothing
    Dim bChildPortal As Boolean = False
    Dim _portalSettings As PortalSettings
 
 
    'CHECK FOR CHILD PORTAL
    'First check the PortalAlias collection to see if we are receiving a request for a child
    'portal (eg. http://www.itcrossing.com/recreation).
 
    'Get the PortalAliasCollection and prepare a portal alias which we will check to see if 
    'it exists.
    Dim pac As PortalAliasCollection = DotNetNuke.Entities.Portals.PortalSettings.GetPortalAliasLookup()
    Dim PotentialFullPortalAlias As String = PortalAlias & itcTabPath
 
 
    'Check to see if potential child portal alias exists
    If pac.Contains(PotentialFullPortalAlias) Then
        'CHILD PORTAL
        'If it does, then grab the PortalId and the PortalAliasInfo since we need these.
        portalID = pac(PotentialFullPortalAlias).PortalID
        objPortalAliasInfo = pac(PotentialFullPortalAlias)
        bChildPortal = True
    Else
        'RESTful URL
        'In the case of a RESTful URL, we retrieve the PortalId using GetPortalByAlias
        Dim dr As IDataReader = CType(DataProvider.Instance().GetPortalByAlias(PortalAlias), IDataReader)
 
        Try
            If dr.Read Then
                portalID = dr.GetInt32(dr.GetOrdinal("PortalID"))
            End If
        Finally
            If Not dr Is Nothing Then dr.Close()
        End Try
        bChildPortal = False
    End If
 
 
    'Retrieve the TabId based on the tab path for RESTful URLs  Ignore if child portal
    If Not bChildPortal Then
        ' This call isn't supported in older version of DNN, so we just use the 
        ' implementation from recent version of DNN to retrieve the same data.
        'intTabID = objTC.GetTabByTabPath(portalID, itcTabPath.Replace("/", "//"))
 
        'For more recent versions of DNN (somewhere around 4.6 and higher), you can comment
        'out the next 25 lines of code and uncomment the line above which starts with intTabID = ...
        Dim strKey As String = itcTabPath.Replace("/", "//")
        Dim tabpathDic As Dictionary(Of String, Integer) = TryCast(DataCache.GetCache("itcTabPathCache"), Dictionary(Of String, Integer))
        If tabpathDic Is Nothing Then
            tabpathDic = New Dictionary(Of String, Integer)(StringComparer.CurrentCultureIgnoreCase)
 
            Dim objTabController As New TabController
            Dim arrTabs As ArrayList = objTabController.GetAllTabs()
            For Each objTab As TabInfo In arrTabs
                ' add the TabPath and TabId to the TabPath Dictionary
                If Not objTab.IsDeleted Then
 
                    If Not tabpathDic.ContainsKey(strKey) Then
                        tabpathDic(strKey) = objTab.TabID
                    End If
                End If
            Next
            DataCache.SetCache("itcTabPathCache", tabpathDic, False)
        End If
 
        'Get the TabId for this particular tab path.
        If tabpathDic.ContainsKey(strKey) Then
            intTabID = tabpathDic(strKey)
        Else
            intTabID = -1
        End If
 
    End If
 
    'create a PortalAliasInfo object based on the PortalAlias so we can retrieve a PortalSettings
    'object and tuck it away in the Context for retrieval in OnBeginRequest below.
    If objPortalAliasInfo Is Nothing Then
        objPortalAliasInfo = PortalSettings.GetPortalAliasInfo(PortalAlias)
    End If
    If Not objPortalAliasInfo Is Nothing Then
        _portalSettings = New PortalSettings(intTabID, objPortalAliasInfo)
        app.Context.Items.Add("PortalSettings", _portalSettings)
    End If
 
    'Check to see if we have a TabId.  If not, we get the default TabId for the child portal
    'since the only reason we shouldn't have a TabId at this point is if we are here because of 
    'a request for a child portal rather than a request for a RESTful URL.
    If intTabID <= 0 Then
        'CHILD PORTAL
        'Get the TabId from the portal settings object.
        intTabID = _portalSettings.HomeTabId
        'Rewrite the URL in the standard QueryString format.
        RewriterUtils.RewriteUrl(app.Context, "~" & itcTabPath & "/" & glbDefaultPage & "?TabID=" & intTabID.ToString())
    Else
        'RESTful URL
        'We already have the TabId, so just rewrite the URL in standard QueryString format
        'Rewrite the URL in the standard QueryString format.
        RewriterUtils.RewriteUrl(app.Context, "~/" & glbDefaultPage & "?TabID=" & intTabID.ToString())
    End If
 
Else


If Statement from OnBeginRequest in UrlRewriteModule.vb

The following If statement is added to OnBeginRequest after the call to RewriteUrl and the subsequent 5 variable declarations.  The ending if isn't included since it should wrap the entire section all the way down to where the check is made to see if PortalId <> -1.  What we want to do here is short circuit the section of code which is attempting to retrieve the PortalId since we can retrieve this from the portal settings object we tucked away in the Context object.

If Not app.Context.Items("PortalSettings") Is Nothing Then
 
    Dim _
        objPortalSettings As PortalSettings = _
            CType(app.Context.Items("PortalSettings"), PortalSettings)
 
    PortalId = objPortalSettings.PortalId
    objPortalAliasInfo = objPortalSettings.PortalAlias
 
Else

Modified If Statement Used After PortalId is known

The following lines of code replace the code in the first part of the If statement that begins with If PortalId <> -1 Then...

Dim _portalSettings As PortalSettings = Nothing
If app.Context.Items("PortalSettings") Is Nothing Then
    _portalSettings = New PortalSettings(TabId, objPortalAliasInfo)
    app.Context.Items.Add("PortalSettings", _portalSettings)
Else
    _portalSettings = CType(app.Context.Items("PortalSettings"), PortalSettings)
End If

Sample Code from IIRF .ini File

Note that I've stripped all the normal comments out of the ini file to show just what was needed.

RewriteLog  c:\temp\iirfLog.out
RewriteLogLevel 0
MaxMatchCount 10
 
RewriteRule ^/([^.?]+[^.?/])$ /$1/ [R=301]
RewriteRule  ^((/([^/.\?]+))*)/*$        /default.aspx?itctabpath=$1
 
IterationLimit 10
    
 Talk to Us!
Email:
Name:
Subject:
Message (Optional):
Send   Cancel