Build Community with a Discussion Forum: Part I

Getting Started

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 tag configures the Web site to use Forms authentication. It also configures the log-in page to be Login.aspx (we ll create Login.aspx later). The section denies access to the anonymous users. This ensures that only authenticated users can access Web forms.

 

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 section.

 

Figure 7: Storing a database connection string in the configuration file.

 

The membership and roles providers are then configured using and sections, respectively (Figure 8).

 

 

   

   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 tag must be set to true in order to use roles-based security.

 

Creating a Log-in and Registration Page

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).

 


Figure 9: Attaching a master page to Login.aspx.

 

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.

 


Figure 10: 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.

 


Figure 11: The UpdatePanel causing partial page rendering.

 

Creating Application-specific Roles

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.

 


Figure 12: Creating roles using the Web Site Administration Tool.

 

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.

 


Figure 13: Assigning roles to a user.

 

Creating a Helper Web Service

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 [email protected]", cnn);

 cmd.Parameters.AddWithValue("@id", contextKey);

 object obj = cmd.ExecuteScalar();

 cmd.CommandText = "select count(*) from ForumThreads

 where [email protected] and IsApproved=1";

 object postCount = cmd.ExecuteScalar();

 cmd.CommandText = "select count(*) from ForumThreads

 where [email protected] and ParentId=0 and IsApproved=1";

 object threadCount = cmd.ExecuteScalar();

 cnn.Close();

 return obj.ToString() + "
" + "Total " +

 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 [email protected]", cnn);

 cmd.Parameters.AddWithValue("@id", contextKey);

 object obj = cmd.ExecuteScalar();

 retVal = obj.ToString() + "
";

 cmd.CommandText = "select count(*) from ForumThreads

 where [email protected] and IsApproved=1";

 object postCount = cmd.ExecuteScalar();

 cmd.CommandText = "select count(*) from ForumThreads

 where [email protected] 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 [email protected] 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)

   + "
" + " by " + reader.GetString(1) + " on "

   + 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 [email protected]", 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 [email protected]", 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.

 

Conclusion

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.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish