A Multipage Survey

Previous Table of Contents Next

A Multipage Survey

Surveys are common places to find forms that span several different Web pages. They're sometimes too long to fit on a single page, and they can usually be broken up into categories.

What follows is a simple multipage Web survey to find out aspects about your personality. This survey presents four different pages but could easily be changed to support as many pages as you would like. The four pages are as follows:

  • A series of general questions to find out what kind of personality you have

  • Some specific questions about your habits and a question based on an answer from the first survey page

  • A page that allows you to enter your name and comments about the survey

  • A "thank you" message that prints after the survey is complete

The same CGI program is used to perform all four functions. It decides which page to open next, depending on which page it has just displayed. The core of the survey program is shown in Listing 23.1.

Did you Know?

By including the line

use CGI::Carp qw(fatalsToBrowser);

your CGI program's die() messages, which might normally go to the Web server's log file, are printed as part of the Web page. When you write longer CGI programs, this might help in debugging.

The results of the survey are saved in a text file but are not displayed by this program at all. This program simply collects the survey answers and stores them. You will have to write another CGI program to display the results.

Listing 23.1. First Part of the Survey Program

1:   #!/usr/bin/perl -w

2:   use Fcntl qw(:flock);

3:   use CGI qw(:all);

4:   use CGI::Carp qw(fatalsToBrowser);

5:   use strict;

6:   my $surveyfile="/tmp/survey.txt";

7:   my @survey_answers=qw(pettype daytype clothes

8:             castaway travel risky ownpet

9:             realname comments);

10:   my $semaphore_file="/tmp/survey.sem";

11:  print header;

12:  if (! param ) {

13:       page_one();       # Survey just started

14:  } elsif (defined param('pageone')) {

15:       page_two();       # Answered one page, print the second

16:  } elsif (defined param('pagetwo')) {

17:       page_three();     # Print the last page.

18:  } else {

19:       survey_done();    # Print a thank-you note, and save

20:  }

Lines 7–9: During the survey, each HTML form contains input fields. The names of each of the fields appear in this array. The save() function and the repeat_hidden() functions will use this array of names later.

Lines 12–13: If this CGI program is passed no parameters—that is, it's not loaded as the result of a form posting—the function page_one() is called to print the first page of the survey.

Lines 14–17: If an HTML form parameter called pageone is passed to this CGI program, the function page_two() is called. If pagetwo is passed, then page_three() is called.

Line 19: If HTML form parameters are passed to this CGI program, but not pageone or pagetwo, the survey is complete, the results are saved, and the "thank you" message is printed in the survey_done() function.

The submit buttons on each of the pages provide the clues as to which page should be loaded next, as you can see in Figure 23.4. Because the submit button name is passed into the CGI program as a parameter, it can be used to show which version of the page was just submitted to the program.

Figure 23.4. Diagram of which button causes which action.

Listing 23.2 continues the survey program.

Listing 23.2. Second Part of the Survey Program

21:  sub page_one {

22:      print<<END_PAGE_ONE;

23:  <form>

24:  <p>Are you a "cat person" or a "dog person"?<br/>

25:  <input type="radio" name="pettype" value="dog"/>Dog<br/>

26:  <input type="radio" name="pettype" value="cat"/>Cat<br/>

27:  </p><p>

28:  Are you more of an early-riser or a night owl?<br/>

29:  <input type="radio" name="daytype" value="early"/>Early riser<br/>

30:  <input type="radio" name="daytype" value="late"/>Night owl<br/>

31:  </p><p>

32:  At work, if you had a choice on how to dress....<br/>

33:  <input type="radio" name="clothes" value="casual"/>Casual<br/>

34:  <input type="radio" name="clothes" value="business"/>Business<br/>

35:  </p><p>

36:  If stranded on a desert island,

37:  who would you rather be stuck with?<br/>

38:  <input type="radio" name="castaway" value="ginger"/>Ginger<br/>

39:  <input type="radio" name="castaway" value="marya"/>Mary Ann<br/>

40:  <input type="radio" name="castaway" value="prof"/>Professor<br/>

41:  <input type="radio" name="castaway" value="skipper"/>Skipper<br/>

42:  <input type="submit" name="pageone" value="Next page"/></p>

43:  </form>


45:  }

Lines 22 through 44 are a new Perl construct that you haven't seen yet; it's called a here document. A here document allows you to specify a string that spans several lines, includes other quotation marks, and acts like a normal double-quoted string. To start a here document, you use << followed by a word. The quotation continues until the next occurrence of the word at the beginning of the line, as in this example:


This is included as part of the string.


The word that identifies the beginning of the here document—END_OF_QUOTE in the previous snippet or END_PAGE_ONE in Listing 23.2—must be followed by a semicolon. At the end of the here document, the word must appear starting in the first column and must not have any trailing characters such as spaces or semicolons. Inside the here document, variables expand as they do in normal double-quoted strings (""), so be careful using $ and @ inside a here document.

Using here documents allows you to embed a large amount of HTML in your Perl program without having to fuss with quotation marks, multiple print statements, and so on.

The function in Listing 23.2 simply prints an HTML form. The <form> tag does not contain an action or a method. When a <form> action attribute is not specified, the current CGI program (the one that produced the form) is reloaded when the form is submitted. When the method attribute is not specified, the default method, GET, is used.

