國外的開源CMS一般都是基於模塊設計的 ,好處是可以隨意定製自己的頁面和模塊,這樣在以後的應用中就能夠靈活的滿足變化的功能需求. 一個模塊齊全的CMS如DNN , Rainbow就可以快速搭建符合需求的系統.
下面就來介紹如何爲Cuyahoga這個著名的開源網站框架加入具有後臺管理的公告模塊.可以參考這篇如何在Cuyahoga中新增一個簡單的功能模塊瞭解基礎的步驟.
爲Cuyahoga開發自定義模塊時,你可以選擇任何數據訪問策略.然而Cuyahoga本身是使用NHibernate作爲數據持久層,可以做到支持多數據庫. 採用Castle.Windsor進行依賴注入,降低模塊之間的耦合. 我們的數據訪問層也將用NHibernate實現.
最終項目的目錄結構如下:
主要步驟如下
1 .創建一個Sql文件(Install.sql)用來安裝數據表及添加模塊的相關信息 , 該sql文件會在安裝模塊時,由Cuyahoga自動執行.也可以手工執行進行安裝.
install.sql
IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[cm_Announcements]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
CREATE TABLE [cm_Announcements] (
[AnnouncementsID] [int] IDENTITY (1,1)NOT NULL ,
[sectionid] [int] NOT NULL ,
[createdby] [nvarchar] (100) NULL ,//公告作者
[Title] [nvarchar] (150) NULL ,//公告標題
inserttimestamp datetime DEFAULT current_timestamp NOT NULL,
updatetimestamp datetime DEFAULT current_timestamp NOT NULL,
CONSTRAINT [PK_cm_Announcements] PRIMARY KEY NONCLUSTERED
(
[AnnouncementsID]
),
CONSTRAINT [FK_cm_Announcements_cm_Modules] FOREIGN KEY
(sectionid) REFERENCES cuyahoga_section (sectionid)
)
END
GO
/*加入模塊信息*/
INSERT INTO cuyahoga_moduletype ([name], assemblyname, classname, path, editpath, inserttimestamp, updatetimestamp) VALUES
('Announcements', 'Cuyahoga.Modules.Announcements', 'Cuyahoga.Modules.Announcements.AnnouncementsModule',
'Modules/Announcements/Announcements.ascx', 'Modules/Announcements/EditAnnouncements.aspx', current_timestamp, current_timestamp)
GO
/*加入模塊版本信息*/
INSERT INTO cuyahoga_version (assembly, major, minor, patch) VALUES ('Cuyahoga.Modules.Announcements', 1, 5, 0);
go
2.創建域模型 在本例中是實體類
Announcement.cs
using System;
using Cuyahoga.Core.Domain;
namespace Cuyahoga.Modules.Announcements.Domain
{
public class Announcement
{
private int _id;
private string _title;
private string _content;
private DateTime _expiredate;
private Section _section;
private User _createdBy;
private DateTime _updateTimestamp;
/// <summary>
/// Property Id (int)
/// </summary>
public int Id
{
get { return this._id; }
set { this._id = value; }
}
/// <summary>
/// Property Title (string)
/// </summary>
public string Title
{
get { return this._title; }
set { this._title = value; }
}
/// <summary>
/// Property Section (Section)
/// </summary>
public Section Section
{
get { return this._section; }
set { this._section = value; }
}
/// <summary>
/// Property CreatedBy (User)
/// </summary>
public User CreatedBy
{
get { return this._createdBy; }
set { this._createdBy = value; }
}
/// <summary>
/// Property UpdateTimestamp (DateTime)
/// </summary>
public DateTime UpdateTimestamp
{
get { return this._updateTimestamp; }
set { this._updateTimestamp = value; }
}
public Announcement()
{
this._id = -1;
}
}
}
3.創建映射文件
Announcement.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Cuyahoga.Modules.Announcements.Domain.Announcement, Cuyahoga.Modules.Announcements" table="cm_Announcements">
<id name="Id" column="Announcementsid" type="Int32" unsaved-value="-1">
<generator class="native">
<param name="sequence">cm_Announcements_Announcementsid_seq</param>
</generator>
</id>
<timestamp name="UpdateTimestamp" column="updatetimestamp" />
<property name="Title" column="title" type="String" length="150" />
<many-to-one name="Section" class="Cuyahoga.Core.Domain.Section, Cuyahoga.Core" column="sectionid" not-null="true" />
<many-to-one name="CreatedBy" class="Cuyahoga.Core.Domain.User, Cuyahoga.Core" column="createdby" not-null="true" />
</class>
</hibernate-mapping>
4.創建公告模塊的核心控制類
AnnouncementsModule.cs
using System;
using System.Collections;
using System.Xml;
using System.Xml.XPath;
using System.Net;
using System.Web;
using System.Text;
using System.IO;
using System.Threading;
using NHibernate;
using Castle.Services.Transaction;
using Castle.Facilities.NHibernateIntegration;
using log4net;
using Cuyahoga.Core;
using Cuyahoga.Core.Domain;
using Cuyahoga.Core.Service;
using Cuyahoga.Core.Util;
using Cuyahoga.Web.Util;
using Cuyahoga.Web.Components;
using Cuyahoga.Modules.Announcements.Domain;
namespace Cuyahoga.Modules.Announcements
{
//採用Facilities管理事務
[Transactional]
public class AnnouncementsModule : ModuleBase, INHibernateModule
{
private static readonly ILog log = LogManager.GetLogger(typeof(AnnouncementsModule));
private int _cacheDuration;
private ISessionManager _sessionManager;
//該模塊需要用到NHibernate session manager提供的服務 進行依賴注入
public AnnouncementsModule(ISessionManager sessionManager)
{
this._sessionManager = sessionManager;
}
public override void ReadSectionSettings()
{
base.ReadSectionSettings();
// Set dynamic module settings
this._cacheDuration = Convert.ToInt32(base.Section.Settings["CACHE_DURATION"]);
}
///不創建子事務
[Transaction(TransactionMode.RequiresNew)]
public virtual IList GetAllAnnouncements()
{
ISession session = this._sessionManager.OpenSession();
string hql = "from Announcement f where f.Section.Id = :sectionId";
IQuery q = session.CreateQuery(hql);
q.SetInt32("sectionId", base.Section.Id);
return q.List();
}
[Transaction(TransactionMode.RequiresNew)]
public virtual Announcement GetAnnouncementsById(int AnnouncementsID)
{
ISession session = this._sessionManager.OpenSession();
return (Announcement)session.Load(typeof(Announcement), AnnouncementsID);
}
[Transaction(TransactionMode.RequiresNew)]
public virtual void SaveAnnouncement(Announcement announcements)
{
ISession session = this._sessionManager.OpenSession();
session.SaveOrUpdate(announcements);
}
[Transaction(TransactionMode.RequiresNew)]
public virtual void DeleteAnnouncements(Announcement announcements)
{
ISession session = this._sessionManager.OpenSession();
session.Delete(announcements);
}
}
}
5.創建用於前臺顯示的用戶控件 用來顯示公告的標題 作者和發佈時間.
Announcements.ascx
<%@ Control Language="C#" AutoEventWireup="true" Codebehind="Announcements.ascx.cs"
Inherits="Cuyahoga.Modules.Announcements.Web.Announcements" %>
<asp:repeater id="rptAnnouncementItems" runat="server" enableviewstate="False">
<itemtemplate>
<div class="genericdetails" style="width:100%">
<marquee direction="left" >
<asp:label id="lblTitle" runat="server"><%# DataBinder.Eval(Container.DataItem, "Title")%></asp:label>
<asp:label id="lblAuthor" runat="server">作者:<%# DataBinder.Eval(Container.DataItem, "CreatedBy.FullName")%></asp:label>
<asp:label id="lblTime" runat="server">發佈時間:<%# DataBinder.Eval(Container.DataItem, "UpdateTimestamp")%></asp:label>
</marquee>
</div>
</itemtemplate>
</asp:repeater>
Announcements.ascx.cs
using System;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Cuyahoga.Core.Util;
using Cuyahoga.Web.UI;
using Cuyahoga.Modules.Announcements.Domain;
namespace Cuyahoga.Modules.Announcements.Web
{
public partial class Announcements : BaseModuleControl
{
private AnnouncementsModule _module;
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack && !base.HasCachedOutput)
{
this._module = base.Module as AnnouncementsModule;
this.rptAnnouncementItems.DataSource = this._module.GetAllAnnouncements();
this.rptAnnouncementItems.DataBind();
}
}
}
}
6.創建公告管理的列表頁面
EditAnnouncements.aspx
<%@ Page Language="C#" AutoEventWireup="true" Codebehind="EditAnnouncements.aspx.cs"
Inherits="Cuyahoga.Modules.Announcements.Web.EditAnnouncements" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>公告管理頁面</title>
</head>
<body>
<form id="Form1" method="post" runat="server">
<div id="moduleadminpane">
<h1>
公告管理</h1>
<table class="tbl">
<asp:Repeater ID="rptAnnouncements" runat="server" OnItemDataBound="rptFiles_ItemDataBound">
<HeaderTemplate>
<tr>
<th>
公告標題</th>
<th>
作者</th>
<th>
發佈日期</th>
<th>
</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<%# DataBinder.Eval(Container.DataItem, "Title") %>
</td>
<td>
<%# DataBinder.Eval(Container.DataItem, "CreatedBy.FullName")%>
</td>
<td>
<asp:Literal ID="litDateModified" runat="server"></asp:Literal></td>
<td>
<asp:HyperLink ID="hplEdit" runat="server">修改</asp:HyperLink>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
<br />
<input id="btnNew" type="button" value="新的公告" runat="server" name="btnNew"/>
</div>
</form>
</body>
</html>
EditAnnouncements.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Cuyahoga.Core.Domain;
using Cuyahoga.Core.Util;
using Cuyahoga.Web.UI;
using Cuyahoga.Modules.Announcements.Domain;
namespace Cuyahoga.Modules.Announcements.Web
{
public partial class EditAnnouncements : ModuleAdminBasePage
{
private AnnouncementsModule _module;
protected void Page_Load(object sender, EventArgs e)
{
// The base page has already created the module, we only have to cast it here to the right type.
this._module = base.Module as AnnouncementsModule;
this.btnNew.Attributes.Add("onclick", String.Format("document.location.href=
'EditAnnouncement.aspx{0}&AnnouncementId=-1'",
base.GetBaseQueryString()));
if (!this.IsPostBack)
{
BindFiles();
}
}
private void BindFiles()
{
this.rptAnnouncements.DataSource = this._module.GetAllAnnouncements();
this.rptAnnouncements.DataBind();
}
protected void rptFiles_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
Announcement announce = e.Item.DataItem as Announcement;
Literal litDateModified = e.Item.FindControl("litDateModified") as Literal;
if (litDateModified != null)
{
litDateModified.Text = TimeZoneUtil.AdjustDateToUserTimeZone(announce.UpdateTimestamp, this.User.Identity).ToString();
}
HyperLink hplEdit = e.Item.FindControl("hpledit") as HyperLink;
if (hplEdit != null)
{
hplEdit.NavigateUrl = String.Format("~/Modules/Announcements/EditAnnouncement.aspx{0}&AnnouncementId={1}", base.GetBaseQueryString(), announce.Id);
}
}
}
}
7.創建公告管理的具體頁面
EditAnnouncement.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EditAnnouncement.aspx.cs" Inherits="Cuyahoga.Modules.Announcements.Web.EditAnnouncement" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>修改公告頁面</title>
</head>
<body>
<form id="Form1" method="post" runat="server">
<div id="moduleadminpane">
<h1>
修改公告</h1>
<div class="group">
<h4>
公告信息</h4>
<table>
<tr>
<td style="width: 100px">
標題:</td>
<td>
<asp:TextBox ID="txtTitle" runat="server" Width="300px"></asp:TextBox>
<asp:RequiredFieldValidator ID="rfTitle" runat="server" ErrorMessage="輸入標題"
Display="Dynamic" CssClass="validator" ControlToValidate="txtTitle" EnableClientScript="False"></asp:RequiredFieldValidator>
</td>
</tr>
</table>
</div>
<p>
<asp:Button ID="btnSave" runat="server" Text="保存" OnClick="btnSave_Click"></asp:Button><asp:Button ID="btnDelete"
runat="server" Visible="False" CausesValidation="False" Text="刪除" OnClick="btnDelete_Click"></asp:Button><asp:Button
ID="btnCancel" runat="server" CausesValidation="False" Text="取消" OnClick="btnCancel_Click"></asp:Button></p>
</div>
</form>
</body>
</html>
EditAnnouncement.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Cuyahoga.Core.Domain;
using Cuyahoga.Web.UI;
using Cuyahoga.Modules.Announcements.Domain;
namespace Cuyahoga.Modules.Announcements.Web
{
public partial class EditAnnouncement : ModuleAdminBasePage
{
private AnnouncementsModule _module;
private int _announcementid;
private Announcement _announcement;
protected void Page_Load(object sender, EventArgs e)
{
// The base page has already created the module, we only have to cast it here to the right type.
this._module = base.Module as AnnouncementsModule;
this._announcementid = Int32.Parse(Request.QueryString["AnnouncementId"]);
if (this._announcementid > 0)
{
this._announcement = this._module.GetAnnouncementsById(this._announcementid);
if (!this.IsPostBack)
{
BindAnnouncement();
}
this.btnDelete.Visible = true;
this.btnDelete.Attributes.Add("onclick", "return confirm('Are you sure?');");
}
else
{
this._announcement = new Announcement();
this.btnDelete.Visible = false;
}
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
base.OnInit(e);
}
#endregion
private void BindAnnouncement()
{
this.txtTitle.Text = this._announcement.Title;
this.txtContent.Text = this._announcement.Content;
//this.calDatePublished.SelectedDate = this._file.DatePublished;
}
protected void btnSave_Click(object sender, EventArgs e)
{
if (this.IsValid)
{
this._announcement.CreatedBy = this.User.Identity as User;
this._announcement.Title = this.txtTitle.Text;
this._announcement.Content = this.txtContent.Text;
this._announcement.Section = base.Section;
//this._announcement.UpdateTimestamp = DateTime.Now;
this._announcement.ExpireDate = DateTime.Now.AddDays(5);
try
{
// Only save meta data.
this._module.SaveAnnouncement(this._announcement);
Context.Response.Redirect("EditAnnouncements.aspx" + base.GetBaseQueryString());
}
catch (Exception ex)
{
ShowError("Error saving file: " + ex.Message);
}
}
}
protected void btnDelete_Click(object sender, EventArgs e)
{
try
{
this._module.DeleteAnnouncements(this._announcement);
Context.Response.Redirect("EditAnnouncements.aspx" + base.GetBaseQueryString());
}
catch (Exception ex)
{
ShowError("Error deleting Announcements: " + ex.Message);
}
}
protected void btnCancel_Click(object sender, EventArgs e)
{
Context.Response.Redirect("EditAnnouncements.aspx" + base.GetBaseQueryString());
}
}
}
到此就完成了公告模塊的開發 在後臺頁面將模塊添加到前臺頁面就可以顯示了.
可以改進的地方:
爲模塊添加url重寫,在後臺管理頁面設置參數