Support Your Customers with an Online Help Desk: Part III

Develop Web Forms Specific to Support Personnel

CodeTalk

LANGUAGES: C#

ASP.NET VERSIONS: 2.0

 

Support Your Customers with an Online Help Desk: Part III

Develop Web Forms Specific to Support Personnel

 

By Bipin Joshi

 

In Part II we developed Web forms and master pages related to customers. We also configured our Web site for membership and role features. In this final part we ll develop Web forms related to support personnel. To be specific, we ll develop Web forms for showing a list of pending issues, showing a history of all issues, and showing issues selected by support personnel. We ll also develop a Web form that shows a thread of communication between a customer and support personnel. This Web form is used by both customers and support personnel.

 

Pending Issues

The issues posted by customers are resolved by one or more support personnel. Whenever customers raise an issue, the issue must appear in a queue of pending issues. Support personnel can then pick up one or more issues from this queue. This calls for the development of a Web form that will show support personnel a list of all the pending issues. We ll develop PendingIssues.aspx just for this purpose.

 

Begin by adding a new Web form to the Support folder. While adding the Web form, make sure to attach SupportMasterPage.master to it. Recollect that in Part II we developed SupportMasterPage.master and placed it in the Support folder. Drag and drop a Label control at the top of PendingIssues.aspx and set its Text property to Pending Issues. Also, set its SkinId property to Heading. We want to ensure that only the users belonging to the SupportPerson role can access the PendingIssues.aspx Web form. We ll make use of the LoginView control for this purpose. The LoginView control is used to display to users (depending on their roles) different views of the Web form. Drag and drop a LoginView control. The RoleGroups property of the LoginView control contains one or more groups for which different views are to be shown. Figure 1 shows RoleGroup Collection Editor of Visual Studio.

 


Figure 1: RoleGroup Collection Editor.

 

Using RoleGroup Collection Editor, add a member and set its Roles property to SupportPerson. Once you close the dialog box, open the smart tag of the LoginView control. From the Views combo box of the smart tag choose RoleGroup[0] (see Figure 2).

 


Figure 2: Opening view for a role group.

 

Doing so will open the content template of role group 0, i.e., SupportPerson. Now drag and drop an UpdatePanel control inside the LoginView control. Next, drag and drop an Object Data Source control inside the UpdatePanel control. Run the configuration wizard for the Object Data Source control and set its business object as Issues. Then set its SELECT method to SelectPending (see Figure 3).

 


Figure 3: Configuring Object Data Source to select pending issues.

 

Next, drag and drop a GridView control inside the UpdatePanel control and set its DataSourceID property to ObjectDataSource1. Also, set its AllowPaging property to true and DataKeyNames property to IssueId. Using the Columns editor of the GridView control (see Figure 4), add columns as shown in Figure 5.

 


Figure 4: Adding columns to the GridView control.

 

Column

Property

Value

BoundField

HeaderText

Issue ID

 

DataField

IssueID

BoundField

HeaderText

Title

 

DataField

Title

BoundField

HeaderText

Posted By

 

DataField

PostedBy

BoundField

HeaderText

Posted On

 

DataField

PostedOn

ButtonField

Text

Select

Figure 5: Columns of pending issues.

 

This completes configuration of the GridView. When support personnel click the Select button, that issue should be assigned to the support personnel clicking the button. We ll do this in the code-behind file later.

 

The support personnel should keep an eye on the list of newly posted issues. They can, of course, refresh PendingIssues.aspx intermediately by using their browser s refresh button. However, it would be nice if the page automatically refreshes itself after a set interval. Fortunately, ASP.NET AJAX Extensions provide a server control named Timer that can refresh an UpdatePanel after a specified time. Drag and drop a Timer control inside the UpdatePanel control and set its Interval property to 60000. The Interval property specifies time in milliseconds, after which the UpdatePanel will be refreshed. We specify the refresh interval to be one minute. Depending on your pattern and frequency of new issues, you should configure this interval. But beware; if set to a very low value, it can cause overhead on your application. This completes the design of RoleGroup[0].

 

Open the smart tag of the LoginView control again and select the LoggedIn template from the Views combobox. The LoggedIn template will be shown to users who are logged in to the Web site but are not part of the SupportPerson role. Drag and drop a Label control inside the LoggedIn template and set its Text property to You are not authorized to access this page . This way when an unauthorized user tries to access PendingIssues.aspx, the above message is displayed.

 

Now go to the RowCommand event handler of the GridView control. When we click the Select button column, GridView raises the RowCommand event. Figure 6 shows this event handler.

 

protected void GridView1_RowCommand(object sender,

 GridViewCommandEventArgs e)

{

int issueid = int.Parse(GridView1.DataKeys[int.Parse(

 e.CommandArgument.ToString())].Value.ToString());

MembershipUser user = Membership.GetUser();

Issues.SetSupportPersonId(issueid, user.UserName);

GridView1.DataBind();

}

Figure 6: Selecting an issue for resolution.

 

The code in Figure 6 retrieves the issue ID that is being selected. This is done with the help of the DataKeys collection of GridView. The DataKeys collection contains all the values of the primary key column as indicated by the DataKeyNames property. The CommandArgument property of the GridViewCommandEventArgs class indicates the row number of the GridView row being selected. The DataKeys collection stores values as objects. Hence, we convert that value into an integer using the Parse method of the integer class. Then we call the GetUser method of the Membership object. The GetUser method returns the currently logged-in user in the form of the MembershipUser object. We then call the SetSupportPersonId method of the Issues class by passing the issue ID and user name. Recollect that the SetSupportPersonId method sets the SupportPersonId column of the Issues table via the Issues_SetSupportPersonId stored procedure. Finally, we call the DataBind method of the GridView class. Doing so will refill the GridView and thus remove the selected record from the pending issues list.

 

This completes PendingIssues.aspx. To test the Web form, run it in the browser and log in with the user ID Support1 (we created this user ID in Part II). Figure 7 shows PendingIssues.aspx in action.

 


Figure 7: Sample run of PendingIssues.aspx.

 

If you click the Select link, that issue will no longer appear in the pending issues list. Now log out and log in again with the user ID customer1. After logging in successfully, try navigating to PendingIssues.aspx and you ll see the error message from the LoggedIn template of the LoginView control (see Figure 8).

 


Figure 8: Error message from the LoggedIn template displayed to unauthorized users.

 

Selected Issues

Once support personnel select an issue from the list of pending issues, the selected issue is assigned for that user for resolution. The support personnel should be able to see all the issues selected by him. The support personnel can then unselect an issue or see its complete details. We ll develop a Web form named SelectedIssues.aspx that does this.

 

Add a new Web form named SelectedIssues.aspx in the Support folder and select its master page to be SupportMasterPage.master. Drag and drop a Label control at the top of the page. Set its Text and SkinId properties to Selected Issues and Heading, respectively.

 

Drag and drop a LoginView control and add to it a role group named SupportPerson. The procedure to add a role group is the same as we discussed earlier. Now open the template for RoleGroup[0] and drag and drop inside it an Object Data Source control. Configure the Object Data Source control by choosing its business object as Issues and SELECT method as SelectBySupportPersonId. The SelectBySupportPersonId method accepts the user ID of the currently logged-in support person. We will be supplying this parameter via code, so finish the Object Data Source configuration wizard without supplying any parameter information (see Figure 9).

 


Figure 9: Ignoring any parameter of the SelectBySupportPersonId method while configuring the Object Data Source control.

 

Next, drag and drop a GridView inside the LoginView control and set its DataSourceID property to ObjectDataSource1. Also, set the AllowPaging property of the GridView to true and the DataKeyNames property to IssueId. Now add columns to the GridView as shown in Figure 10. Figure 11 shows the properties of these columns that we need to set.

 


Figure 10: Adding columns to the GridView.

 

Column

Property

Value

BoundField

HeaderText

Issue ID

 

DataField

IssueId

BoundField

HeaderText

Title

 

DataField

Title

BoundField

HeaderText

Posted By

 

DataField

PostedBy

BoundField

HeaderText

Posted On

 

DataField

PostedOn

HyperLinkField

Text

Show

 

DataNavigateUrlFields

IssueId

 

DataNavigateUrlFormatString

~/displaythread.aspx?issueid={0}

ButtonField

Text

Unselect

Figure 11: Properties of the GridView columns we must set.

 

