我今天刚开发了一个DNN插件,也是我第一次开发DNN插件,我把开发的过程描述下来,对于精通DNN的希望多多指点,对于希望涉足DNN的起到抛砖引玉的作用.
本示例以一张基本的表Department的CRUD来说明用C#制作DNN4.3插件的全过程
1:首先创建Department表
(ModuleID,DepartmentID,DepartmentName,CreatedByUser,CreatedDate),
DepartmentID是主键
通过ModuleID建立Department表和Modules表的关系(ModuleID字段是实现模块插件的关键)
创建对应的CRUD存储过程
2:创建部门模块的内核部分
2.1: 创建VS2005的类库项目
2.2 添加DotNetNuke.dll 引用
2.3 添加DepartmentInfo实体类
using System;
using System.Configuration;
using System.Data;

namespace ISS.DNN.Modules.Department


{

/**//**//**//// -----------------------------------------------------------------------------
///<summary>
/// The Info class for the Department
/// </summary>
/// <remarks>
/// </remarks>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public class DepartmentInfo

{


Private MembersPrivate Members#region Private Members

private int _ModuleId;
private int _DepartmentID;
private string _DepartmentName;
private int _CreatedByUser;
private DateTime _CreatedDate;
private string _CreatedByUserName;

#endregion


ConstructorsConstructors#region Constructors

// initialization
public DepartmentInfo()

{
}

#endregion


Public MethodsPublic Methods#region Public Methods


/**//**//**//// <summary>
/// Gets and sets the Module Id
/// </summary>
public int ModuleId

{
get

{
return _ModuleId;
}
set

{
_ModuleId = value;
}
}


/**//**//**//// <summary>
/// Gets and sets the Item Id
/// </summary>
public int DepartmentId

{
get

{
return _DepartmentID;
}
set

{
_DepartmentID = value;
}
}


/**//**//**//// <summary>
/// gets and sets the DepartmentName
/// </summary>
public string DepartmentName

{
get

{
return _DepartmentName;
}
set

{
_DepartmentName = value;
}
}


/**//**//**//// <summary>
/// Gets and sets the User Id who Created/Updated the DepartmentName
/// </summary>
public int CreatedByUser

{
get

{
return _CreatedByUser;
}
set

{
_CreatedByUser = value;
}
}


/**//**//**//// <summary>
/// Gets and sets the User Id who Created/Updated the DepartmentName
/// </summary>
public string CreatedByUserName

{
get

{
return _CreatedByUserName;
}
set

{
_CreatedByUserName = value;
}
}


/**//**//**//// <summary>
/// Gets and sets the Date when Created/Updated
/// </summary>
public DateTime CreatedDate

{
get

{
return _CreatedDate;
}
set

{
_CreatedDate = value;
}
}


#endregion
}
}

2.4 创建DataProvider抽象类并添加一下代码
using System;
using DotNetNuke;
using System.Data;

using DotNetNuke.Framework;

namespace ISS.DNN.Modules.Department


{

/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// An abstract class that provides the DAL contract
/// </summary>
/// <remarks>
/// </remarks>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public abstract class DataProvider

{


Shared/Static MethodsShared/Static Methods#region Shared/Static Methods

// singleton reference to the instantiated object
static DataProvider objProvider = null;

// constructor
static DataProvider()

{
CreateProvider();
}

// dynamically create provider
//一定要注意DataProvider的配置节department
private static void CreateProvider()

{
objProvider = (DataProvider)Reflection.CreateObject("department");
}

// return the provider
public static DataProvider Instance()

{
return objProvider;
}

#endregion


Abstract methodsAbstract methods#region Abstract methods

public abstract void AddDepartment(int ModuleId, string DepartmentName, int UserId);
public abstract IDataReader GetDepartment(int ModuleId, int DepartmentID);
public abstract IDataReader GetDepartments(int ModuleId);
public abstract void UpdateDepartment(int ModuleId, int DepartmentID, string DepartmentName, int UserId);
public abstract void DeleteDepartment(int ModuleId, int DepartmentID);

#endregion
}
}

2.5 创建业务控制类DepartmentController
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Xml;
using System.Web;
using DotNetNuke;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Services.Search;

namespace ISS.DNN.Modules.Department


{

/**//**//**//// -----------------------------------------------------------------------------
///<summary>
/// The Controller class for the Department
/// </summary>
/// <remarks>
/// </remarks>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public class DepartmentController : ISearchable, IPortable

{


ConstructorsConstructors#region Constructors

public DepartmentController()

{
}

#endregion


Public MethodsPublic Methods#region Public Methods


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// adds an object to the database
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="objDepartment">The DepartmentInfo object</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public void AddDepartment(DepartmentInfo objDepartment)

{
if (objDepartment.DepartmentName.Trim() != "")

{
DataProvider.Instance().AddDepartment(objDepartment.ModuleId, objDepartment.DepartmentName, objDepartment.CreatedByUser);
}
}


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// deletes an object from the database
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="ModuleId">The Id of the module</param>
/// <param name="DepartmentId">The Id of the item</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public void DeleteDepartment(int ModuleId, int DepartmentID)

{
DataProvider.Instance().DeleteDepartment(ModuleId,DepartmentID);
}


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// gets an object from the database
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="moduleId">The Id of the module</param>
/// <param name="DepartmentId">The Id of the item</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public DepartmentInfo GetDepartment(int ModuleId, int DepartmentID)

{
return CBO.FillObject (DataProvider.Instance().GetDepartment(ModuleId, DepartmentID),typeof(DepartmentInfo)) as DepartmentInfo;
}


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// gets an object from the database
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="moduleId">The Id of the module</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public IList GetDepartments(int ModuleId)

{
return CBO.FillCollection(DataProvider.Instance().GetDepartments(ModuleId),typeof(DepartmentInfo));
}


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// saves an object to the database
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="objDepartment">The DepartmentInfo object</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public void UpdateDepartment(DepartmentInfo objDepartment)

{
if (objDepartment.DepartmentName.Trim() != "")

{
DataProvider.Instance().UpdateDepartment(objDepartment.ModuleId, objDepartment.DepartmentId, objDepartment.DepartmentName, objDepartment.CreatedByUser);
}
}

#endregion


Optional InterfacesOptional Interfaces#region Optional Interfaces


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// GetSearchItems implements the ISearchable Interface
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="ModInfo">The ModuleInfo for the module to be Indexed</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public SearchItemInfoCollection GetSearchItems(ModuleInfo ModInfo)

{
SearchItemInfoCollection SearchItemCollection = new SearchItemInfoCollection();
IList colDepartments = GetDepartments(ModInfo.ModuleID);

foreach (DepartmentInfo objDepartment in colDepartments)

{
if(objDepartment != null)

{
SearchItemInfo SearchItem = new SearchItemInfo(ModInfo.ModuleTitle, objDepartment.DepartmentName, objDepartment.CreatedByUser, objDepartment.CreatedDate, ModInfo.ModuleID, objDepartment.DepartmentId.ToString(), objDepartment.DepartmentName, "DepartmentId=" + objDepartment.DepartmentId.ToString());
SearchItemCollection.Add(SearchItem);
}
}

return SearchItemCollection;
}



/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// ExportModule implements the IPortable ExportModule Interface
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="ModuleID">The Id of the module to be exported</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public string ExportModule(int ModuleID)

{
string strXML = "";
IList colDepartments = GetDepartments(ModuleID);

if (colDepartments.Count != 0)

{
strXML += "<Departments>";
foreach (DepartmentInfo objDepartment in colDepartments)

{
strXML += "<Department>";
strXML += "<DepartmentName>" + XmlUtils.XMLEncode(objDepartment.DepartmentName) + "</DepartmentName>";
strXML += "</Department>";
}
strXML += "</Departments>";
}

return strXML;
}


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// ImportModule implements the IPortable ImportModule Interface
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="ModuleID">The Id of the module to be imported</param>
/// <param name="DepartmentName">The DepartmentName to be imported</param>
/// <param name="Version">The version of the module to be imported</param>
/// <param name="UserId">The Id of the user performing the import</param>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public void ImportModule(int ModuleID, string DepartmentName, string Version, int UserId)

{
XmlNode xmlDepartments = Globals.GetContent(DepartmentName, "Departments");

foreach (XmlNode xmlDepartment in xmlDepartments.SelectNodes("Department"))

{
DepartmentInfo objDepartment = new DepartmentInfo();

objDepartment.ModuleId = ModuleID;
objDepartment.DepartmentName = xmlDepartment.SelectSingleNode("DepartmentName").InnerText;
objDepartment.CreatedByUser = UserId;
AddDepartment(objDepartment);
}

}

#endregion

}
}

