ASP 101 - Active Server Pages 101 - Web04
The Place ASP Developers Go!

Please visit our partners


Windows Technology Windows Technology
15 Seconds
4GuysFromRolla.com
ASP 101
ASP Wire
VB Forums
VB Wire
WinDrivers.com
internet.commerce internet.commerce
Partners & Affiliates














ASP 101 is an
internet.com site
ASP 101 is an internet.com site
IT
Developer
Internet News
Small Business
Personal Technology

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

ASP 101 News Flash ASP 101 News Flash



 Top ASP 101 Stories Top ASP 101 Stories
What is ASP?
VBScript Classes: Part 1 of N
Migrating to ASP.NET

QUICK TIP:
Previewing ASP pages in Visual InterDev 6.0
Show All Tips >>
ASP 101 RSS Feed ASP 101 Updates


Collecting Opinions and Feedback

We've spent most of this chapter looking at ways that we can track errors on our site, and try to prevent them occurring in the first place. To finish off, we'll take a brief look at one way that you can prompt your visitors to tell you what they think about your site. While this might seem to be quite unconnected with errors, just remember that there are a whole variety of users, operating systems, browser types, and various other kinds of assorted network software and hardware out there. Your site may appear to work fine in your own browser and operating system environment, but be fundamentally broken when viewed by users in other environments.

Checking Your Site for 'Content' Errors

Unlike physical errors (such as broken links and missing graphics), cosmetic and content errors on your site generally can't be detected automatically. The only way you can really confirm that your site is always presenting the appearance you want, is to view it in as many different browsers as possible. For example, our HTML Reference Database works fine in Navigator and in Internet Explorer from version 3 upwards:

But this page just refuses to work at all in the latest version of Opera 3.0. This is because the values of the check boxes are not sent back to the server in the expected format, something we discussed when we looked at client-side scripting issues in Chapter 4. Our page contains <NOSCRIPT> tags within a <FORM>, which Opera 3.0 cannot handle (and yes, we've fixed it now):

What about Operating Systems, Networks and Language?

Some errors are quite easy to find, but there can be more insidious ones that are very difficult to track down. For example, do you have a Unix box and a Mac for testing your pages? If you only check your site using Windows-based browsers, how do you know they will work properly with other operating systems? These probably won't have the fonts you specify in your pages, and will provide HTML controls that look totally different to the Windows-style ones you're used to.

And what about other network software or hardware? Do you know what effect the various kinds of proxy servers have on pages that pass values between them? As we discovered in Chapter 5, you can't depend on Windows Challenge/Response authentication working though all proxy servers (depending on how they're configured), and it doesn't work at all on non-Microsoft browsers anyway.

The chances of being able to check your site on all the possible combinations of browser, operating system, and network configuration are probably as close to zero as makes no difference. And did you remember language issues? Words and phrases you use in your pages may be meaningless to people in other countries, or-even worse-mean something rude! In reality, you are going to depend a lot on feedback from people who use (or try to use) your site to discover these kinds of problems.

Providing an 'Opinions' Page

It's nice to know what people really think about your site, as well as how they physically use it (i.e. the kind of thing you discover from traffic figures that you collect in your server log files). The easiest way to collect this kind of information is to provide a questionnaire or survey page on your site, and ask people to fill it in.

We wanted to do more than that, because there are traditional difficulties with surveys on Web sites. Visitors often tend to be 'surfing', or moving around from site to site, when they get to your site. Expecting them to spend ten minutes filling in forms can be over-optimistic in a lot of cases. Unless there is some immediate inducement or benefit, it can often seem to be a waste of time.

Instead, we collect visitors opinions; using a small, non-intrusive, and very simple page that pops up away from the main browser window. We use the term 'opinions', because to the visitor it probably sounds a lot less time consuming and complicated than filling out a questionnaire or survey form. We also make it physically simple for a visitor to express their opinions, by including just six check boxes and two buttons:

Opening the 'Opinions' Page

To open this page into a new browser window, we use some client-side JavaScript code. This means that it won't be seen by users of non script-enabled browsers, but, as the page itself requires script to work properly, this isn't a bad thing. We also want to display the opinions window only at certain times, and certainly only once during a visitor's session on our site.

