DotNetNuke Modules – Automating the PA

tran-wtr-04 Last week we showed how to create a basic Program Assembly (PA).   You’ll remember that a lot of the process was manual.  And if you make a process manual, it is always open to error.

But there is a better way.  By using the Microsoft Web Deployment Project along with the MSBuild Community Task, you can completely automate all but creating the DNN file.

You will need:

Download and install those now.

The rest of this post will assume that you’ve already created the module that you want to build the Program Assembly for.

  • You will want to create a deployment project by selecting the DNN Project in the solution explorer and then selecting the “Build” > “Add Web Deployment Project…” from the menu.  Once you select the menu option,  a dialog will appear prompting you for the name of the deployment project and the location you want it to be in.  I find it easiest to place the project under inetpub/wwwroot like any other web project, but it really will not matter where you place it.
  • Once the web deployment project has been created, right click the project and select “Property Pages” from the menu.  We need to change a few of the defaults the deployment project has set up.
    1. When the properties dialog displays, select “Output Assemblies” from the tree on the left-hand side of the dialog.  Then select the radio button that says, “Merge each individual folder output to its own assembly.”  You’ll probably also want to set the file and assembly version here, so check that box and enter the values.
    2. Next, select “Deployment” from the tree and check the “Remove the App_Folder from output location” check box.
    3. Press “OK” to exit.
  • To get the rest of the magic to happen we need to drop into the msbuild source.  To do this, right click on the deployment project and select, “Open Project File” from the menu.  This should bring up an XML file in a text editor.  Once you have it open, you should see an import statement around line 47 (near the bottom):
      <Import Project="$(MSBuildExtensionsPath)\Microsoft\WebDeployment\v9.0\Microsoft.WebDeployment.targets" />

    You’ll want to insert another line below it and move the two lines up before the first <ItemGroup> element:

      <Import Project="$(MSBuildExtensionsPath)\Microsoft\WebDeployment\v9.0\Microsoft.WebDeployment.targets" />
      <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />

    This will allow this project to use the build extensions we installed earlier.

  • You should also see several “PropertyGroup” sections followed by an empty ItemGroup section.  Within this item group section we will want to exclude files from being part of the build.  For example, there are VB files that are part of the DNN install that we don’t want to be part of the build, so we need to exclude them.  Since I work in Csharp, I can tell it to just exclude all of the VB files, but if you work in VB, you will need to exclude them explicitly.  You’ll also want to exclude the following:
    1. Anything under the admin directory
    2. Anything under the portals directory
    3. Any ASPX or ASCX files in the root directory of the website project
    4. Anything under the components directory
    5. Anything under the config directory
    6. Anything under the images directory
    7. Anything under the install directory
    8. Anything under the js directory
    9. Anything under the providers directory
    10. Anything under any module directories that are not the module you are building the deployment project, and ultimately a PA, for.
    11. My ItemGroup section looks like this:
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\admin\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\Portals\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.vb" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\*.as?x" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\Components\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\Config\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\images\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\install\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\js\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\providers\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\DesktopModules\HTML\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\DesktopModules\AuthenticationServices\**\*.*" />
          <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\DesktopModules\SplitTester\**\*.*" />

      If you are not familiar with msBuild, the \**\*.* means “Any files in the directory or in any of its subdirectories.”

  • If you build the deployment project now, you should end up with files in the directory you specified when you created the deployment project.  As a note, you may want to set the deployment project up so that it only builds in release mode.  Otherwise, you’ll have to go through this extra build step while you are developing, which can significantly slow you down.
  • There are two directories in the deployment project that we are interested in.  The bin directory should have a dll for all the files in the App_Code/ModuleName directory and a DLL for all of the files in the DesktopModules/ModuleName directory.  If you are using the multiple project method to create DLLs for the BaseProvider and SQLProvider you’ll also have a DLL for the base provider project and a DLL for the SQL provider project.  There should also be ASCX and RESX files that are part of our module under the DesktopModules directory.  If we set up everything correctly, there should only be one directory there.
  • Next, we need to copy all of the files that we want to be part of our installation into one installation directory.  To do this, you’ll need to uncomment out the after build section:
      <Target Name="AfterBuild">

    And place some copy commands in it.  Here’s just one line of many such lines from one of my files:

        <Exec Command="copy .\release\bin\App_SubCode_dmbcllcStore.dll .\installation\App_SubCode_dmbcllcStore.dll" />

  • Of course, if we run this again, the files will already be there, so we’ll also want the first line in our AfterBuild section to delete the existing files.  You can do that with this line:
        <Exec Command="del /Q installation\*.*" />

  • Finally, we’ll want to create a programmable assembly out of all of this.  This is simply a matter of taking all of  the files in the installation directory and putting them in a zip file.  We can have the build process do this for us by building a named list of all of the files in the installation directory (still in the AfterBuild section):
        <CreateItem Include="installation\*.*">
          <Output ItemName="ZipFiles" TaskParameter="Include" />

    And then telling the build process to make a zip file out of them:

        <Zip ZipFileName=""
             Files="@(ZipFiles)" />

  • Assuming you’ve done everything, the way I’ve intended, you should now be able to build your solution and end up with a zip file that you can install into DotNetNuke.While it may be obvious to some, it probably needs to be stated that you’ll want to create a separate deployment project for each module you create.  But you can create multiple modules using the same DNN project.  You’ll just need to exclude the files from all of the other modules in each web deployment  project you create.

I’m sure if someone wanted to get really clever, they could actually use this process to create the DNN file for them as well so that there is only one file that has to be modified when a new file is added to the PA.  For the amount of time it would save, I haven’t found that effort worth the time.  For the most part, the number of files in a module remain rather static.


Other post in DotNetNuke - Module Development

Most Commented Post

Leave a Reply

Comment Policy:

  • You must verify your comment by responding to the automated email that is sent to your email address. Unverified comments will never show.Leave a good comment that adds to the conversation and I'll leave your link in.
  • Leave me pure spam and I'll delete it.
  • Leave a general comment and I'll remove the link but keep the comment.

Notify me of followup comments via e-mail