I spent a whole cache of hours yesterday tracking down a bug reported with the iFinity Friendly Url Provider. This bug was that the RSS feed button which is in-built into DNN modules didn’t work. The problem turned out to be the same old thing that causes the majority of incompatibility between my Friendly Url Provider and other DNN Core functions and DNN Modules – misuse or non-use of the Friendly Url Provider API. So here’s my plea to DNN developers worldwide, both core and third party: please use all the Friendly Url API features to create your Urls, don’t craft them up yourself within your code!
Generating a DNN internal site Url
Anyone who has written any DNN code will sooner or later have to generate a Url to put as the Href behind a generated link. Soon enough they will come across the DotNetNuke.Common.Globals.NavigateUrl( ) call. This takes in the page you’d like to generate the Url for, plus some other options regarding Portal Alias (the domain name), and any query string values. Great! It works like a charm, and the underlying NavigateUrl() code also figures out whether you are running friendly Urls or not.
However, developers often want something slightly different to the /default.aspx reference that inevitably shows up at the end of the Url.
Most of the time that I have seen, the developer takes the power into their own hands and starts replacing ‘default.aspx’ with their desired value in their own code. The code inevitably looks like this:
Dim url as string = DotNetNuke.Common.Globals.NavigateUrl(tab, path, etc..)
url = url.Replace(‘default.aspx’, ‘my-clever-hack.aspx’)
Because the Url Rewriting actually ignores the .aspx, it doesn’t matter, and the code works.
Everyone is happy, and the code is checked in. Don’t believe me? Try going to this Url and tell me if it works or not : http://www.dotnetnuke.com/tabid/125/But_I_Don't_Want_A_Download.aspx (it’s the DotNetNuke downloads page)
You don’t need the default.aspx – you only need the /tabid/125/.aspx - everything else is superfluous.
The .aspx is only there to tell IIS it’s an asp.net request, and the tabid/125/ does all the heavy lifting in finding the right page to display.
The problem comes when the developer replaces the page name with something that needs to be in the Url to make something work.
In the case of the RSS feeds, you’ve got to request the page called ‘rss.aspx’.
In the e-commerce module ASPDOTNETSF for DNN, it contains the product information, like this: /products/tabid/55/p-1-product-name.aspx
In both of these cases, the developers that wrote the code replaced the ‘default.aspx’ with the custom page value after calling the Friendly Url Provider. And this stops the Friendly Url Provider knowing that there is a special case to handle.
Only then someone, in the future, replaces their standard Friendly Url Provider with a new one – and all of a sudden things don’t work anymore, because the default.aspx gets removed from the resulting url that gets generated. Anyone interested in getting Friendly Urls to work on DNN considers the /default.aspx appendix on all the pages to be the greatest problem, closely followed by the tabid/nn path. So default.aspx is always the first up against the wall when the Url revolution comes, and any code that assumes it will be there is now broken.
The Correct Way to get a Url when you want to replace default.aspx
Now, the only module I know of that works correctly is Scott McCulloch’s News Articles module, which isn’t surprising given that he wrote the DNN Friendly Url Provider. He knows how to call the Friendly Url API correctly, which is to forget NavigateUrl, and call the FriendlyUrlProvider, like this:
DotNetNuke.Services.Url.FriendlyUrl.FriendlyUrlProvider.Instance().FriendlyUrl(tab, "~/Default.aspx?TabId=" & tab.TabID.ToString(), "My_Custom_Page_Name.aspx")
This code calls the Friendly Url Provider, and uses a little-known feature which allows you to specify a custom page name. I’ve posted on DotNetNuke.com forums before about how the Blog module could instantly make thousands of people happier if they used this call to generate the internal links for the module. All it would have to do is supply the title for the blog post as the custom page name, and the entire module would become more valuable to those who are of a SEO bent.
In truth, the NavigateUrl( ) call should probably have one more overload added to it, which takes a custom page name and passes that through to the configured Friendly Url Provider. Developers would be much more likely to discover the overload and say ‘hey, neat, I can provide any page name I like’. If the DNN core member responsible reads this, then I’ll give you the code on how to do it.
At least the Blog module doesn’t modify the url after the Friendly Url Provider call, though. The other modules which do modify the Url after calling the Friendly Url Provider leave developers (like me) of customized Friendly Url Providers out in the cold when it comes to interoperability. Developers take note: you need to tell the Friendly Url Provider that a special case is being used, so that it can handle it.
In the case of RSS feeds, just by modifying the core code to call down to the Friendly Url Provider, it’s easy to intercept the call and say ‘oh, that’s for an RSS feed – well, in that case, here’s your RSS url’. Without it, you are stuck with /default.aspx on every page you want an RSS feed. For websites with friendly Urls, that’s a disaster case.
In the case of ASPDOTNETSF, well, that’s even more difficult. The module won’t work at all with any Friendly Url Provider because the whole thing is built on the assumption that DNN urls look like this : domain.com/pagename/tabid/xx/default.aspx. Change that, and the whole thing doesn’t work.
Well, I shouldn’t say ‘won’t work at all with any Friendly Url Provider’ because I have developed a way to make it work with my Url Master for DotNetNuke module. It’s not just by having ‘unfriendly’ urls on the product pages, either, I’m talking about full, human friendly Urls for the ASPDOTNETSF product and category pages. But it would have been a lot easier if they had used the Friendly Url API in the first place!
Since writing this, Chris Hammond from Engage has informed me that the Engage:Publish module does indeed use the right way of generating a custom Url. I should have known this already from another thread regarding the iFinity Friendly Url Provider - I was able to sort out a problem he was having precisely because the Engage:Publish module behaved in the right way! So add another one to the list.