Develop a Survey Application: Part III

Invitations, Questions, Statistics, and Security

CodeTalk

LANGUAGES: VB.NET

ASP.NET VERSIONS: 2.0

 

Develop a Survey Application: Part III

Invitations, Questions, Statistics, and Security

 

By Bipin Joshi

 

In Part II of this series we developed a Web form for managing questions, answer choices, and participants. In this final installment we ll make provisions for sending survey invitations, as well as develop an important Web form that displays survey questions and choices to the end user. Finally, we ll develop an administrative Web form for generating survey statistics.

 

Sending Survey Invitations

Recollect that in the ManageSurvey.aspx Web form we added a LinkButton titled Send Invitations. An invitation is sent to all the participants of the survey when the administrator clicks on this LinkButton. Let s code this functionality now.

 

Sending e-mails requires a from address, which we store in the section of the web.config file. Figure 1 shows the relevant markup from the web.config file.

 

Figure 1: Using the section to store the sender s e-mail address.

 

We must store a key named webmasteremail along with its value. We ll use this key further while sending e-mail invitations. We also need to configure the SMTP host and its credentials in the web.config file. Figure 2 shows this markup.

 

Figure 2: Configuring the SMTP host.

 

The section defines the SMTP configuration settings. The tag specifies the SMTP host name or IP address; set it to localhost. The defaultCredentials tag specifies whether to use the default credentials for authentication with the SMTP host. Now open ManageSurvey.aspx and go in the ItemCommand event handler of the DetailsView. Add the code shown in Figure 3 in the event handler.

 

Protected Sub DetailsView1_ItemCommand(ByVal sender

 As Object, ByVal e

 As System.Web.UI.WebControls.DetailsViewCommandEventArgs)

 Handles DetailsView1.ItemCommand

If e.CommandName = "Invite" Then

Dim cnn As New SqlConnection(

 ConfigurationManager.ConnectionStrings(

 "ConnectionString").ConnectionString)

