Organizing Your Project
The various options there are to develop a module for DNN can be overwhelming. There are so many different ways to do this that it's easy to get lost. As ASP.NET and DNN have progressed new methods evolved over the course of the last decade to make things easier for you and to give you more options. And similarly to the toolbox you use, many of the decisions will come down to personal preference.
Inline vs External Module Creation
You'd be surprised what you can do without any tools. DNN includes a number of ways in which you can create a module inline in the application. As modules are "active components" you need to be logged in as superuser. If you switch to Edit mode and you hover over any module's menu, you'll see the "Develop" menu item. This allows you to edit any of the module's controls directly inside your browser. DNN includes a powerful code highlighter called CodeMirror to help you. Combined with the ability to create modules from scratch on the Extensions page (through the "Create New Module" button) this means you can create an entire module without ever going to the files directly. Let me demonstrate this.
Hello World
Open up your DNN site, log in as host and go to Host > Extensions
From the buttons at the top click on "Create New Module"
Select "New" in the dropdown labeled "Create Module From"
Use the "Add Folder" buttons to create an owner folder "WROX" and a module folder "HelloWorld"
Select C# and enter "HelloWorld.ascx" as file name and "HelloWorld" as module name. You should have something like fig 3.
- Select "Add Test Page" and click "Create Module". You should now have a screen like fig 4.
- Change to edit mode using the control bar's menu at the top right. You'll see the module gets its menu shown. Select "Develop" from the gear menu (fig 5).
- You will see a popup screen appear with two tabs and on the first tab a dropdown with our HelloWorld.ascx selected. Now delete all but the first line and insert the following on the second line, update and close the dialog
<h1>Hello <%= UserInfo.DisplayName %></h1>
- You'll now see "Hello SuperUser Account" displayed in the module. SuperUser Account is the DisplayName of the currently logged in user. If you'd log off you'll just see "Hello" as there is no more user logged in.
Congratulations. You've now successfully created your first bit of active content! Now let's take this up a notch. Go back into development mode and copy the following underneath the title:
LISTING 1: Hello World Web Forms example
<table>
<%
foreach (var tab in GetTabs()) {
%>
<tr>
<td><%= tab.TabID %></td>
<td><a href="<%= DotNetNuke.Common.Globals.NavigateURL(tab.TabID) %>"><%= tab.TabPath %></a></td>
</tr>
<%
}
%>
</table>
<script runat="server">
public List<DotNetNuke.Entities.Tabs.TabInfo> GetTabs()
{
return DotNetNuke.Entities.Tabs.TabController.GetPortalTabs(PortalId, -1, true, true);
}
</script>
What you should see is a list of pages with their path as a hyperlink to those pages in DNN. In the above there is a code block inside script runat="server" that runs solely on the server (you won't discover it in the HTML output of the page). This method is then used in the ASP.NET code above to fill an HTML table. Finally you'll note that we've used several methods from the DNN API (NavigateURL, GetPortalTabs). Finding your way around the DNN API will take some time but the more modules you make the more you'll find yourself using the same familiar bits of the framework.
The Razor Host Module
Over the years a lot of criticism has been levelled at Microsoft for its Web Forms implementation. In part because it was bulky and in part because Web Forms does not allow the programmer much control over the HTML that is emitted by the server. To address these issues we've seen the emergence of MVC and its associated view engine Razor. A Razor file is not unlike a Web Forms ascx/aspx in that it is basically a template telling the engine what HTML to render. Most significantly you'll find that the <%
%>
; way of separating code from markup has been replaced with a single @ symbol. So <%= Math.Sqrt(81) %>
; is simply @Math.Sqrt(81)
. But that is just a slight advantage in brevity. Razor files no longer support the Web Forms drag and drop interface in Visual Studio. You'll need to craft the HTML yourself. Many, including myself, see this as a step forward.
A lot of Razor's features are related to using MVC as a development approach. Although DNN will probably support that in the future, for now DNN still assumes you are rendering controls to a Web Forms page. To benefit from the Razor hype, DNN includes (since version 5.6.1) the so-called Razor Host module. It will allow you to create a razor script and the module will render this script on a DNN page. To access certain DNN features it includes three Helper Objects: DnnHelper, HtmlHelper and UrlHelper. DnnHelper allows access to details about the current module, tab, portal and user. The HtmlHelper makes accessing localized texts easy. And finally the UrlHelper allows for easy construction of DNN urls.
Using this feature we will now redo the Hello World example in the Razor Host module.
Hello World in Razor
Create a new page and add the "Razor Host" module to this page.
Switch to Edit Mode and from the module's pencil menu select "Edit Script"
Click to add a new script file and give it a meaningful name like HelloWorld or something. Click to add the file.
Now select the file from the dropdown and replace the contents with this:
<h1>Hello @Dnn.User.DisplayName</h1>
- Select the checkbox to make the script active and click to save and return to the page.
You will now see what we had before; a welcome message for the logged in user. Again we'll add a list of pages in our current portal with links to each of them:
LISTING 2: Hello World Razor example
@functions {
public List<DotNetNuke.Entities.Tabs.TabInfo> GetTabs()
{
return DotNetNuke.Entities.Tabs.TabController.GetPortalTabs(Dnn.Portal.PortalId, -1, true, true);
}
}
<table>
@foreach (var tab in GetTabs())
{
<tr>
<td>@tab.TabID</td>
<td><a href="@(DotNetNuke.Common.Globals.NavigateURL(tab.TabID))">@tab.TabPath</a></td>
</tr>
}
</table>
You can see the result in figure 6. As you can see from the above, Razor bears a lot of resemblance to Web Forms in the way it does the templating of your data. But there are some neat features that Web Forms doesn't have and vice versa. A fuller discussion of Razor falls outside the scope of this chapter, however.
Final Remarks
Although it's perfectly valid to develop your module inline in DNN, you'll obviously miss out on the smoother experience of using a full IDE. Plus you won't be able to compile your code either. It's fine for a half-day quickie. But for anything more elaborate you'll want to use Visual Studio. For the remainder of the chapter we'll assume you are using Visual Studio to develop your module.
WAP vs. WSP
Now you'll face one of the oldest dichotomies in module development. When DNN was first created, Visual Studio (2003) included a so-called Web Application Project (WAP) template. This template assumed that you were developing a number of user controls inside a larger Web Forms application and that you would compile the code behind to a dll in the bin folder. That is exactly the route DNN took with modules. So to many it came as a nasty surprise that in Visual Studio 2005 this option was deprecated in favor of Web Site Projects (WSP) which took a very different approach. Microsoft later corrected this with a service pack, but it was an illustration of how we can be left behind by developments in Redmond.
In a WAP project you only load your controls and refer to the rest of the project by including the dlls as references in your project. The result of your work will be the ascx files plus a dll that is the compiled code of your project. With WSP you load the entire web site (i.e. DNN) and add code files to the App_Code folder which will then get compiled dynamically when your application first loads. WSP was obviously meant to speed up development as you could change code and just hit refresh in your browser. But having template (.ascx) files under DesktopModules and the code files "far away" under App_Code was a change that was not conducive to larger, more complicated modules. Plus it made creating a distributable module significantly more complex. And it also meant installing the source code on the client's server which ruled out all commercial module developers.
To this day you can create a module using either WAP or WSP approaches. But most developers use the WAP approach as it totally isolates your work from DNN and that is seen as a bonus. The WSP approach actually only makes sense when faced with a "quick and dirty" task for a single site.
Inside vs Outside the root
If you have downloaded and unpacked the source version of DNN, you may have noticed something odd. The modules are not under \Website\DesktopModules\etc where you might have expected to find them. Instead they are under \Dnn Platform\Modules. So how does this work? The way that the project has been set up is with the module projects outside the root of the website (i.e. outside the DesktopModules folder). The site is running under \Website, so how do developers work on the module and see their changes? Inevitably this means that if you make a change in one of the ascx files it won't be visible when you refresh your browser.
The clue is in MSBuild tasks. What you need to do is to click Rebuild in Visual Studio. What will happen is that some extra (MSBuild) tasks will run at the end of the regular compilation of the module which copies over all the files to the Website\DesktopModules folder. This is a very clever way of leveraging build automation. The upside of this way of organizing your project is that it is even more isolated from the website. And I presume that for such a large project as the DNN Platform this makes good sense. But it remains rare for module developers to use this approach. Plus: we don't work on the DNN site itself simultaneously. So we are not expected to interfere at all with the DNN files. That already provides enough isolation.
In short: it is possible to develop your module in total isolation in some directory far away from your test DNN site. But it requires quite a bit more jumping through hoops and the benefits are limited. So in the remainder we will assume to be developing inside the test DNN installation.
Note
This is an extract from the Wrox book Professional DNN 7 by Shaun Walker et al. Copyright remains with P.A. Donker and Wiley Publishers.