CodeTalk
LANGUAGES: C#
ASP.NET VERSIONS: 3.5
Build Community with a Discussion Forum: Part I
Getting Started
By Bipin Joshi
Community. This simple word has received serious attention from most leading software companies and even from non-software companies. Indeed, the importance of building a community around one s products and services has become a popular trend these days. To that end, discussion forum software plays a vital role in building such a community.
There are several discussion forum applications (free and commercial) that can help you with your own, customized communities. Most of them are loaded with dozens of features. However, there is a downside to this; many of these applications are bulky. They require too many resources in terms of database tables, graphics, and other system requirements. At times, you need something that is lightweight, yet meets your requirements especially when your community consists of a small group of people and you don t need too many rich features. It simply makes sense to quickly roll out something on your own; this series of articles will show you how.
Functional Requirements
Let s first jot down the functional requirements our discussion forum application should meet:
- The application should allow you to create multiple discussion forums (each for a specific purpose).
- Users should be able to start a new thread of discussion by posting a new question, or contribute to an existing thread by replying to a question.
- The discussion forums must be accessible only to authenticated users (to avoid any SPAM and marketing messages).
- A post made by a user must be moderated before it appears on the forum.
- For better user experience, the application should be AJAX-enabled.
- To make the application lightweight, it should use as few resources as possible (resources here include database tables, Web forms, components, etc.).
Software Requirements
We ll use ASP.NET 3.5 to build our discussion forum. The database used for storing application data is SQL Server 2005. We ll also use the AJAX Control Toolkit in some places. And although we ll use SQL Data Source while developing this application, you also can use Object Data Source. Of course, you must develop additional classes if you opt for Object Data Source. For designing and developing the Web forms, we ll use Visual Studio 2008 (Visual Web Developer Express Edition is fine, too).
Database Tables
One requirement for our discussion forum is that it should use minimum resources, such as database tables. Keeping this requirement in mind, we ll create only two tables in the database. The first table, named ForumCategories, stores information about different forums available; the second, named ForumThreads, stores forum posts and replies.
We first need to create a new database in SQL Server 2005. To do so, open SQL Server Management Studio and create a new database named ForumDb. Figure 1 shows the New Database dialog box of SQL Server Management Studio.
Figure 1: Create a new database
using SQL Server Management Studio.
Once the ForumDb database is created, add two tables (ForumCategories and ForumThreads, as shown in Figure 2). The download accompanying this article contains the database (.mdf) files, which you can attach directly in your SQL Server (see end of article for download details).
Figure 2: The ForumCategories and
ForumThreads tables.
As you can see from Figure 2, both tables contain an identity column that acts as a primary key. The ForumCategories table stores CategoryID, Name, and Description of one or more forums. The ForumThreads table stores details of posts in terms of Title, Description, PostedBy, and PostedOn columns. One peculiar feature of our discussion forum is that it stores posts and their replies in the same table. The ParentId column plays an important role in our application. For all the posts (i.e., the original question), the ParentId will be 0; for all the replies to that post, the ParentId will be the Id of the post. For example, if we have a post with Id as 100, that record will have Id as 100 and ParentId as 0. All the replies to that post will have ParentId as 100. This way, we easily can track one particular thread. We then can sort the messages belonging to a single thread as per their posted date and time to arrive at a particular discussion. The IsApproved bit column decides whether a message will be displayed in the forum or not. By default, IsApproved contains a value of 0, indicating that the message is not yet approved.
Configuring the Database for Application Services
Our discussion forum is authenticated; only authenticated users can read or post messages. We ll use the Forms-based authentication of ASP.NET, along with membership and roles services. Before we use membership and roles services, however, we need to configure the ForumDb database to support these features. You enable these features using the aspnet_regsql.exe tool. Running this tool from Visual Studio s Command Prompt starts a wizard that guides you through the configuration process. After successfully completing the wizard, you should see the tables shown in Figure 3 (those starting with aspnet_) in the ForumDb database.
Figure 3: Tables created by the aspnet_regsql.exe
tool.
Creating the Web Site
Now that we created and configured the ForumDb database, let s create a new Web site. Begin by creating a new ASP.NET Web Site in Visual Studio (see Figure 4).
Figure 4: Create a new Web site.
Make sure to choose Visual C# as your programming language. Once the Web site is created, add a new master page to it. This master page will be used with all the other Web forms, and simply contains a graphical logo and copyright notice. Drag and drop a ScriptManager control on the master page. Because many of our Web forms use AJAX functionality, it makes sense to add a ScriptManager control in the master page (rather than individual Web forms). Figure 5 shows the master page in design mode.
Figure 5: The master page in design
mode.
Configuring Membership and Roles Providers
Now that we have a master page, let s configure the authentication scheme for our site, along with membership and roles providers. Open the web.config file (if it doesn t exist already, add one to your Web site) and add to it the markup shown in Figure 6.
Figure 6: Configuring authentication scheme.
The
The membership and roles providers are configured such
that they store user and role information in the ForumDb database. This calls
for adding a connection string in the
Figure 7: Storing a database connection string in the configuration file.
The membership and roles providers are then configured
using
type="System.Web.Security.SqlMembershipProvider"/> type="System.Web.Security.SqlRoleProvider"/> Figure 8:
Configuring membership and roles providers. The membership provider uses the built-in
SqlMembershipProvider class to manage membership information, whereas the roles
provider uses the built-in SqlRoleProvider class to manage application-specific
roles. Note that the enabled attribute of the We need to create a Web form wherein new users can
register and existing users can log in. To develop this Web page, add a new Web
form to the Web site and name it Login.aspx. Make sure to attach the master
page we created earlier (see Figure 9). Then drag and drop two UpdatePanel controls and two
UpdateProgress controls to the Web form. The UpdatePanel controls enable
partial page rendering to the Web forms so that, instead of causing full page
post backs, only a small region will be updated. The first UpdatePanel contains
a Login control, the second UpdatePanel control contains a CreateUserWizard
control. The UpdateProgress controls display a progress indicator for the
UpdatePanel controls. The UpdateProgress controls are attached with the
UpdatePanel controls using the AssociatedUpdatePanelID property. Figure 10
shows the Login.aspx in design mode. Run the Login.aspx Web form we just created and register
at least two users. Notice that the UpdatePanel controls provide partial page
rendering. Also, observe the progress indicator message appearing in the
UpdateProgress controls. Figure 11 shows a sample run of the Web form while
trying to log in. The forum has two types of users: those who can read and
post messages and those who can moderate the messages. Because moderators also
can read and post messages, defining a single role is sufficient. To create
this role, invoke the Web Site Administration Tool from the Website | ASP.NET
Configuration menu option. Then, using the Security tab, create a role named
Moderator. Figure 12 shows this tool with the Moderator role defined. Once you create the Moderator role, you need to assign
this role to one or more users. This can be done using the manage users option
from the Security tab. Figure 13 shows the relevant page. We used UpdatePanel and UpdateProgress controls to AJAX-enable
the Web form in Login.aspx. ASP.NET AJAX also lets you consume Web services
from the client-side script. This feature can be of great help to increase the richness
of your application. We ll use this feature in our forum application at some
places, so we need to create a Web service that can be called from the client-side
script. Begin by adding to the Web site a new Web service (WebService.asmx).
Once the Web service is added, modify the Web service class definition shown in
Figure 14. ... using System.Web.Script.Services; ... [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ScriptService] public class WebService : System.Web.Services.WebService { ... } Figure 14:
Creating a script-callable Web service. Take note of a couple of things in the code shown in
Figure 14. The Web service class (WebService) is decorated with the
[ScriptService] attribute. A Web service marked with the [ScriptService]
attribute can be called from the client-side script. The [ScriptService]
attribute resides in the System.Web.Script.Services namespace. We need four Web
methods in our Web service (see Figure 15). Web Method Description GetForumDetails Returns a string containing forum description and total
number of posts. GetForumDetailsForModerator Returns a string containing forum description, total
number of posts, and details of the latest post made. ApprovePost Approves a post; i.e., set the IsApproved column value
of the ForumThreads table to 1. RejectPost Deletes a post from the ForumThreads table. Figure 15: Web
methods that will be called from client-side script. Let s now examine these Web methods one by one. Figure 16
shows the GetForumDetails Web method. [WebMethod] public string GetForumDetails(int contextKey) { string
strConn=ConfigurationManager.ConnectionStrings["connstr"]. ConnectionString; SqlConnection cnn = new
SqlConnection(strConn); cnn.Open(); SqlCommand cmd = new
SqlCommand("select description from ForumCategories
where CategoryId=@id", cnn); cmd.Parameters.AddWithValue("@id", contextKey); object obj =
cmd.ExecuteScalar(); cmd.CommandText =
"select count(*) from ForumThreads where categoryid=@id and
IsApproved=1"; object postCount =
cmd.ExecuteScalar(); cmd.CommandText =
"select count(*) from ForumThreads where categoryid=@id and
ParentId=0 and IsApproved=1"; object threadCount =
cmd.ExecuteScalar(); cnn.Close(); return obj.ToString() +
" postCount.ToString() +
" posts in " + threadCount.ToString() +
" threads!"; } Figure 16: Getting
forum details for users. The GetForumDetails Web method accepts an integer-representing
forum category ID and returns a string containing the information about the
forum. Note that the signature of this Web method must match, as shown in
Figure 16, because later we ll use this Web method with the PopupControlExtender
control of the AJAX Control Toolkit. Inside, we simply fetch a description of
the forum, total posts, and threads active on the forum. These details are to
be displayed in a client-side popup, so we ll format the string as HTML markup.
Notice the use of the ConfigurationManager class to retrieve the database
connection string from the web.config file. Figure 17 shows the GetForumDetailsForModerator
Web method. [WebMethod] public string GetForumDetailsForModerator(int contextKey) { string strConn =
ConfigurationManager.ConnectionStrings["connstr"]. ConnectionString; SqlConnection cnn = new
SqlConnection(strConn); string retVal =
""; cnn.Open(); SqlCommand cmd = new
SqlCommand("select description from ForumCategories
where CategoryId=@id", cnn); cmd.Parameters.AddWithValue("@id",
contextKey); object obj =
cmd.ExecuteScalar(); retVal = obj.ToString() +
" cmd.CommandText =
"select count(*) from ForumThreads where categoryid=@id and
IsApproved=1"; object postCount =
cmd.ExecuteScalar(); cmd.CommandText =
"select count(*) from ForumThreads where categoryid=@id and
ParentId=0 and IsApproved=1"; object threadCount =
cmd.ExecuteScalar(); retVal += "Total
" + postCount.ToString() + " posts in " + threadCount.ToString()
+ " threads!" + " cmd.CommandText =
"select top 1 title,postedby,postedon from ForumThreads where
categoryid=@id and ParentId=0 and IsApproved=1 order by
postedon desc"; SqlDataReader reader =
cmd.ExecuteReader(); if (reader.HasRows) { reader.Read(); retVal += "Latest
Post : " + reader.GetString(0) + " +
reader.GetDateTime(2).ToString(); } reader.Close(); cnn.Close(); return retVal; } Figure 17: Getting
forum details for moderators. The GetForumDetailsForModerator Web method is similar to
GetForumDetails, but it returns some additional information. In addition to a description
of the forum, total posts, and threads, it returns the title of the latest post,
along with user information and post date. This method is intended to be called
from administrative pages, which will be accessed only by the moderators. Later, when we develop a Web form for moderating the posts,
we ll allow the moderator to approve or reject individual messages. The
ApprovePost and RejectPost Web methods do this, respectively. These Web methods
are shown in Figure 18. [WebMethod] public void ApprovePost(int postId) { string strConn =
ConfigurationManager.ConnectionStrings["connstr"] .ConnectionString; SqlConnection cnn = new SqlConnection(strConn); cnn.Open(); SqlCommand cmd = new SqlCommand("update ForumThreads set IsApproved=1 where Id=@id", cnn); cmd.Parameters.AddWithValue("@Id", postId); cmd.ExecuteNonQuery(); cnn.Close(); } [WebMethod] public void RejectPost(int postId) { string strConn =
ConfigurationManager.ConnectionStrings["connstr"] .ConnectionString; SqlConnection cnn = new SqlConnection(strConn); cnn.Open(); SqlCommand cmd = new SqlCommand("delete from ForumThreads where Id=@id", cnn); cmd.Parameters.AddWithValue("@Id", postId); cmd.ExecuteNonQuery(); cnn.Close(); } Figure 18:
Approving and rejecting posts. The ApprovePost Web method accepts the ID of the post to
be approved. Inside, it simply sets to 1 the IsApproved column of that post.
All the requests to retrieve posts for displaying on the forum will have a
WHERE clause that filters only the posts, with the IsApproved column value
equal to 1. The RejectPost Web method accepts a post ID to be rejected and
simply deletes that post from the ForumThreads table. This completes our Web service. We are now ready to
develop several Web forms for displaying and moderating posts. We ll continue
our development in Part
II. A discussion forum is a very common way to build community
around your products and services. In this series of articles we ll develop an
AJAX-enabled discussion forum that is lightweight, yet meets our requirements.
In this first part we created a database with two tables. We also configured
our Web site for ASP.NET application services. We then created a log-in page
that makes use of UpdatePanel and UpdateProgress controls. Finally, we created
a Web service that will be called from client-side script to enhance the user
experience. The source code
accompanying this article is available for download. Bipin Joshi is the
proprietor of BinaryIntellect Consulting (http://www.binaryintellect.com)
where he conducts premier training programs on a variety of .NET technologies.
He wears many hats, including software consultant, mentor, prolific author, Webmaster,
Microsoft MVP, and member of ASPInsiders. Having adopted Yoga as a way of life,
Bipin also teaches Kriya Yoga. He can be reached via his blog at http://www.bipinjoshi.com. Creating a Log-in and Registration Page
Figure 9: Attaching a master page to
Login.aspx.
Figure 10: Login.aspx in design mode.
Figure 11: The UpdatePanel causing partial
page rendering. Creating Application-specific Roles
Figure 12: Creating roles using the Web
Site Administration Tool.
Figure 13: Assigning roles to a user.Creating a Helper Web Service
" + "Total " +
";
";
"
+ " by " + reader.GetString(1) + " on "Conclusion