Dim cmd As New SqlCommand("select * from surveyparticipants

 where [email protected]", cnn)

Dim p1 As New SqlParameter("@id", DetailsView1.SelectedValue)

cmd.Parameters.Add(p1)

Dim da As New SqlDataAdapter

da.SelectCommand = cmd

Dim ds As New DataSet

da.Fill(ds, "participants")

Dim client As New SmtpClient()

For Each row As DataRow In ds.Tables("participants").Rows

client.Send(ConfigurationManager.AppSettings

("webmasteremail"), row("email"),

"Invitation to participate in our survey",

"Please take the survey at the following URL :" & vbcrlf &

 http://" & Request.Url.Host & "/survey.aspx?id=" &

 DetailsView1.SelectedValue)

Next

Label3.Text = "Invitations sent successfully!"

End If

End Sub

Figure 3: Sending survey invitations.

 

Recollect that we set to Invite the CommandName property of the Send Invitations LinkButton. The code checks the CommandName property of DetailsViewCommandEventArgs and if it is indeed Invite, fetches all the participants for that survey in a DataSet. The code then iterates through all the fetched records and sends an e-mail to each participant. The e-mail contains the URL where the participant can take the survey. To send the e-mail, we created an instance of the System.Net.Mail.SmtpClient class. The SmtpClient class has a method named Send that takes four parameters: From, Recipients, Subject, and Body. The from address is retrieved from the section using the AppSettings collection of the ConfigurationManager class. The complete URL to Survey.aspx is formed with the help of the Request.Url.Host property and currently selected survey ID. A success message is displayed in a Label control when all the e-mails are sent.

 

Taking the Survey

After receiving the invitations, the end users are supposed to take the survey at the specified URL. The survey questions and possible choices are displayed on the Survey.aspx Web form. This Web form receives the survey ID as a query string parameter and renders the corresponding questions.

 

To develop Survey.aspx, add a new Web form in the root folder of the Web site and name it Survey.aspx. The Web form uses a DataList control to display the questions and the choices. Drag and drop a DataList control and a SQL data source control on the Web form. Configure the SQL data source control to select all the records from the SurveyQuestions table matching the SurveyID passed via query string. Then set the DataSourceID property of the DataList control to SqlDataSource1. Design the ItemTemplate of DataList as shown in Figure 4.

 


Figure 4: Displaying survey questions and choices.

 

Using the DataBindings dialog box, data bind Label3 to display QuestionID and Label2 to display the question. Below the Labels is an instance of RadioButtonList, CheckBoxList, and TextBox, respectively. Depending on the AnswerType of the question, only one of them will be displayed. Finally, there is a HiddenField that is bound with the AnswerType property. The FooterTemplate of the DataList simply contains a Button titled Submit. Listing One shows the complete markup of Survey.aspx.

 

We need to display the choices in a RadioButtonList, CheckBoxList, or TextBox, depending on the AnswerType of a question. This task is handled in the ItemDataBound event of the DataList. Add the code shown in Figure 5 in the ItemDataBound event handler of the DataList.

 

Protected Sub DataList1_ItemDataBound(ByVal sender As Object,

 ByVal e As System.Web.UI.WebControls.DataListItemEventArgs)

 Handles DataList1.ItemDataBound

If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType =

 ListItemType.AlternatingItem Then Dim anstype As

 HiddenField = e.Item.FindControl("HiddenField1")

Dim questionid As Label = e.Item.FindControl("Label3")

Dim rbl As RadioButtonList =

 e.Item.FindControl("RadioButtonList1")

Dim cbl As CheckBoxList = e.Item.FindControl("CheckBoxList1")

Dim txt As TextBox = e.Item.FindControl("TextBox1")

Dim ds As DataSet = GetDataSet(questionid.Text)

Select Case anstype.Value

Case "S"

rbl.Visible = True

cbl.Visible = False

txt.Visible = False

rbl.DataSource = ds

rbl.DataTextField = "Choice"

rbl.DataValueField = "ChoiceID"

rbl.DataBind()

Case "M"

rbl.Visible = False

cbl.Visible = True

txt.Visible = False

cbl.DataSource = ds

cbl.DataTextField = "Choice"

cbl.DataValueField = "ChoiceID"

cbl.DataBind()

Case "T"

rbl.Visible = False

cbl.Visible = False

txt.Visible = True

End Select

End If

End Sub

Figure 5: Displaying choices.

 

The event handler first checks the ItemType property of the item being data bound. Only if the ItemType is ListItemType.Item or ListItemType.AlternatingItem is the rest of the code executed. Inside the If condition we get a reference to all the controls of the ItemTemplate (i.e., QuestionID Label, RadioButtonList, CheckBoxList, TextBox, and HiddenField) using the FindControl method. Then we fill a DataSet with all the choices for the current QuestionID using a helper function named GetDataSet (discussed later). Depending on the AnswerType (single choice or multiple choices), this DataSet is bound with the RadioButtonList or CheckBoxList, respectively. The remaining controls are kept hidden from the end user.

 

The GetDataSet helper function accepts a QuestionID as a parameter and returns a DataSet with all the choices for that question. Figure 6 shows the complete code for this function.

 

Private Function GetDataSet(ByVal id As Integer) As DataSet

Dim cnn As New

 SqlConnection(ConfigurationManager.ConnectionStrings

("ConnectionString").ConnectionString)

Dim cmd As New SqlCommand("select * from surveychoices

 where [email protected]", cnn)

Dim p1 As New SqlParameter("@id", id)

cmd.Parameters.Add(p1)

Dim da As New SqlDataAdapter

da.SelectCommand = cmd

Dim ds As New DataSet

da.Fill(ds, "choices")

Return ds

End Function

Figure 6: The GetDataSet helper function.

 

Figure 7 shows a sample run of Survey.aspx. Note how the RadioButtonList, CheckBoxList, and TextBox are displayed depending on the AnswerType.

 


Figure 7: Sample run of Survey.aspx.

 

When the user fills the survey and clicks the Submit button, the answers should be stored in the SurveyAnswers table for later reference. This is achieved by handling the Click event of the Submit button. Add the code shown in Figure 8 in the Click event handler of the Submit button.

 

Protected Sub Button1_Click(ByVal sender As Object,

 ByVal e As System.EventArgs)

For Each item As DataListItem In DataList1.Items

If item.ItemType = ListItemType.Item Or item.ItemType =

 ListItemType.AlternatingItem Then

Dim questionid As Integer

Dim choiceid As Integer = 0

Dim choicetext As String = ""

questionid = CType(item.FindControl("Label3"), Label).Text

Dim anstype As HiddenField = item.FindControl("HiddenField1")

Select Case anstype.Value

Case "S"

Dim rbl As RadioButtonList =

 item.FindControl("RadioButtonList1")

choiceid = rbl.SelectedValue

SaveAnswer(questionid, choiceid, "")

Case "M"

Dim cbl As CheckBoxList = item.FindControl("CheckBoxList1")

For i As Integer = 0 To cbl.Items.Count - 1

If cbl.Items(i).Selected Then

choiceid = cbl.Items(i).Value

SaveAnswer(questionid, choiceid, "")

End If

Next

Case "T"

Dim txt As TextBox = item.FindControl("TextBox1")

choicetext = txt.Text

SaveAnswer(questionid, 0, choicetext)

End Select

End If

Next

End Sub

Figure 8: Saving survey answers.

 

The code iterates through all the Items and AlternatingItems of the DataList. With each iteration, QuestionID and AnswerType are retrieved using the FindControl method, as before. If the AnswerType is S , the SelectedValue of the RadioButtonList is retrieved. This answer is stored in the SurveyAnswers table using a helper function named SaveAnswer (discussed later). If the AnswerType is M , the code iterates through the CheckBoxList and each selected option is saved in the table using the SaveAnswer helper function. Finally, if the AnswerType is T , the text entered in the TextBox is saved to the database using the SaveAnswer helper function. Figure 9 shows the SaveAnswer function.

 

Private Sub SaveAnswer(ByVal qid As Integer, ByVal cid

 As Integer, ByVal ct As String)

Dim cnn As New SqlConnection(

 ConfigurationManager.ConnectionStrings

("ConnectionString").ConnectionString)

Dim cmd As New SqlCommand("insert into surveyanswers(

 QuestionID,ChoiceID,ChoiceText) values(@qid,@cid,@ct)", cnn)

Dim p1 As New SqlParameter("@qid", qid)

Dim p2 As New SqlParameter("@cid", IIf(cid = 0,

 DBNull.Value, cid))

Dim p3 As New SqlParameter("@ct", IIf(ct = "",

 DBNull.Value, ct))

cmd.Parameters.Add(p1)

cmd.Parameters.Add(p2)

cmd.Parameters.Add(p3)

cnn.Open()

cmd.ExecuteNonQuery()

cnn.Close()

End Sub

Figure 9: The SaveAnswer helper function.

 

The function accepts QuestionID, ChoiceID, and ChoiceText as parameters. Inside it prepares an INSERT statement with required parameters. Note how the @cid and @ct parameters are created. If the cid method parameter is 0, the @cid parameter inserts a NULL value in the ChoiceID column. Similarly, if the ct method parameter is an empty string, then the @ct parameter inserts a NULL value in the ChoiceText column.

 

Viewing Survey Statistics

Once the survey campaign has run and participants have submitted their answers, the administrator would like to see the statistics of the survey responses. To provide such functionality, add a new Web form named SurveyStats.aspx in the Admin folder. Figure 10 shows a sample run of this Web form.

 


Figure 10: Showing survey statistics.

 

The Web form consists of a DropDownList listing all the surveys. Selecting a survey shows all its objective questions and their choices, and votes for each choice. To design this Web form, drag and drop on the Web form two SQL data source controls, a DropDownList and a DataList. Configure one of the SQL data source controls to select all the records from the Survey table and configure the other to select all records from the SurveyQuestions table with AnswerType equal to S or M and matching the selected SurveyID from the DropDownList. Bind the DropDownList to the first SQL data source control and the DataList to the other SQL data source control. Design the ItemTemplate of the DataList, as shown in Figure 11.

 


Figure 11: ItemTemplate to display survey statistics.

 

Using the DataBindings dialog box, bind Label3 with the QuestionID column and Label2 with the Question column. The BulletedList displays all the choices, along with the votes for each choice. Listing Two shows the complete markup of SurveyStats.aspx. To display the number of votes for all the choices, we need to handle the ItemDataBound event of the DataList. Add the code shown in Figure 12 in the ItemDataBound event of the DataList.

 

Protected Sub DataList1_ItemDataBound(ByVal sender As Object,

 ByVal e As System.Web.UI.WebControls.DataListItemEventArgs)

 Handles DataList1.ItemDataBound

If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType =

 ListItemType.AlternatingItem Then

Dim questionid As Integer

questionid = CInt(CType(e.Item.FindControl("Label3"),

 Label).Text)

Dim cnn As New SqlConnection(ConfigurationManager.

 ConnectionStrings("ConnectionString").ConnectionString)

Dim cmd As New SqlCommand("select * from surveychoices

 where [email protected]", cnn)

Dim p1 As New SqlParameter("@qid", questionid)

cmd.Parameters.Add(p1)

Dim da As New SqlDataAdapter

da.SelectCommand = cmd

Dim ds As New DataSet

da.Fill(ds, "choices")

cnn.Open()

For Each row As DataRow In ds.Tables("choices").Rows

Dim cmdAns As New SqlCommand("select count(*) from

 surveyanswers where [email protected]", cnn)

Dim pCid As New SqlParameter("@cid", row("choiceid"))

cmdAns.Parameters.Add(pCid)

Dim count As Integer = cmdAns.ExecuteScalar()

row("Choice") = row("Choice") & " - " & count

Next

cnn.Close()

Dim list As BulletedList =

 e.Item.FindControl("BulletedList1")

list.DataSource = ds

list.DataTextField = "Choice"

list.DataBind()

End If

End Sub

Figure 12: Displaying choices along with the number of votes.

 

We retrieve the QuestionID using the FindControl method. Then we fetch all the possible choices for that question into a DataSet. We iterate through all the choices and for each choice select the total number of records from the SurveyAnswers table. The count is simply appended to the Choice column. Finally, the resultant DataSet is bound with the BulletedList control.

 

Securing Administrative Pages

In this last section we ll secure the administrative pages we created earlier so that only the administrator(s) can access them. To begin, open the web.config file and add the markup as shown in Figure 13.

 

...

Figure 13: Enabling Forms authentication.

 

Here, we set the authentication mode to Forms . We also set the loginUrl attribute of the tag to Login.aspx. The Web forms in the root folder (Login.aspx and Survey.aspx) can be accessed by all, so we configure the tag to allow all the users (*). The Web forms in the Admin folder need to be protected; hence, we add a tag for that folder and configure the tag to deny anonymous users (?). Now, create a user called Administrator (or admin) using the Web site administration tool (see Figure 14).

 


Figure 14: Creating a user.

 

Note that because we have not configured any specific membership provider, the membership information will be stored in the ASPNETDB.MDF file under the App_Data folder.

 

Next, add a Web form named Login.aspx and drag and drop a Login control on it. That s it! The site is now protected from anonymous users. Any attempt to access administrative pages directly will redirect the user to the login page, as shown in Figure 15.

 


Figure 15: Login.aspx in action.

 

Conclusion

In this final installment we completed the survey application by adding an invitation feature and Web forms to display survey questions and their choices. We also developed an administrative Web form for displaying survey statistics. And lastly, as a security feature, we implemented forms-based authentication that protects the administrative pages.

 

Here are some suggestions for extending and enhancing the survey application:

  • You can add functionality to track survey participants.
  • You can add functionality such that a participant can participate in the survey only once.
  • The survey statistics can be graphical and can include more details.
  • You may consider automating the job of sending invitations to the participants.
  • You can add some exception-handling code and custom error pages especially for Survey.aspx.
  • You may want to implement role-based security so several levels of users can use the survey data in different ways.

 

Have fun as you explore new ways to use and customize this application.

The sample code for this series is available for download.

 

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

 

Begin Listing One Markup of Survey.aspx

<%@ Page Language="VB" AutoEventWireup="false"

 CodeFile="Survey.aspx.vb" Inherits="_Default" %>

 Transitional//EN" "http://www.w3.org/TR/xhtml1/

 DTD/xhtml1-transitional.dtd">

Untitled Page

 ImageUrl="~/Images/logo.gif">

 DataSourceID="SqlDataSource1" Width="100%">

 Text='<%# Eval("QuestionID") %>'>

)

 Text='<%# Eval("Question") %>'>

 Font-Bold="False" Rows="5" TextMode="MultiLine">

 Value='<%# Eval("AnswerType") %>' />

 Text="Please answer the following survey questions :">

 

 OnClick="Button1_Click" />

 Text="Copyright (C) 2006. All rights reserved.">

 

 ConnectionString="<%$ ConnectionStrings:ConnectionString %>"

SelectCommand="SELECT * FROM [SurveyQuestions] WHERE

  ([SurveyID] = @SurveyID)">

 QueryStringField="id" Type="Int32" />

End Listing One

 

Begin Listing Two Markup of SurveyStats.aspx

<%@ Page Language="VB" MasterPageFile="~/Admin/

 AdminMasterPage.master" AutoEventWireup="false"

 CodeFile="SurveyStats.aspx.vb" Inherits=

"Admin_SurveyStats" title="Untitled Page" %>

 "ContentPlaceHolder1" Runat="Server">

 "

Suryey Statistics

">

 "Select Survey :">

 AutoPostBack="True" DataSourceID="SqlDataSource1"

DataTextField="Title" DataValueField="SurveyID">

 "SqlDataSource2" Width="100%">

 Text='<%# Eval("QuestionID") %>'>

)

 Text='<%# Eval("Question") %>'>

 BulletStyle="Square">

 Text="Here are the survey results :">


 ConnectionString="<%$ ConnectionStrings:ConnectionString %>"

SelectCommand="SELECT [SurveyID], Develop a Survey Application: Part III FROM [Survey]">

 

 ConnectionString="<%$ ConnectionStrings:ConnectionString %>"

SelectCommand="SELECT * FROM [SurveyQuestions] WHERE

  (([SurveyID] = @SurveyID) AND

  ([AnswerType] <> @AnswerType))">

 Name="SurveyID" PropertyName="SelectedValue"

Type="Int32" />

 Type="String" />

End Listing Two

 

 

 

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