Notice that the submit button on the form is named pageone. When this form is submitted, a parameter called pageone will be sent to the CGI program; the value isn't important here. When this parameter is submitted, it is the cue to the CGI program to load the second page.

Listing 23.3 continues the survey CGI program.

Listing 23.3. Third Part of the Survey Program

46:  # Print out any of the responses so far as hidden fields

47:  sub repeat_hidden {

48:       foreach my $answer ( @survey_answers ) {

49:           if (defined param($answer)) {

50:                 print "<input type=hidden";

51:                 print " name=\"$answer\" ";

52:                 print " value=\"", param($answer),"\"/>\n";

53:            }

54:       }

55:  }

56:  sub page_two {

57:       my $pet=param('pettype');

58:      if (! defined $pet) {

59:            $pet="goldfish";

60:       }

61:       print<<END_PAGE_TWO;

62:  <form>

63:  <p>Would you rather...<br/>

64:  <input type="radio" name="travel" value="travel"/>Travel<br/>

65:  <input type="radio" name="travel" value="home"/>Stay at home<br/>

66:  </p><p>

67:  Do you consider yourself...<br/>

68:  <input type="radio" name="risky" value="yes"/>A daredevil<br/>

69:  <input type="radio" name="risky" value="no"/>Cautious<br/>

70:  </p><p>

71:  Do you own a $pet?<br/>

72:  <input type="radio" name="ownpet" value="$pet"/>Yes<br/>

73:  <input type="radio" name="ownpet" value="no"/>No<br/>

74:  </p>

75:  <input type="submit" name="pagetwo" value="Last Page"/>


77:       repeat_hidden();

78:       print "</form>";

79:  }

Line 47: As the comment in line 46 indicates, the function in this line prints the values of all the form fields so far as hidden fields. The array @survey_answers contains all the possible "name=" values on the HTML forms. When run the first time, most of the fields will not exist because those portions of the survey haven't been filled out yet.

Lines 48–49: Each of the possible parameters in @survey_answers is checked, and each parameter is defined. An <input type="hidden"> HTML tag is printed to hold the value on the current form.

Line 56: This function prints the second page of the survey.

Lines 57–60: This function is called as the second page of the survey. If the first page of the survey was properly filled out, then param('pettype') will hold either dog or cat, and this value will be stored in $pet. If the responder skipped that question, and param('pettype') is not defined, then goldfish is used instead.

Lines 61–76: The rest of the second page of the form is printed, and $pet is substituted as a question; thus, this questionnaire is customized based on the response on the first page.

Line 77: Any HTML form parameters from the first page are carried over onto this form as hidden fields.

If you view the survey form at this point—the second page—all the answers from the first page are stored as hidden fields at the end of the second page. The code for the third page is shown in Listing 23.4.

Listing 23.4. Fourth Part of the Survey Program

80:  sub page_three {

81:       print<<END_PAGE_THREE;

82:  <form><p>

83:  Last page!  This information is optional!<br/>

84:  Your name:</p><p>

85:  <input type="text" name="realname"/><br/>

86:  Any comments about this survey:<br/>

87:  <textarea name="comments" cols="40" rows="10">

88:  </textarea>

89:  </p>

90:  <input type="submit" name="pagethree"

91:       value="Submit survey results"/>


93:       repeat_hidden();

94:       print "</form>";

95:  }

The function page_three() is fairly straightforward, simply printing a text box and a text area in a form. At the end, it again calls repeat_hidden() to put all the hidden fields into the third page of the survey. The conclusion to the survey CGI program is shown in Listing 23.5.

Listing 23.5. Last Part of the Survey Program

96:   sub survey_done {

97:       save();

98:       print "Thank You!";

99:   }

100:  #

101:  # Save all of the survey results to $surveyfile

102:  #

103:  sub save {

104:       get_lock();

105:       open(SF, ">>$surveyfile") || die "Cannot open $surveyfile: $!";

106:       foreach my $answer (@survey_answers) {

107:            if (defined param ($answer) ) {

108:                 print SF $answer, "=", param($answer), "\n";

109:            }

110:       }

111:      close(SF);

112:      release_lock();

113:  }

114:  #

115:  # Locks and unlocks the survey file so that multiple survey-takers

116:  # Don't clash and write at the same time.

117:  #


119: # Function to lock (waits indefinitely)

120: sub get_lock {

121:    open(SEM, ">$semaphore_file") 

122:        || die "Cannot create semaphore: $!";

123:    flock SEM, LOCK_EX;

124: }


126:# Function to unlock

127: sub release_lock {

128:    close(SEM);

129: }

Line 96: This function is called simply to print a "thank you" message—always a nice thing to do after having someone go through three pages of survey—and call the save() function.

Line 103: The save() function here is almost a clone of the save function from Hour 22, "Basic Forms." It locks the survey file with get_lock(), writes the answers to it using a mechanism similar to that in repeat_hidden(), and then unlocks the file with release_lock().

Feel free to modify this survey to suit your own needs. The design is fairly flexible and could be adapted for many purposes.

    Previous Table of Contents Next
    © 2000- NIV