Note the HyperLinkField column. When support personnel clicks the Show link, we want to be navigated to a page named DisplayThread.aspx (we ll develop DisplayThread.aspx later). DisplayThread.aspx then shows the complete issue and past responses, if any. DisplayThread.aspx needs to know the issue ID of an issue to be displayed. We pass the issue ID via a querystring parameter named issueid. The DataNavigateUrlFormatString property of HyperLinkField indicates a format of the resultant hyperlink. The value of issueid will be coming from the IssueId column. To represent this value, a placeholder  {0} is used. Furthermore, the DataNavigateUrlFields property specifies a comma-separated list of database columns whose values are used by the DataNavigateUrlFormatString property. In our example, we set the DataNavigateUrlFields property to IssueId. Next, add the code shown in Figure 12 to the Page_Load event handler of SelectedIssues.aspx.

 

protected void Page_Load(object sender, EventArgs e)

{

MembershipUser user=Membership.GetUser();

ObjectDataSource1.SelectParameters["supportpersonid"].

 DefaultValue = user.UserName;

}

Figure 12: Supplying supportpersonnelid to the SelectBySupportPersonId method.

 

The code retrieves the current user by calling the GetUser method of the Membership object. We then set the supportpersonid parameter from the SelectParameters collection of the Object Data Source control to the UserName of the current user. This way only the selected issues of a currently logged-in user are fetched.

 

If support personnel wants to unselect an issue for some reason, then they must click the Unselect link of the GridView. Doing so raises the RowCommand event of the GridView. Figure 13 shows the RowCommand event handler.

 

protected void GridView1_RowCommand(object sender,

 GridViewCommandEventArgs e)

{

GridView gv = (GridView)sender;

int issueid = int.Parse(gv.DataKeys[int.Parse(

 e.CommandArgument.ToString())].

Value.ToString());

Issues.UnsetSupportPersonId(issueid);

gv.DataBind();

}

Figure 13: Unselecting an issue.

 

The code first typecasts the sender parameter of the event handler to the GridView. It then retrieves the issue ID of the record from the DataKeys collection. It then calls the UnsetSupportPersonId method of the Issues class. The UnsetSupportPersonId method sets the SupportPersonId column of the Issues table to NULL. This way, the issue starts appearing in the list of pending issues (PendingIssues.aspx) again. Finally, the DataBind method of the GridView is called to refill the grid with the remaining selected issues. Figure 14 shows a sample run of SelectedIssues.aspx.

 


Figure 14: Sample run of SelectedIssues.aspx.

 

The Show hyperlink will not work because we have not yet developed DisplayThread.aspx. If you click the Unselect link, that issue will be removed from selected issues and will start appearing in the list of pending issues (PendingIssues.aspx) again.

 

Responding to an Issue

Now that we ve developed customer and support personnel-specific forms, it s time to develop an important piece of functionality. Currently, customers can post issues and support personnel can see and select them. However, they must be able to post responses to the issues raised by the customers. Similarly, customers should be able to reopen an issue if they need some further information. These functional pieces are captured by DisplayThread.aspx. DisplayThread.aspx displays a thread of communication between a customer and support personnel. Using this Web form, support personnel respond to the issues posted by customers. They also decide if an issue is closed or not. Customers, on the other hand, can reply with more queries or clarifications. When customers respond to an issue, the issue again becomes pending or open.

 

Begin by adding to the root folder of the Web site a new Web form named DisplayThread.aspx. This Web form is used by customers and support personnel, so we cannot place it inside the Customer or Support folders. A master page for this Web form must be set programmatically. If DisplayThread.aspx is being accessed by a customer, the master page attached must be CustomerMasterPage.master. Similarly, if the Web form is being accessed by support personnel, the master page should be SupportMasterPage.master. While adding the Web form you can choose any master page.

 

Drag and drop an Object Data Source control and configure it to use the Issues class as the business object. This Object Data Source control will call the SelectById method of the Issues class to display an issue (see Figure 15).

 


Figure 15: Selecting a specific issue via the SelectById method.

 

Recollect that we are navigating to the DisplayThread.aspx Web form from two Web forms, namely Customers/History.aspx and Support/SelectedIssues.aspx. While doing so, we pass an issue ID as a querystring parameter. Therefore, we need to configure the Object Data Source control such that the parameter source of the issueid parameter of the SelectById method is a querystring parameter named issueid. Figure 16 shows the relevant dialog box of the Object Data Source configuration wizard.

 


