To C# version
If you have followed the previous parts of this tutorial, you should now have your module and its controls installed on your local DotNetNuke site. This is a great starting point for creating an install package. When DotNetNuke installs an extension package (which is really just the extension’s files zipped together) it uses the installation manifest. The installation manifest is an XML document with a .dnn file extension in the installation package. Basically it tells DotNetNuke which files to put where and which database scripts to run on install/uninstall.Lucky for us we have access to a great extension installation manifest generator; namely DotNetNuke itself.
Before we start creating the installation package we should finish an important part of it; the database uninstall script.Back in part 3 we created the database install script where the table and stored procedures are created. In the uninstall script we should remove these.Open up the file Uninstall.SqlDataProvider and replace its content with the following:
/** Drop Table **/
ALTER TABLE {databaseOwner}[{objectQualifier}SipidCode_Products] DROP CONSTRAINT [FK_{objectQualifier}SipidCode_Products_Modules]GO
DROP TABLE {databaseOwner}[{objectQualifier}SipidCode_Products]GO
/** Drop Stored Procedures **/
DROP PROCEDURE {databaseOwner}[{objectQualifier}SipidCode_GetProducts]GO
DROP PROCEDURE {databaseOwner}[{objectQualifier}SipidCode_GetProduct]GO
DROP PROCEDURE {databaseOwner}[{objectQualifier}SipidCode_AddProduct]GO
DROP PROCEDURE databaseOwner}[{objectQualifier}SipidCode_UpdateProduct]GO
DROP PROCEDURE databaseOwner}[{objectQualifier}SipidCode_DeleteProduct]GO
Don’t forget to save the file.The batch script you just created will be run when the module is uninstalled and drops the Products table and its foreign key constraint. It also drops the stored procedures we created.
The Create Package screen is displayed. Just click the Next link.
The Choose Files to include screen is displayed.
Only the files shown in the picture above should be included in the package:
If there are any others, delete those lines from the text box. Click Next when you are done.
The Choose Assemblies to include screen is displayed. Just click Next.
The Create Manifest screen is displayed.
The text box contains the extension installation manifest for the package.Great to have it generated for you, isn’t it? When you are done admiring the manifest, click next.
The Create Package screen (not the same one as before) is displayed.
Remove the leading underscore character from the Manifest Field Name. A copy of the manifest file will be written into the project directory, overwriting the old and useless file we got from the template. Finish by clicking Next.The installation package is now created and written into the /Install/Module folder of your DotNetNuke installation.You can now go ahead and install the package where you want. I would recommend you to set up another DotNetNuke site for testing your installations on. If you install it on your development site it will collide with your project files and possibly result in trouble.
Tags: dotnetnuke, module, tutorial, visual basic
DotNetNuke | Modules | Tutorials
The settings control is used a bit differently from the view and edit controls. On the module settings page the standard settings are always shown. If you create and register a settings control it will be appended to the end of the standard settings and show up at the bottom of the page.While the edit page is used to edit the content of the module, the settings page handles the behavior of the module.
In our scenario we will create a settings control where you can configure if the product description will be displayed or not. And yes, I know it’s a pretty lame example but at least it doesn’t clutter up what I am trying to show: the basics for creating a settings control.‘Nuff said, let’s get to work.
Since the configuration of whether to show the product description or not is basically an on/off selection we will use a checkbox control. Open Settings.ascx and make the contents look like this:
<%@ Control Language="vb" AutoEventWireup="false" Inherits="SipidCode.Modules.Products.Settings" Codebehind="Settings.ascx.vb" %><%@ Register TagPrefix="dnn" TagName="Label" Src="~/controls/LabelControl.ascx" %><table cellspacing="0" cellpadding="2" border="0"> <tr> <td width="150"><dnn:Label ID="ShowDescriptionLabel" runat="server" ControlName="ShowDescription" Suffix=":"></dnn:Label></td> <td><asp:CheckBox id="ShowDescription" runat="server" /></td> </tr></table>
Now open Settings.ascx.vb. As you might remember from previous parts the view and edit controls inherit from PortalModuleBase. An important difference with the settings control is that it inherits from ModuleSettingsBase. If you are going to create your controls from scratch, don’t forget this.ModuleSettingsBase inherits from PortalModuleBase and gives us members necessary for loading and updating settings for the module instance.As you can see the templates have supplied us with overrides of the ModuleSettingsBase methods LoadSettings and UpdateSettings. Change them so they look like the following:
Public Overrides Sub LoadSettings() Try If (Not IsPostBack) Then If String.IsNullOrEmpty(CType(TabModuleSettings("ShowDescription"), String)) Then ShowDescription.Checked = True Else Dim show As Boolean If Not Boolean.TryParse(CType(TabModuleSettings("ShowDescription"), String), show) Then show = True ' Default to showing the description End If ShowDescription.Checked = show End If End If Catch ex As Exception ProcessModuleLoadException(Me, ex) End TryEnd Sub
Public Overrides Sub UpdateSettings() Try Dim controller As New Entities.Modules.ModuleController controller.UpdateTabModuleSetting(TabModuleId, "ShowDescription", ShowDescription.Checked.ToString()) Catch ex As Exception ProcessModuleLoadException(Me, ex) End TryEnd Sub
No black magic here either. In LoadSettings we get the settings from the settings store and populate our controls (in our scenario we only have one checkbox).In UpdateSettings we get the settings from the controls and stuff them back into the settings store.Note that settings are only stored in string form, so you have to convert them to and from strings if they are other types (as our show description setting is a bool).
Here is what the different keys are used for:
And this is how your settings control will look in a moment:
Just a couple of steps left until you can see it for yourself...Now compile your project so we can go ahead and register the control.In DotNetNuke, go to Host > Module Definitions. Locate Products in the list and click the edit icon. Scroll down to the Module Controls section and click Add Module Control.Make the following settings, then click Update:
Now go to the page where you added the Products module and click the settings icon. At the bottom of the settings page you should now see your settings control as the section with the header Product Settings.If you play around with the Show description setting you will notice that the product list shows the description regardless of what you set. This, of course, is because we have not yet implemented any functionality in the view control that takes this setting into account.
To correct this, open up ViewProducts.ascx.vb. Locate the ProductList_ItemDataBound event handler and make it look like this (the green section of code is added):
Protected Sub ProductList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewItemEventArgs) Handles ProductList.ItemDataBound If e.Item.ItemType = ListViewItemType.DataItem Then Dim prod As ProductsInfo prod = CType(CType(e.Item, ListViewDataItem).DataItem, ProductsInfo)
If Me.PortalSettings.UserMode = DotNetNuke.Entities.Portals.PortalSettings.Mode.Edit Then Dim prodContainer As Panel Dim editLink As New HtmlAnchor
prodContainer = CType(e.Item.FindControl("ProductContainer"), Panel) editLink.HRef = EditUrl("ItemId", prod.ItemId.ToString(), "EditItem") editLink.Style.Add("width", "16px") editLink.Style.Add("height", "16px") editLink.Style.Add("display", "inline-block") editLink.Style.Add("background-image", "url(" + DotNetNuke.Common.Globals.ApplicationPath + "/images/edit.gif)") editLink.Title = "Edit product" prodContainer.Controls.AddAt(0, editLink) End If
' Show description according to setting. Dim productDescription As Label Dim showDescription As Boolean
productDescription = CType(e.Item.FindControl("ProductDescription"), Label) If Not Boolean.TryParse(CType(Settings("ShowDescription"), String), showDescription) Then showDescription = True End If productDescription.Visible = showDescription
End IfEnd Sub
Compile the project again, the go and play with the setting again. The list should now display the description text only if the Show description checkbox is checked.
This time we are going to create ourselves an edit control. We start with the markup, so open EditProducts.ascx. Make it look like this:
<%@ Control language="vb" Inherits="SipidCode.Modules.Products.EditProducts" AutoEventWireup="false" Explicit="True" Codebehind="EditProducts.ascx.vb" %><%@ Register TagPrefix="dnn" TagName="TextEditor" Src="~/controls/TextEditor.ascx"%><table width="650" border="0" summary="Edit Product Item"> <tr> <td width="75">Name</td> <td><asp:TextBox ID="Name" runat="server"></asp:TextBox></td> </tr> <tr> <td>Description</td> <td><dnn:TextEditor ID="Description" runat="server" height="200" width="510" /></td> </tr></table><p> <asp:linkbutton cssclass="CommandButton" id="cmdUpdate" resourcekey="cmdUpdate" runat="server" borderstyle="none" text="Update" OnClick="cmdUpdate_Click"></asp:linkbutton> <asp:linkbutton cssclass="CommandButton" id="cmdCancel" resourcekey="cmdCancel" runat="server" borderstyle="none" text="Cancel" causesvalidation="False" OnClick="cmdCancel_Click"></asp:linkbutton> <asp:linkbutton cssclass="CommandButton" id="cmdDelete" resourcekey="cmdDelete" runat="server" borderstyle="none" text="Delete" causesvalidation="False" OnClick="cmdDelete_Click"></asp:linkbutton> </p>
Open EditProducts.ascx.vb. In the region “Private Members” the class variable ItemId is declared. This is where we store the ID of the product item to edit.Insert the following code into the empty Page_Load method (we deleted the method body in the previous part, remember?):
If Not (Request.QueryString("ItemId") Is Nothing) Then ItemId = Integer.Parse(Request.QueryString("ItemId"))End If
Now we have the product item ID in the ItemId variable, if supplied. If there was no ItemId parameter in the query string this means that we should create a new item instead of editing an existing one.
Add the following code:
If Not IsPostBack Then If Not Null.IsNull(ItemId) Then cmdDelete.Attributes.Add("onClick", "javascript:return confirm('" + Localization.GetString("DeleteItem") + "');")
Dim dc As New ProductsController Dim itm As ProductsInfo = dc.GetProduct(ModuleId, ItemId)
If Not itm Is Nothing Then Name.Text = itm.Name Description.Text = itm.Description Else Response.Redirect(Globals.NavigateURL(), True) End If Else cmdDelete.Visible = False End IfEnd If
This only has to be done the first time the page is loaded so we wrap it in a check for IsPostBack. In the case when an ItemId is supplied we add some client script to get a confirmation when deleting an item. Then we fetch the product item with the current module ID and item ID as keys and populate the controls. If there is no matching item, something must be wrong. In that case we redirect the heck out of there, back to the view page.When an ItemId is not supplied, this means we should create a new item. Just hide the Delete button.
Error handling is always a good thing, so wrap everything in Page_Load in a try-catch. The Page_Load method should now look something like this:
Protected Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Try If Not (Request.QueryString("ItemId") Is Nothing) Then ItemId = Integer.Parse(Request.QueryString("ItemId")) End If
If Not itm Is Nothing Then Name.Text = itm.Name Description.Text = itm.Description Else Response.Redirect(Globals.NavigateURL(), True) End If Else cmdDelete.Visible = False End If End If Catch ex As Exception Exceptions.ProcessModuleLoadException(Me, ex) End TryEnd Sub
The error handling code displays an error message to the user, which is enough for us right now.
Go through all three of the Click event handlers and make sure they are marked as Protected and not Private.
Insert the following as method body for cmdCancel_Click:
Next in line is the implementation of cmdUpdate_Click. Basically we check if we got an ItemId or not and thereby decide if we should update an existing item or create a new one.cmdUpdate_Click should look like this:
Protected Sub cmdUpdate_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdUpdate.Click Try Dim controller As New ProductsController Dim productItem As New ProductsInfo
productItem.ModuleId = ModuleId productItem.ItemId = ItemId productItem.Name = Name.Text productItem.Description = Description.RichText.Text
If Null.IsNull(ItemId) Then controller.AddProduct(productItem) Else controller.UpdateProduct(productItem) End If Response.Redirect(Globals.NavigateURL(), True) Catch ex As Exception Exceptions.ProcessModuleLoadException(Me, ex) End TryEnd Sub
One thing to note about the code above is how we pull the content from the Description TextEditor control. If you use Description.Text you will get the HTML encoded content (“<” is converted to “<” and so on) which is not what you want in this case. Use Description.RichText.Text and you will get the raw markup.
Protected Sub cmdDelete_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdDelete.Click Try If Not Null.IsNull(ItemId) Then Dim controller As New ProductsController controller.DeleteProduct(ModuleId, ItemId) End If Response.Redirect(Globals.NavigateURL(), True) Catch ex As Exception Exceptions.ProcessModuleLoadException(Me, ex) End TryEnd Sub
With that done, compile the project to make sure everything is ok. Then log in as host in your DotNetNuke website. Now we’re going to add our brand new edit control to the Products module. Go to Host > Module Definitions. Locate the Products row in the list and click the edit icon.
Scroll down to the section Module Controls and click Add Module Control.
Make the following settings and click Update:
Now we can at last create product items and edit them. Great! Try it out!Go to the page where you added the Products module. Make sure you are in edit mode. Now click the Add Content link. This time it actually happens something interesting. You should now see the edit control with a text field for product name and a text editor for description. Enter some text in them and click Update. The product you just added should now appear in the product list.Now click the edit (pencil) icon next to the product name. This will take you to the edit screen and let you edit the product item.Play around with your nice new controls until next time, when we look at the settings control.
A DotNetNuke module usually contains several user controls; view, edit and settings. We will start by creating our view control. As I mentioned in part 2 of this series, the view control is what is displayed to your visitors. The Products view control will be a list of product items belonging to the module instance.Open ViewProducts.ascx. Delete all content of the file and replace it with this:
<%@ Control language="vb" Inherits="SipidCode.Modules.Products.ViewProducts" AutoEventWireup="false" Explicit="True" Codebehind="ViewProducts.ascx.vb" %><asp:ListView ID="ProductList" runat="server"> <LayoutTemplate> <div class="sc-productlist-container"> <asp:PlaceHolder ID="itemPlaceholder" runat="server"></asp:PlaceHolder> </div> </LayoutTemplate> <ItemTemplate> <asp:Panel ID="ProductContainer" runat="server" CssClass="sc-product-container"> <asp:Label ID="ProductName" runat="server" CssClass="sc-product-name" Text='<%# Eval("Name") %>'></asp:Label> <asp:Label ID="ProductDescription" runat="server" CssClass="sc-product-description" Text='<%# Eval("Description") %>'></asp:Label> </asp:Panel> </ItemTemplate></asp:ListView>
Open ViewProducts.ascx.vb and replace the Page_Load method with the following:
Protected Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Try Dim controller As New ProductsController Dim products As List(Of ProductsInfo) ' Get the product list. products = controller.GetProducts(ModuleId) ' Bind the product list to the ListView control. ProductList.DataSource = products ProductList.DataBind() Catch exc As Exception ' Module failed to load. ProcessModuleLoadException(Me, exc) End TryEnd Sub
Delete the method lstContent_ItemDataBound.Add the method ProductList_ItemDataBound, looking like this:
Protected Sub ProductList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewItemEventArgs) Handles ProductList.ItemDataBound If e.Item.ItemType = ListViewItemType.DataItem Then Dim prod As ProductsInfo = DirectCast(DirectCast(e.Item, ListViewDataItem).DataItem, ProductsInfo)
prodContainer = CType(e.Item.FindControl("ProductContainer"), Panel) editLink.HRef = EditUrl("ItemId", prod.ItemId.ToString(), "EditItem") editLink.Style.Add("width", "16px") editLink.Style.Add("height", "16px") editLink.Style.Add("display", "inline-block") editLink.Style.Add("background-image", "url(" + DotNetNuke.Common.Globals.ApplicationPath + "/images/edit.gif)") editLink.Title = "Edit product" prodContainer.Controls.AddAt(0, editLink) End If End IfEnd Sub
In ProductList_ItemDataBound we check if the module is viewed in edit mode. In that case we add a link to the edit page (which we have not created a control for yet). The URL to the edit page is created by calling the EditUrl method (defined in PortalModuleBase).
By adding edit links to the items in the list we have created a way to edit existing items, but how about creating new items then?The ViewProducts class implements the interface IActionable, which defines the property ModuleActions. Look in the region “Optional Interfaces” for the implementation. The property returns a ModuleActionCollection object containing the actions that can be performed on the module. Each action is represented by an icon (or an entry in the module title flip-out menu) when the module is viewed in edit mode.You can return several actions for a module, but for now we are content with the “add content” action we got from the template.We will just make one small adjustment: locate the call to EditUrl() and give it the string "EditItem" as parameter. The property implementation should then look like this:
Public ReadOnly Property ModuleActions() As Entities.Modules.Actions.ModuleActionCollection Implements Entities.Modules.IActionable.ModuleActions Get Dim Actions As New Entities.Modules.Actions.ModuleActionCollection Actions.Add(GetNextActionID, Localization.GetString(Entities.Modules.Actions.ModuleActionType.AddContent, LocalResourceFile), Entities.Modules.Actions.ModuleActionType.AddContent, "", "", EditUrl("EditItem"), False, Security.SecurityAccessLevel.Edit, True, False) Return Actions End GetEnd Property
In part 3 we made a lot of changes in the data layer. Due to this there is a lot of broken code in the EditProducts and Settings controls. To sidestep this we just delete the broken code for now and create working code in coming posts.In EditProducts.ascx.vb, delete the contents of methods Page_Load, cmdUpdate_Click and cmdDelete_Click. Now we should be able to successfully compile the project again and are ready to try out the view in DotNetNuke.We are going to set up the module manually in the system so we can run it, so open your browser and point it to your local DotNetNuke installation. Log in with the Host account.
We begin with setting up the database, so select SQL from the Host menu. In Visual Studio, open the file 01.00.00.SqlDataProvider. Copy all text and paste it into DotNetNuke. Make sure Run as Script is checked and click Execute.
From the Host menu, select Module Definitions. Make sure Edit mode is selected.
Select Create New Module from the Module Definitions flip-out menu.
In the Create New Module screen, select/enter the following settings:
Then click Create Module.
You should now be able to add the module Products to a page. Go ahead and try it.There will of course not be any products in the list, since we have not defined any yet. As you can see there is an Add Content link in the module (corresponding to the module's action we looked at earlier in this post.), but there is no Edit control so it will only lead to a blank page.In the next post we will create that Edit control so we can put some products into the list.
In this part we are going to take a look at how the data layer is organized, set up the database objects and create the functionality needed to access the data.
Of course you are free to access your data any way you want. You might not even have the need to access the database. Anyway, the project template provides us with a data layer structure that I found quite okay to work with.Your module controls call methods in the ProductsController class (our BLL), which calls methods in the SqlDataProvider class (part of our DAL, which inherits from the abstract DataProvider class). SqlDataProvider then access the database by calling stored procedures (also part of our DAL). The stored procedure calls are made using the Microsoft Enterprise Library.So, where do we start? Let's build from the ground up and start with the SQL scripts that set up your database objects.Open the file 01.00.00.SqlDataProvider. As you can see the file is really just an SQL batch script with placeholders for database owner and object prefix ({databaseOwner} and {objectQualifier}). On module installation the placeholders are replaced with the current DotNetNuke installation's values for database owner and object prefix.I suggest we begin with a clean slate, so delete everything in the file. Insert the following instead:
To not loose focus we keep the data structure fairly simple... nothing too weird here. The ModuleID column is used to associate the product items to a specific module instance. The rest of the columns belong to the actual product item.Now that we have a table we need some stored procedures to access its data. But before we create the stored procedures it's a good idea to drop them if the already exist. So add the following:
And lastly, creation of the stored procedures:
To store the data from the database and use it the application code, we use a class as data holder. This class only members are public properties corresponding to the database table columns we are interested in. Open the ProductsInfo.vb file in the Components folder.As you see there is just an empty constructor and some public properties. Delete all class members and make the class look like this:
Public Class ProductsInfo
Private _ModuleId As Integer Private _ItemId As Integer Private _Name As String Private _Description As String
Public Property ModuleId() As Integer Get Return _ModuleId End Get Set(ByVal Value As Integer) _ModuleId = Value End Set End Property
Public Property ItemId() As Integer Get Return _ItemId End Get Set(ByVal Value As Integer) _ItemId = Value End Set End Property
Public Property Name() As String Get Return _Name End Get Set(ByVal Value As String) _Name = Value End Set End Property
Public Property Description() As String Get Return _Description End Get Set(ByVal Value As String) _Description = Value End Set End Property
End Class
Personally, I would have used the shorthand auto properties introduced in VB10. But since I'd like this tutorial to work for VB9 users without too much hassle we stay old school for now :)
Public MustOverride Function GetProducts(ByVal ModuleId As Integer) As IDataReaderPublic MustOverride Function GetProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer) As IDataReaderPublic MustOverride Sub AddProduct(ByVal ModuleId As Integer, ByVal Name As String, ByVal Description As String)Public MustOverride Sub UpdateProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer, ByVal Name As String, ByVal Description As String)Public MustOverride Sub DeleteProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer)
Now open SqlDataProvider.vb so we can make matching changes there. Expand the "Public Methods" region. We need to change these methods so they match the abstract methods in DataProvider. Besides changing the method signatures we also have to adjust the implementations. The string parameter in the calls to GetFullyQualifiedName() should match the name of the stored procedure we want to call. We also have to change the parameters sent to the stored procedures. The methods in the "Public Methods" region should look like this:
Public Overrides Function GetProducts(ByVal ModuleId As Integer) As IDataReader Return CType(SqlHelper.ExecuteReader(ConnectionString, GetFullyQualifiedName("GetProducts"), ModuleId), IDataReader)End Function
Public Overrides Function GetProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer) As IDataReader Return CType(SqlHelper.ExecuteReader(ConnectionString, GetFullyQualifiedName("GetProduct"), ModuleId, ItemId), IDataReader)End Function
Public Overrides Sub AddProduct(ByVal ModuleId As Integer, ByVal Name As String, ByVal Description As String) SqlHelper.ExecuteNonQuery(ConnectionString, GetFullyQualifiedName("AddProduct"), ModuleId, Name, Description)End Sub
Public Overrides Sub UpdateProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer, ByVal Name As String, ByVal Description As String) SqlHelper.ExecuteNonQuery(ConnectionString, GetFullyQualifiedName("UpdateProduct"), ModuleId, ItemId, Name, Description)End Sub
Public Overrides Sub DeleteProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer) SqlHelper.ExecuteNonQuery(ConnectionString, GetFullyQualifiedName("DeleteProduct"), ModuleId, ItemId)End Sub
Then we need to make matching changes in the ProductsController class. As I said before ProductsController calls the methods in SqlDataProvider to get the data and returns it to the controls. In our simple implementation there is not very much interesting going on in ProductsController. In a more advanced scenario there could be business specific logic in there, thereby earning ProductsController the name "business logic layer".We want the methods in the "Public methods" region in ProductsController.vb to look like this:
Public Function GetProducts(ByVal ModuleId As Integer) As List(Of ProductsInfo) Return CBO.FillCollection(Of ProductsInfo)(DataProvider.Instance().GetProducts(ModuleId))End Function
Public Function GetProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer) As ProductsInfo Return CType(CBO.FillObject(DataProvider.Instance().GetProduct(ModuleId, ItemId), GetType(ProductsInfo)), ProductsInfo)End Function
Public Sub AddProduct(ByVal prod As ProductsInfo) If prod.Name.Trim <> "" Then DataProvider.Instance().AddProduct(prod.ModuleId, prod.Name, prod.Description) End IfEnd Sub
Public Sub UpdateProduct(ByVal prod As ProductsInfo) If prod.Name.Trim <> "" Then DataProvider.Instance().UpdateProduct(prod.ModuleId, prod.ItemId, prod.Name, prod.Description) End IfEnd Sub
Public Sub DeleteProduct(ByVal ModuleId As Integer, ByVal ItemId As Integer) DataProvider.Instance().DeleteProduct(ModuleId, ItemId)End Sub
Remove the inheritance from interfaces ISearchable and IPortable, changing the class declaration from
Public Class ProductsController Implements Entities.Modules.ISearchable, Entities.Modules.IPortable
to
Delete the methods GetSearchItems, ExportModule and ImportModule. We will revisit these in later posts.
With that done we now have the functionality we need to access the database and get/set/update our data. Next time we will put it to use in the control for the module's edit view.
During the next parts of this tutorial we will create a module displaying a simple listing of products. We will call it... Products! In this part we will set up the project and look at what is in the templates.In Visual Studio, create a new project. In the New Project dialog, under Installed Templates, expand Other Languages > Visual Basic and select the Web templates node. Then select "DotNetNuke Compiled Module" from the templates list. Enter "Products" as the name of your module. Then point the location to the "DesktopModules" folder of your DNN installation. Make sure "Create directory for solution" is not checked. This is important. Click OK.
Now when we have the project started, there are a couple of things you should know.First: the project is kind of a cross-breed between a library project and a web project. This is so we can work with the user controls (the ascx files and their associated code-behind and designer files) pretty much like in a standard web project. If you just start an ordinary library project, add the files needed for a user control and try to work with them in the Visual Studio environment, you are neck deep in additional work.Second: since the project is of the mixed type described above, it will try to run if you start debugging it. This will not work. I will cover dubugging of modules in some other post.
Third: If a web.config file for some reason is created in your module project; delete it. Since your project folder is a subdirectory in the DotNetNuke web application that hosts you module, a web.config file there will most probably lead to problems later on when you are running your web site.With those things straightened out, go into the properties settings of your project by double-clicking the My Project node in solution explorer. On the Application tab to the left, set the Assembly name to "SipidCode.Modules.Products". Make sure the Root namespace field is empty.
Nowadays DNN is compiled against version 3.5 of the .NET framework. This means we will have to set at least that as target version for our project as well.Select the Compile tab. Click the Advanced Compile Options... button at the bottom of the screen. At the bottom of the Advanced Compiler Settings dialog, select ".NET Framework 3.5" as Target framework.
Click OK to close the dialog.You will now be informed that the project has to be closed and reopened, and asked if you want to continue. Click Yes.
Open up the project properties again. Then select the References tab to the left. As you can see the topmost reference (DotNetNuke.Library) cannot be found. Select it in the list and click the Remove button below the list to remove it.
Now we need a reference to the DotNetNuke assembly for things to work correctly. Click the Add... button below the list. The Add Reference dialog will open. Select the Browse tab. The contents of the Products project folder will be displayed. Go up two levels in the folder structure and from there go into the bin folder. Select the file "DotNetNuke.dll" and click OK.
You can now close the Products tab.From the Edit menu, select Find and Replace > Replace in Files. Enter "YourCompany" in the Find what field, and "SipidCode" in Replace with. Expand Find options and make sure Look at these file types is set to "*.*". Click Replace All.
You can now compile the project to make sure it works as it should.Let's take a peek at what the project template has given us.
User controls: ViewProducts, EditProducts and Settings. These correspond to different modes of your module. Their names give us a hint of what they are for. The View control is used when the module is displayed to the visitors of your website. The Edit control is used when an administrator goes into edit mode. This is usually where the content of the module is handled. Then we have the Settings control. That one is displayed in bottom of the settings screen of the control. Used to handle settings special to your module.
Database setup and uninstall scripts: the files with the extension SqlDataProvider. As you see there is one with the version number "01.00.00". That one is run on installation of the module, setting up tables, stored procedures and so on. It's really just a SQL batch script. There can be multiple installation scripts, each corresponding to a version of your module. We will go into the details of that in some future post.There is also the Uninstall script. That one is run on (yeah! you guessed right!) uninstallation of the module, removing the database objects created by the installation script.
Extension installation manifest: Products.dnn. This is the instruction file for the installation. It tells DotNetNuke stuff like which scripts to run, which files to copy where and so on. We will look closer at that one later on. We've got a lot of other stuff to cover before we get to the installation part.Components folder. This is where the files involved in the data access layer are located.The Documentation folder contains only some info concerning the project template. It is pretty obsolete and is of no interest to us.Next time we are going to take a look into the data layer. Then we will write some code, at last.
This is a re-work of the C# module tutorial series to suit VB developers. It uses newer versions of Visual Studio and DNN, but otherwise it is pretty much the same as its C# sibling.Credits for the VB code goes to my friend Kai Joussen who made this possible.But now, to the Visual Basic module action...
So, you want to create your own DNN modules, but don't know how? Started programming with the Visual Studio Starter Kit but got stuck because it seems so... complicated? Fear not. There is no über advanced programming neccessary. Once you get a grip on how it all fits together you will wonder what all the fuss was about.In this tutorial series I will assume that you have at least basic skills in ASP.NET development. I will also frequently skip over info and procedures you easily can google yourself.So, since this is meant to be a tutorial, let's start from the beginning and look at what you need to get some serious module dev action.Of course there are several setups and practices regarding the development of DotNetNuke modules, but this tutorial will use the following:
In this tutorial series I use version 5.4.2 of both DotNetNuke web application and Visual Studio Starter Kit.If you still use Visual Studio 2008, don't get too worried. The differences aren't that big and I tried to use procedures that should work fairly well in VS 2008 too.
Make sure you have IIS installed, configured and running on your machine. This tutorial won't cover setting that up since there is tons if guides for that out there.Next, download the new install version of DotNetNuke. You'll find the latest version under "Recommended Download" in the downloads section of DNN's project page on CodePlex. To simplify things i suggest you do not use the source code version since this will make things slower and possibly confuse you, since all the source files for DotNetNuke will be in your installation.I have DotNetNuke installed in the web root of my local web server, but installation in a virtual directory works fine too.To install, simply unzip the contents of the installation package from CodePlex into your web root or virtual folder. Now set up an empty database in SQL Server or SQL Server Express. Point your web browser to your DotNetNuke installation, e.g. "http://localhost" or "http://localhost/MyDnnInstallation". This will start the installation wizard, which I won't go through in detail either. Read the info on the screens carefully when you go through it and you should be fine.
The Visual Studio Starter Kit will help you with templates for our new module. It can also be downloaded from CodePlex under "Other Available Downloads". The installation is very simple so I won't go into that at all. The starter kit contains templates for both C# and Visual Basic and installs them for both Visual Studio 2008 and 2010 (probably also for 2005, but I have not verified this myself).
Now we have the tools ready, so the next part will be about starting our first module project and take a look at what is in the templates.
When you start distributing your modules to other people you definitely want to version them. Mostly to keep your customers/clients/co-workers from going nuts when it's update time, and avoid the avalanche of support cases you will get if you don’t use versioned updates…This is done by making sure the extension installation manifest contains the necessary info about what DotNetNuke should do when installing the package on sites where various versions of the module is already installed.You can work half way to installation magic by knowing how to fiddle around with the manifest, and make quite complex installations. That is outside the scope of this post, though. I will talk about version 5 of the extension installation manifest from a module installation point of view. It will be kind of a basic case of module versioning, but hopefully it will get you started.I have not found a really good reference on the version 5 extension installation manifest freely avaliable online, but Charles Nurse has written some pieces on the subject, well worth checking out:
The New Extension Installer Manifest – Part 1, IntroductionThe New Extension Installer Manifest – Part 2, Component InstallersThe New Extension Installer Manifest – Part 3, The Cleanup ComponentThe New Extension Installer Manifest – Part 4, The Assembly ComponentThe New Extension Installer Manifest – Part 5, The File ComponentThe New Extension Installer Manifest – Part 6, The Script Component
It has been in my mind a long time to put together some kind of more or less complete manifest reference, but have not gotten around to it yet.I don’t in any way claim to be an expert on the subject, but I have found a practice that usually gets me around. Your feedback is most welcome.Let’s get started.
As a starting point I'll use the manifest we got from my module development tutorial series. You can get the manifest here.In this update scenario, imagine that we need to make a change in one of the stored procedures and that we want to add an image which will be used in the view control.
Add the file 01.00.01.SqlDataProvider to your module project. This file should contain the database scripts needed to change whatever needs to be changed from version 01.00.00. In our case it would be a script first removing the stored procedure, and then creating the new version of it.When making changes to tables, it is very important not to drop the table and recreate it. If you do, you will very soon have your customers on the phone wondering why all their precious data went bye-bye when they installed the new version of the module.Instead, alter the table. This often takes more work to accomplish, but it is the only option you have, really. Hopefully you think I'm silly even mentioning this kind of obvious thing, but I've seen it happen, so just in case...
Open up the manifest file and look at row 2:
Change the version to "1.0.1".
In the scripts element, add a script element for the 01.00.01.SqlDataProvider file. The scripts section should then look like:
When uninstalling the module, the script specified in the script tag with type set to UnInstall is executed.If you create a manifest with DotNetNuke as described here, a version is specified for the UnInstall script. However, as far as I know, that version tag has no function. It works whether or not the version tag is there, or whatever version is set.
In the files element, there should be a file element for each file to be copied into the module folder.The simplest case is when the file is in the root of the package zip file. Then you use something like:
If the file resides in a subfolder you use the path element inside the file element. The subfolder will be created if it does not exist. So, if the name of the subfolder is images, you use:
If the source file is not located in the same place as the target file, or if it has another name, you use the sourceFileName tag. For example, if the image file is located in the subfolder sourceImages in the installation package and has the name MySourceImage.png:
Usually it doesn't get more complicated than this. Happy versioning!
Tags: dotnetnuke, module, manifest
DotNetNuke | Modules
There have been some questions about how to debug when developing DotNetNuke modules. If you have followed my module development tutorial series you know you can’t just start a debugging session for the module project.To debug your module code you have to attach the debugger to the process actually running the code. Since the development website is running on your local webserver, that process is the IIS worker process.
Before you start debugging, make sure the module is installed on the development website and that the control you want to debug is added to a page on the website (so the code we want to debug is run when we enter that page).Open up the development website in your browser to make sure the webserver worker process is running. Open up your module project in Visual Studio.In the Debug menu, select Attach to process…In the Attach to process dialog, select aspnet_wp.exe from the Available processes list and click the Attach button.The screenshot below is from an XP machine. If you use Vista or Win 7, the name of the worker process is w3wp.exe.
Now execution will stop at any breakpoints in your code, just as usual.
Try it out by setting a breakpoint in the Page_Load method in ViewProducts.ascx.cs and surf to the page where you added the module.
When you are done debugging, go into the Debug menu again and select Stop Debugging.
Tags: dotnetnuke, module, debugging
To Visual Basic version
Tags: dotnetnuke, module, tutorial
I use BlogEngine.NET Log in
Johan Seppäläinen lives in Uppsala, Sweden. He spends most of his days working as a systems architect/developer, specialized in solutions built on Microsoft platforms.Occasionally there is time for some recreational coding, when he pursues optimal solutions and code zen, mainly in C#. When he is not writing in this blog, that is.