In fact, it would be nice to only open it at certain intervals for any one visitor, say a maximum of once every two weeks. This stops it annoying them by opening every time they come to our site, and makes the process of providing feedback look less like an automated operation and more like we really value their opinions.

The code that achieves the control we want over opening the 'opinions' window is shown below. It implements a function named getOpinions which needs to appear in every page where we want to trigger the opening of the window (providing it hasn't been seen in the last two weeks). To make updating the code easier, we place it in an include file, and we can then insert it into each page where want to use it with a SSI #include tag. Here's the contents of opinions.inc:

<SCRIPT LANGUAGE="JAVASCRIPT">
<!--
function getOpinions()
{
strCookie = document.cookie;
if (strCookie.indexOf("WDDoneOpinions=True") < 0)
{
theDate = new Date();
theDay = theDate.getDate() + 14; // gets the day of the month
if (theDay > 28) {
theDay = theDay - 28;
theMonth = theDate.getMonth() + 1;
theDate.setMonth(theMonth);
};
theDate.setDate(theDay); // sets the day of the month
strDate = theDate.toGMTString(); // expiry dates must be UNC (GMT)
document.cookie = "WDDoneOpinions=True;path=/;expires=" + strDate;
window.open("/common/opinion.htm","opinion_win",
"resizable=yes,scrollbars=no,toolbar=no,"
+ "location=no,directories=no,status=no,"
+ "menubar=no,width=370,200,top=5,left=5")
}
}
// -->
</SCRIPT>
You can see that the code uses a cookie, in a similar way to the pop-up window examples we looked at in Chapter 2. If the value WDDoneOpinions=True is present in the document's cookie property, we don't show the 'opinions' window. If the value is not present, we create a cookie containing this value and add it to the document's cookie property then open the 'opinions' window. Remember that this is all happening on the client, not on our server.

Creating the Expiry Date

What is interesting is how we create the expiry date for the cookie so that it will remain on the client machine's hard disk for two weeks. Normally cookies are discarded when the user closes their browser, and without an expiry date they will see the opinions page once every visit after they close and reopen their browser.

By creating an expiry date two weeks into the future in our code, and placing it in the cookie, we know it will not disappear when the browser is closed. But after two weeks it will expire, and no longer appear in the document's cookie property (or be sent back to our server). At this point, we'll create a new cookie with a new date two weeks into the future.

In our code, we add 14 days to today's date, and then check to see if we went past the end of the shortest month. If so, we subtract 28 to make it wrap correctly into the next month and increment the month (OK, so we sometimes miss a day or two, but it's not critical in this situation). Notice that the getDate and setDate methods retrieve and set the day within the date, not the whole date. Once we've calculated the new expiry date, we can convert it to UNC (GMT), and drop it into the cookie:

theDate = new Date();
theDay = theDate.getDate() + 14; // gets the day of the month
if (theDay > 28) {
theDay = theDay - 28;
theMonth = theDate.getMonth() + 1;
theDate.setMonth(theMonth);
};
theDate.setDate(theDay); // sets the day of the month
strDate = theDate.toGMTString(); // expiry dates must be UNC (GMT)
document.cookie = "WDDoneOpinions=True;path=/;expires=" + strDate;
 
Executing the getOpinions Function

All that remains now is to insert the opinions.inc include file into appropriate pages, and fire the getOpinions function it contains to open the new window. We include the opinions.inc file in the main 'content' menus for each section of the site, and fire the getOpinions function in the unload event of the window object, which is triggered when the page is unloaded:

...
<!-- #include virtual="/path_to_include_files/opinions.inc" -->
</HEAD>
<BODY ONUNLOAD="getOpinions();">
...
 
An alternative way of opening the page would be to use server-side code to create the page and then add an HTTP
WINDOW-TARGET header to the response-as shown in Chapter 2. However, as we discovered there, this is unreliable in many browsers. And it doesn't give us any control over window size and position, so a client-side solution is the better option.

The 'Opinions' Table