Figure 16: Specifying a parameter source as querystring.

 

Now drag and drop another Object Data Source control and configure it to use the IssueResponses class as its business object. Set the SELECT method of the Object Data Source control to be SelectByIssueId (see Figure 17).

 


Figure 17: Calling SelectByIssueId to retrieve responses for an issue.

 

Just like the previous Object Data Source control, configure a parameter source for the issueid parameter of the SelectByIssueId method to be a querystring parameter named issueid. Next, drag and drop a FormView control on the Web form and set its DataSourceID property to ObjectDataSource1. Design its ItemTemplate as shown in Figure 18.

 


Figure 18: ItemTemplate of FormView for displaying issue details.

 

You need to bind the various Label controls shown in Figure 18 to the columns of the Issues table. We do this by using the Databindings Editor of the Label control (see Figure 19). Note the use of the Eval data binding expression. Figure 20 shows all the data bindings we need to add.

 


Figure 19: Binding Label controls to columns of the Issues table.

 

Label

Eval expression

Label3

Eval( Title )

Label4

Eval( Description )

Label6

Eval( PostedBy )

Label8

Eval( PostedOn , {0:g} )

Label10

(Eval( Status ).ToString== O ? Open : Closed )

Label12

Eval( ResolvedOn )

Figure 20: Binding FormView to display issue details.

 

Note the Eval expression for Label10. The Issues table contains a column named Status. This column stores the status of an issue. The possible status codes are O (Open) and C (Closed). The Eval expression checks the status code and displays a more readable string, Open or Closed, to the user.

 

Now drag and drop an UpdatePanel control on the Web form. Also add an UpdateProgress control and set its AssociatedUpdatePanelID property to UpdatePanel1. Place a Label control inside the UpdateProgress control and set its Text property to Please wait... . Also, set its SkinId property to Message. Add a DataList control inside the UpdatePanel and set its DataSourceID property to ObjectDataSource2. Next, design ItemTemplate of the DataList as shown in Figure 21. Figure 22 shows data binding expressions of various Label controls that make up the ItemTemplate.

 


Figure 21: ItemTemplate of DataList showing issue responses.

 

Label

Eval expression

Label13

Eval( Title )

Label14

Eval( Description )

Label16

Eval( PostedBy )

Label18

Eval( PostedOn , {0:g} )

Figure 22: Binding DataList to display issue responses.

 

Next, design FooterTemplate of the DataList as shown in Figure 23. FooterTemplate allows the users to respond to the issue. The Mark as closed checkbox is visible only to support personnel. Clicking the Submit button adds a record in the IssueResponses table. This completes the design of DisplayThread.aspx. Now write the code shown in Figure 24 in the Page_PreInit event handler of DisplayThread.aspx.

 


Figure 23: FooterTemplate of DataList.

 

protected void Page_PreInit(object sender, EventArgs e)

{

if (Roles.IsUserInRole("SupportPerson"))

{

this.MasterPageFile = "~/support/SupportMasterPage.master";

}

else

{

this.MasterPageFile = "~/customer/CustomerMasterPage.master";

}

}

Figure 24: Changing the master page of DisplayThread.aspx at run time.

 

As discussed earlier, we need to assign the master page of DisplayThread.aspx at run time, depending on the role of the currently logged-in user. We do this in PreInit of the Web form. We check the role of a user using the IsUserInRole method of the Roles object. If the role is SupportPerson, we set the MasterPageFile property of the Web form class to SupportMasterPage.master; otherwise, we set it to CustomerMasterPage.master.

 

We should ensure that a particular issue and its responses are accessed only by the customer who raised the issue and support personnel. This is done in the Page_Load event handler (see Figure 25).

 

protected void Page_Load(object sender, EventArgs e)