2.6 创建UI 部门浏览控件ViewDepartment,这个类一定要继承PortalModuleBase,该类是DNN模块的基类也是模块插件机制的关键,提供了很多默认的实现,该类又实现了一个接口IActionable,这个接口指明该控件又哪些操作行为,本类指明了可以对Department进行添加操作
Actions.Add(this.GetNextActionID(), Localization.GetString(ModuleActionType.AddContent, this.LocalResourceFile), ModuleActionType.AddContent, "", "", this.EditUrl(), false, SecurityAccessLevel.Edit, true, false);
namespace ISS.DNN.Modules.Department


{
using System;
using System.Data;
using System.Collections;
using System.Drawing;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Reflection;
using DotNetNuke;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Actions;
using DotNetNuke.Security;
using DotNetNuke.Services.Exceptions;
using DotNetNuke.Services.Localization;


/**//**//**//// <summary>
/// ViewDepartment 的摘要说明。
/// </summary>
public class ViewDepartment : PortalModuleBase, IActionable

{
protected System.Web.UI.WebControls.DataList lstDepartment;


Web 窗体设计器生成的代码Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
override protected void OnInit(EventArgs e)

{
//
// CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
//
InitializeComponent();
base.OnInit(e);
}

/**//**//**//// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器
/// 修改此方法的内容。
/// </summary>
private void InitializeComponent()

{
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion


Public MethodsPublic Methods#region Public Methods

public bool DisplayAudit()

{
bool retValue = false;

if ((string)Settings["auditinfo"] == "Y")

{
retValue = true;
}

return retValue;
}

#endregion


Event HandlersEvent Handlers#region Event Handlers


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// Page_Load runs when the control is loaded
/// </summary>
/// <remarks>
/// </remarks>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
protected void Page_Load(System.Object sender, System.EventArgs e)

{
try

{
DepartmentController objDepartments = new DepartmentController();
IList colDepartments;

//get the content from the Department table
colDepartments = objDepartments.GetDepartments(ModuleId);

if (colDepartments.Count == 0)

{
//add the content to the Department table
DepartmentInfo objDepartment = new DepartmentInfo();
objDepartment.ModuleId = ModuleId;
objDepartment.DepartmentName = Localization.GetString("DefaultContent", LocalResourceFile);
objDepartment.CreatedByUser = this.UserId;
objDepartments.AddDepartment(objDepartment);

//get the content from the Department table
colDepartments = objDepartments.GetDepartments(ModuleId);
}

//bind the content to the repeater
lstDepartment.DataSource = colDepartments;
lstDepartment.DataBind();
}
catch (Exception exc) //Module failed to load

{
Exceptions.ProcessModuleLoadException(this, exc);
}

}

#endregion


Optional InterfacesOptional Interfaces#region Optional Interfaces


/**//**//**//// -----------------------------------------------------------------------------
/// <summary>
/// Registers the module actions required for interfacing with the portal framework
/// </summary>
/// <value></value>
/// <returns></returns>
/// <remarks></remarks>
/// <history>
/// </history>
/// -----------------------------------------------------------------------------
public ModuleActionCollection ModuleActions

{
get

{
ModuleActionCollection Actions = new ModuleActionCollection();
Actions.Add(this.GetNextActionID(), Localization.GetString(ModuleActionType.AddContent, this.LocalResourceFile), ModuleActionType.AddContent, "", "", this.EditUrl(), false, SecurityAccessLevel.Edit, true, false);
return Actions;
}
}

#endregion
}
}

2.7 创建UI 部门编辑控件EditDepartment(实现CUD),该类也要继承PortalModuleBase


1
namespace ISS.DNN.Modules.Department
2

{
3
using System;
4
using System.Data;
5
using System.Drawing;
6
using System.Web;
7
using System.Web.UI.WebControls;
8
using System.Web.UI.HtmlControls;
9
using DotNetNuke;
10
using DotNetNuke.Common;
11
using DotNetNuke.Common.Utilities;
12
using DotNetNuke.Entities.Modules;
13
using DotNetNuke.Services.Exceptions;
14
using DotNetNuke.Services.Localization;
15
16
/**//**//**//// <summary>
17
/// EditDepartment 的摘要说明。
18
/// </summary>
19
public class EditDepartment : PortalModuleBase
20
{
21
protected System.Web.UI.WebControls.RequiredFieldValidator valDepartmentName;
22
protected System.Web.UI.WebControls.LinkButton cmdUpdate;
23
protected System.Web.UI.WebControls.LinkButton cmdCancel;
24
protected System.Web.UI.WebControls.LinkButton cmdDelete;
25
protected DotNetNuke.UI.UserControls.ModuleAuditControl ctlAudit;
26
protected DotNetNuke.UI.UserControls.TextEditor txtDepartmentName;
27
28
Web 窗体设计器生成的代码Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
29
override protected void OnInit(EventArgs e)
30
{
31
//
32
// CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
33
//
34
InitializeComponent();
35
base.OnInit(e);
36
}
37
38
/**//**//**//// <summary>
39
/// 设计器支持所需的方法 - 不要使用代码编辑器
40
/// 修改此方法的内容。
41
/// </summary>
42
private void InitializeComponent()
43
{
44
this.cmdUpdate.Click += new System.EventHandler(this.cmdUpdate_Click);
45
this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
46
this.cmdDelete.Click += new System.EventHandler(this.cmdDelete_Click);
47
this.Load += new System.EventHandler(this.Page_Load);
48
49
}
50
#endregion
51
52
Private MembersPrivate Members#region Private Members
53
54
private int DepartmentID = Null.NullInteger;
55
56
#endregion
57
58
Event HandlersEvent Handlers#region Event Handlers
59
60
/**//**//**//// -----------------------------------------------------------------------------
61
/// <summary>
62
/// Page_Load runs when the control is loaded
63
/// </summary>
64
/// <remarks>
65
/// </remarks>
66
/// <history>
67
/// </history>
68
/// -----------------------------------------------------------------------------
69
protected void Page_Load(System.Object sender, System.EventArgs e)
70
{
71
try
72
{
73
//Determine DepartmentId of Department to Update
74
if(this.Request.QueryString["DepartmentId"] !=null)
75
{
76
DepartmentID = Int32.Parse(this.Request.QueryString["DepartmentId"]);
77
}
78
79
//If this is the first visit to the page, bind the role data to the datalist
80
if (Page.IsPostBack == false)
81
{
82
cmdDelete.Attributes.Add("onClick", "javascript:return confirm('" + Localization.GetString("DeleteItem") + "');");
83
84
if(DepartmentID != -1)
85
{
86
//get DepartmentName
87
DepartmentController objDepartments = new DepartmentController();
88
DepartmentInfo objDepartment = objDepartments.GetDepartment(ModuleId,DepartmentID);
89
if (objDepartment != null)
90
{
91
txtDepartmentName.Text = objDepartment.DepartmentName;
92
ctlAudit.CreatedByUser = objDepartment.CreatedByUserName;
93
ctlAudit.CreatedDate = objDepartment.CreatedDate.ToString();
94
}
95
else
96
{
97
Response.Redirect(Globals.NavigateURL(), true);
98
}
99
}
100
else
101
{
102
cmdDelete.Visible = false;
103
ctlAudit.Visible = false;
104
}
105
}
106
}
107
catch (Exception exc) //Module failed to load
108
{
109
Exceptions.ProcessModuleLoadException(this, exc);
110
}
111
}
112
113
/**//**//**//// -----------------------------------------------------------------------------
114
/// <summary>
115
/// cmdCancel_Click runs when the cancel button is clicked
116
/// </summary>
117
/// <remarks>
118
/// </remarks>
119
/// <history>
120
/// </history>
121
/// -----------------------------------------------------------------------------
122
protected void cmdCancel_Click(System.Object sender, System.EventArgs e)
123
{
124
try
125
{
126
this.Response.Redirect(Globals.NavigateURL(this.TabId), true);
127
}
128
catch (Exception exc) //Module failed to load
129
{
130
Exceptions.ProcessModuleLoadException(this, exc);
131
}
132
}
133
134
/**//**//**//// -----------------------------------------------------------------------------
135
/// <summary>
136
/// cmdDelete_Click runs when the delete button is clicked
137
/// </summary>
138
/// <remarks>
139
/// </remarks>
140
/// <history>
141
/// </history>
142
/// -----------------------------------------------------------------------------
143
protected void cmdDelete_Click(System.Object sender, System.EventArgs e)
144
{
145
try
146
{
147
//Only attempt to delete the item if it exists already
148
if (!Null.IsNull(DepartmentID))
149
{
150
DepartmentController objDepartments = new DepartmentController();
151
objDepartments.DeleteDepartment(ModuleId,DepartmentID);
152
}
153
154
this.Response.Redirect(Globals.NavigateURL(this.TabId), true);
155
}
156
catch (Exception exc) //Module failed to load
157
{
158
Exceptions.ProcessModuleLoadException(this, exc);
159
}
160
}
161
162
/**//**//**//// -----------------------------------------------------------------------------
163
/// <summary>
164
/// cmdUpdate_Click runs when the update button is clicked
165
/// </summary>
166
/// <remarks>
167
/// </remarks>
168
/// <history>
169
/// </history>
170
/// -----------------------------------------------------------------------------
171
protected void cmdUpdate_Click(System.Object sender, System.EventArgs e)
172
{
173
try
174
{
175
DepartmentController objDepartments = new DepartmentController();
176
DepartmentInfo objDepartment = new DepartmentInfo();
177
178
objDepartment.ModuleId = ModuleId;
179
objDepartment.DepartmentId = DepartmentID;
180
objDepartment.DepartmentName = txtDepartmentName.Text;
181
objDepartment.CreatedByUser = this.UserId;
182
183
//Update the DepartmentName within the Department table
184
if(Null.IsNull(DepartmentID))
185
{
186
objDepartments.AddDepartment(objDepartment);
187
}
188
else
189
{
190
objDepartments.UpdateDepartment(objDepartment);
191
}
192
193
//Redirect back to the portal home page
194
this.Response.Redirect(Globals.NavigateURL(this.TabId), true);
195
}
196
catch (Exception exc) //Module failed to load
197
{
198
Exceptions.ProcessModuleLoadException(this, exc);
199
}
200
}
201
202
#endregion
203
204
}
205
}
206
2.8 创建模块设置控件Settings,该类一定要继承ModuleSettingsBase
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetNuke;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Services.Localization;
using DotNetNuke.Services.Exceptions;

namespace ISS.DNN.Modules.Department


{
public abstract class Settings : DotNetNuke.Entities.Modules.ModuleSettingsBase

{

Web Form Designer generated codeWeb Form Designer generated code#region Web Form Designer generated code
override protected void OnInit(EventArgs e)

{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/**//**//**//// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()

{

}
#endregion

public override void LoadSettings()

{
try

{
if (!Page.IsPostBack)

{
string setting1 = ((string)TabModuleSettings["settingname1"]);
string setting2 = ((string)Settings["settingname2"]);
}
}
catch (Exception exc)

{
Exceptions.ProcessModuleLoadException(this, exc);
}
}

public override void UpdateSettings()

{
try

{
DotNetNuke.Entities.Modules.ModuleController objModules = new DotNetNuke.Entities.Modules.ModuleController();
objModules.UpdateTabModuleSetting(TabModuleId, "settingname1", "value");
objModules.UpdateModuleSetting(ModuleId, "settingname2", "value");
Response.Redirect(Globals.NavigateURL(), true);
}
catch (Exception exc)

{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
}
}

OK,现在已经完成了系统的关键开发了,编译项目生成ISS.DNN.Modules.Department.dll,下一篇介绍SqlDataProvider的开发,UI界面的开发,以及安装包的制作!