We now have a browser window that opens at the kind of intervals that we want, and we need to think about what we'll display inside it. We also need to think about how we'll store the opinions expressed by our users. We're collecting six items of information from checkboxes, which we'll store as a number in our database. SQL Server doesn't have an explicit True/False field type like Access and other desktop databases, so we'll use the usual notation of zero for False (no) and -1 for True (yes).

The next screenshot shows the table named Opinions that we'll be using to store values. As well as the six yes/no answers, we're also going to give the user the opportunity to provide their email address to add to our mailing list, so we need a text field for that. We'll also store the IP address of the site, so that we can use the table and associated reports with all three sites we host, and we'll store the current data and time using a default value in the table:

You'll notice that we've also included a unique key field, of type IDENTITY (which automatically generates unique numeric record identifiers, like an Access AutoNumber field). We'll need this when we come to use the information in a later example. If you implement the visitor logging and information system that we describe in the next chapter, and set up the database for it using the scripts that we supply, this table will be created for you in the IISLogs database. If not, you can use the script opinions.sql that is included with the samples for this chapter to create the table in another database instead.

Building the 'Opinions' Page

As to what you actually display inside the new browser window, that's obviously up to you. It depends on the kind of opinions and information you want to collect. The following code shows the file we use, named opinion.htm. We've included it with the other samples for this book, and you can modify it to suit your own requirements.

The Visible Part of the Page

The visible (body) part of the page is simple enough, with all the controls placed on a <FORM> that has the ACTION set to the name of an ASP file that will process user's opinions. The form also contains two HIDDEN-type controls that will return the user's email address (if supplied), and the IP address of our site (hard-coded into the HTML). To make it easier to see what's going on, we've removed the <FONT> tags that format the text from the code below-they are in the sample file that we provide:

...
<BODY TOPMARGIN=10>
<FORM NAME="opinion" ACTION="opinion.asp" METHOD="POST">
<TABLE CELLPADDING=0 CELLSPACING=0>
<TR>
<TD NOWRAP COLSPAN=2><B>* Thank you for visiting ... </B></TD>
</TR><TR>
<TD NOWRAP VALIGN="TOP"><INPUT TYPE="CHECKBOX" NAME="chkUseful"> </TD>
<TD NOWRAP VALIGN="MIDDLE">I find that your site is a useful ... </TD>
</TR><TR>
<TD NOWRAP VALIGN="TOP"><INPUT TYPE="CHECKBOX" NAME="chkDesign"> </TD>
<TD NOWRAP VALIGN="MIDDLE">I like the design and organization ... </TD>
</TR><TR>
<TD NOWRAP VALIGN="TOP"><INPUT TYPE="CHECKBOX" NAME="chkVisit"> </TD>
<TD NOWRAP VALIGN="MIDDLE">I am a regular visitor and/or ... </TD>
</TR><TR>
<TD NOWRAP VALIGN="TOP"><INPUT TYPE="CHECKBOX" NAME="chkBuy"> </TD>
<TD NOWRAP VALIGN="MIDDLE">I plan to buy at least one of your ... </TD>
</TR><TR>
<TD NOWRAP VALIGN="TOP"><INPUT TYPE="CHECKBOX" NAME="chkEmail"
ONCLICK="getAddress();"> </TD>
<TD NOWRAP VALIGN="MIDDLE">Please add me to your Web Developer ... </TD>
</TR><TR>
<TD NOWRAP VALIGN="TOP"><INPUT TYPE="CHECKBOX" NAME="chkComment"
ONCLICK="sendComments();"> </TD>
<TD NOWRAP VALIGN="MIDDLE">I'm sending you my comments in a ... </TD>
</TR><TR>
<TD NOWRAP ALIGN="RIGHT" COLSPAN="2">
<INPUT TYPE="HIDDEN" NAME="txtEmail">
<INPUT TYPE="HIDDEN" NAME="txtSiteIP" VALUE="199.199.199.199">
<INPUT TYPE="SUBMIT" VALUE="Submit">
<INPUT TYPE="RESET" VALUE="Cancel" ONCLICK="window.close();">
</TD>
</TR>
</TABLE>
</FORM>
</BODY>
</HTML>
The Code That Makes It Work

At the start of the page, after the page title in the <HEAD> section, we provide two JavaScript functions. The first, getAddress, uses a prompt dialog to collect the user's email address, and place it into a HIDDEN-type control named txtEmail in the <FORM> section of the main page.