{

int issueid =

 int.Parse(Request.QueryString["issueid"].ToString());

Issue issue= Issues.SelectById(issueid);

MembershipUser user=Membership.GetUser();

if(Roles.IsUserInRole("Customer") &&

 issue.PostedBy!=user.UserName)

{

throw new Exception("You are not authorized to view

 this thread!");

}

}

Figure 25: Allowing appropriate users to view an issue.

 

The code retrieves the issue ID passed via a querystring. It then retrieves details of that issue by calling the SelectById method of the Issues class. The SelectById method returns an Issue object. The if condition checks if the currently logged-in user belongs to the Customer role. If so, it further ensures that the user who posted the issue (the PostedBy property) and the user who is accessing the page (the UserName property) are the same; otherwise, it throws an exception.

 

The FooterTemplate of the DataList contains a checkbox titled Mark as closed. This checkbox must be visible only to support personnel. This checking is done inside the ItemDataBound event handler of the DataList (see Figure 26).

 

protected void DataList1_ItemDataBound(object sender,

 DataListItemEventArgs e)

{

if (e.Item.ItemType == ListItemType.Footer)

{

if (Roles.IsUserInRole("SupportPerson"))

{

CheckBox cb = (CheckBox)e.Item.FindControl("CheckBox1");

cb.Visible = true;

}

else

{

CheckBox cb = (CheckBox)e.Item.FindControl("CheckBox1");

cb.Visible = false;

}

}

}

Figure 26: Showing or hiding the checkbox.

 

ItemDataBound is raised for each item of the DataList. We are interested only in the footer of the DataList, so the first if condition checks if the ItemType of the current item is Footer. The second if condition determines the role of the currently logged-in user. If the role is SupportPerson, then the checkbox is made visible; otherwise, it s made hidden.

 

When a user enters some response and clicks the Submit button, the DataList raises the ItemCommand event. The ItemCommand event handler adds a record to the IssueResponses table (see Figure 27).

 

protected void DataList1_ItemCommand(object source,

 DataListCommandEventArgs e)

{

MembershipUser user = Membership.GetUser();

int issueid =

 int.Parse(Request.QueryString["issueid"].ToString());

string subject=((TextBox)e.Item.FindControl(

 "TextBox1")).Text;

string desc= ((TextBox)e.Item.FindControl("TextBox2")).Text;

bool isclosed = ((CheckBox)e.Item.FindControl(

 "CheckBox1")).Checked;

IssueResponses.Add(issueid,subject,desc, user.UserName);

Issues.SetStatus(issueid,(isclosed?'C':'O'));

if (Roles.IsUserInRole("SupportPerson"))

{

SmtpClient client = new SmtpClient();

Issue issue=Issues.SelectById(issueid);

MembershipUser customer =

 Membership.GetUser(issue.PostedBy);

StringBuilder builder = new StringBuilder();

builder.Append("Hello,");

builder.Append("\r\n");

builder.Append("A response to your query titled ");

builder.Append(issue.Title);

builder.Append(" has been posted.");

builder.Append("You can view it on-line by visiting ");

builder.Append("http://");

builder.Append(Request.Url.Host);

builder.Append("/login.aspx");

builder.Append("\r\n\r\n");

builder.Append("--");

builder.Append("\r\n");

builder.Append("Customer Support");

MailMessage msg = new MailMessage();

msg.Subject = "[SUPPORT] Re: " + issue.Title;

msg.Body = builder.ToString();

msg.To.Add(customer.Email);

client.Send(msg);

}

DataList1.DataBind();

FormView1.DataBind();

}

Figure 27: Submitting a response.

 

The code in Figure 27 retrieves the user name, issue ID, subject, and description of the response. If the support personnel has checked the checkbox, it indicates the issue is to be marked as Closed; otherwise, it must be marked as Open. It then calls the Add method of the IssueResponses class. The status of the issue is set via the SetStatus method of the Issues class. If the currently logged-in user is a support person, an automated e-mail notification is sent to the customer informing him about the new response. Notice the use of SmtpClient and MailMessage classes to send an e-mail. The MailMessage class represents an e-mail message. The MailMessage thus created is sent via the SmtpClient class. The SmtpClient class uses the e-mail configuration as specified in the web.config file for sending the e-mail. After submitting the issue, the FormView and DataList are bound again so they reflect the new response.

 

Run the help desk application with the user ID customer1 or customer2. Click on the History hyperlink and select any of the previously posted issues. You should be navigated to DisplayThread.aspx and details of that issue will be displayed. Figure 28 shows a sample run of the Web form. Note that because you are logging in as a customer, the Mark as closed checkbox will not be displayed. Also try accessing the page by logging in as support personnel.

 


Figure 28: Sample run of DisplayThread.aspx.

 

Displaying a History of Issues

Now we ll develop the final Web form of our help desk application. Earlier we developed a Web form that shows the history of issues for a specific customer. That Web form is used by customers to track their issues. Support personnel also need to have a history of all the issues. The history of issues as seen by support personnel is the total list of issues, not only the issues handled by one person from support staff. To develop this Web form, add a new Web form in the Support folder and name it History.aspx. Make sure to set its master page to SupportMasterPage.master.

 

Drag and drop an UpdatePanel control inside the Web form. Add a GridView control, a FormView control, and a DataList control to the form. Further, drag and drop three Object Data Source controls on the form. Configure the first Object Data Source control to use the Issues class as its business object and the SelectAll method as its SELECT method. Configure the second Object Data Source control to use the Issues class as its business object and the SelectById method as its SELECT method. The issueid parameter of SelectById will be supplied by the GridView control. Finally, configure the third Object Data Source control to use IssueResponses as its business object and the SelectByIssueId method as its SELECT method. As in the previous case, the issueid parameter of SelectByIssueId will be supplied by the GridView control.

 

Set the DataSourceID property of the GridView control to ObjectDataSource1. Now add columns to the GridView as shown in Figure 29.

 

Column

Property

Value

BoundField

HeaderText

Issue ID

 

DataField

IssueID

BoundField

HeaderText

Title

 

DataField

Title

BoundField

HeaderText

Posted By

 

DataField

PostedBy

BoundField

HeaderText

Posted On

 

DataField

PostedOn

BoundField

HeaderText

Handled By

 

DataField

SupportPersonId

Select CommandField

SelectText

Show

Figure 29: Adding columns.

 

Now set the DataSourceID property of the FormView and DataList to ObjectDataSource2 and ObjectDataSource3, respectively. Once an issue is selected in the GridView, its details are shown in the FormView. All the responses for that issue are also shown in the DataList. The design of the FormView and DataList closely matches that of DisplayThread.aspx, so we won t repeat the steps.

 

When the Support/History.aspx Web form is loaded, the first issue should be selected by default (with its details shown). This is accomplished by handling the Page_Load event of the Web form (see Figure 30).

 

protected void Page_Load(object sender, EventArgs e)

{

if (GridView1.Rows.Count > 0)

{

GridView1.SelectedIndex = 0;

}

}

Figure 30: Selecting the first issue in the history GridView.

 

The code simply sets the SelectedIndex property of the GridView control to 0 if there are any rows in the GridView. Doing so will supply the issueid parameter to ObjectDataSource2 and ObjectDataSource3. As a result, the FormView and DataList controls will display issue details and responses, respectively. Figure 31 shows a sample run of Support/History.aspx.

 


Figure 31: Sample run of Support/History.aspx.

 

Conclusion

In this three-part series we developed a help desk application. To improve user experience and performance we used ASP.NET AJAX Extensions in many places. The partial-page-rendering model used by controls such as UpdatePanel allows us to convert any server control into an AJAX-enabled control. In Part III we developed Web forms related to support personnel. We also developed a Web form that support personnel can use to respond to issues. You can further improve the functionality of our help desk by:

  • Providing issue deletion and an archival facility.
  • Using some fancy navigation controls (such as TreeView or Menu).
  • Allowing users to change their information, such as password and e-mail.

 

The sample code for Part III is available for download to asp.netPRO subscribers.

 

Bipin Joshi is the founder and owner of BinaryIntellect Consulting (http://www.binaryintellect.com), where he conducts professional training programs on .NET technologies. He is the author of Developer s Guide to ASP.NET 2.0 (http://www.binaryintellect.com/books) and co-author of three Wrox books on .NET 1.x. He writes regularly for http://www.DotNetBips.com, a community Web site he founded in the early days of .NET. He is a Microsoft MVP, MCAD, MCT, and member of ASPInsiders. He jots down his thoughts about .NET, life, and Yoga at http://www.bipinjoshi.com. He also conducts workshops on Yoga and Meditation, where he helps IT professionals develop a positive personality. You can contact him at mailto:[email protected].

 

 

 

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