The second function, sendComments, just opens the browser's or user's default email message dialog, by setting the page's location.href property to a URL that uses the mailto: protocol. In both cases, the functions only carry out the task if the corresponding check box is set. This value will also be stored in our database to indicate that they provided an email address or sent a comment.

In fact, we don't actually know this to be the case, because they could cancel the email prompt or new message dialog. We could check for a null value returned from the prompt and clear the checkbox, but we'll trust our visitors to provide accurate information. The results overall will be close enough for our needs anyway.

...
<SCRIPT LANGUAGE="JavaScript">
<!--
function getAddress() {
// prompts for an email address when user 'turns on' the fifth check box
if (document.forms[0].chkEmail.checked)
document.forms[0].txtEmail.value = prompt('Enter your email address:', '')
};
function sendComments() {
// opens a new email message window when user 'turns on' the sixth check box
if (document.forms[0].chkComment.checked)
location.href = 'mailto:feedback@yoursite.com'
};
//-->
</SCRIPT>
...
The ASP Page to Store the Opinions

To complete the operation of collecting opinions, we need a page to take the values in the form and store them in our database. It will also return a 'thank you' message in the opinions window. The page, opinion.asp, is shown below. It collects the values from the Request.Form collection and places them into a set of variables, converting the checkbox values into numbers as it goes.

Then the code checks to see if at least one of the checkboxes was actually checked-if not we assume they pressed Submit instead of Cancel by mistake, and so we don't want to store the results. But, providing we have got at least one opinion, we can go ahead and create the SQL statement, then execute it against our table:

<%@LANGUAGE="VBSCRIPT"%>
<!-- #include virtual="/connect/iislog.inc" -->
...
...
<%
'---------- store comments if there are any ------------------
bUseful = CInt(Request.Form("chkUseful") = "on") 'gives -1 for 'yes'
bDesign = CInt(Request.Form("chkDesign") = "on") 'and zero for 'no'
bVisit = CInt(Request.Form("chkVisit") = "on")
bBuy = CInt(Request.Form("chkBuy") = "on")
bEmail = CInt(Request.Form("chkEmail") = "on")
bComment = CInt(Request.Form("chkComment") = "on")
strAddress = Request.Form("txtEmail")
strSiteIP = Request.Form("txtSiteIP")
If bUseful + bDesign + bVisit + bBuy + bEmail + bComment < 0 Then
%>
<TD NOWRAP><B>* Thank you for you opinions about our site</B></FONT></TD>
<%
On Error Resume Next 'store opinions in database table
Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open strConnect 'defined in include file at head of page
strSQL = "INSERT INTO Opinions(bUseful,bDesign,bVisit,bBuy,bMailList," _
& "bComment,tAddress,tHostIP) VALUES(" & bUseful & "," & bDesign _
& "," & bVisit & "," & bBuy & "," & bEmail & "," & bComment & ",'" _
& strAddress & "','" & strSiteIP & "')"
oConn.Execute strSQL
Set oConn = Nothing
...
 

If we didn't get any opinions expressed, we provide a polite note to the user that this is the case. Then the remainder of the page creates a nice 'thank you', including our feedback email address:

...
Else 'no opinions expressed
%>
<TD NOWRAP><B>* You didn't check any of the boxes</B></FONT></TD>
<%
End If
%>
</TR><TR>
<TD>This site is here to help you, as a Web Developer, ... etc.</TD>
</TR><TR>
<TD NOWRAP ALIGN="RIGHT" COLSPAN="2">
<INPUT TYPE="RESET" VALUE="Close" ONCLICK="window.close();">  
</TD>
</TR>
</TABLE>
</FORM>
</BODY>
</HTML>
 
And here's the result. It takes only a few moments for a visitor to tell us what they think, and then they can get on and use our site to find what they came for:

You might disagree with our decision to treat no checked controls as meaning they have no opinion. It could mean that they just hated the site and never intend to buy any of our books. However, it's up to you to ask the questions you want answers to in relation to your own site, and to gauge the meaning of the answers you get.



BackContentsNext
�1998 Wrox Press Limited, US and UK.