CSCI 4230
Internet Applicaiton Development
Spring 2001
Suggested Solution to Homework #4

(1)    For example, not too well documented:

use strict;
use CGI::Pretty qw(-nosticky);
use HTML::Entities;
use DBI;
$|++;
#
# IsMyCodeHotOrNot.pl
#
# Kwok-Bun Yue  February 23, 2001
#
# This CGI program accepts submission of code by users and lets
# users vote on the degree of hotness of each code.  This is
# a toy program modeled after the site AmIHotOrNot.com, except
# that program code, not pictures are hosted.
#
# There are no error checking for the submitted form.
#

=pod  MS Access Table:
 IsMyCodeHotOrNot
  Pid    AutoNumber
  name   Text(50)
  email   Text(100)
  description  Text(255)
  title   Text(200)
  code   Memo
  when   Date/Time
=cut

# Porting Global constants.
# Stylesheet file.
my $STYLE_FILE = 'styles/IsMyCodeHotOrNot.css';
# ODBC DSN
my $DSN = 'yuep';
# Voting range.
my $MIN_VOTE = 1;
my $MAX_VOTE = 10;

# Global constatns for author information.
my $AUTHOR_EMAIL = 'yue@uhcl.edu';
my $COPYRIGHT = 'copyright 2001 Kwok-Bun Yue';

# Global CGI object.
my $q = new CGI::Pretty;

# This file name without path.
my $SELF = $q->script_name();
# Remove path.
if ($SELF =~ /([^\\\/]*)$/) {
 $SELF = $1;
}
# Remove query string.
$SELF =~ s/\?.*//;

# Name of the hidden HTTP parameter for storing which page
#   the request comes from and which page to go to.
my $FROM = 'from';
my $TO = 'to';

# Values of the $FROM and $TO HTTP parameters.
my $VOTING_PAGE = 'votingPage';
my $SUBMIT_FORM = 'submitForm';
my $SUCCESSFUL_SUBMISSION = 'successfulSubmission';

# HTTP parameter for form voting.
my $VOTE = "vote";
# HTTP parameter for the unique id identifying
# the code stored in the database.
my $PID = "pid";

# HTTP parameters for form submissions.
my $NAME ='name';
my $DESCRIPTION = 'description';
my $EMAIL = 'email';
my $TITLE = 'title';
my $CODE = 'code';
my $CODEFILE = 'codefile';
my $LANGUAGE = 'language';

# Get HTTP parameters.
my $from = $q->param($FROM);
my $to = $q->param($TO);
my $name = $q->param($NAME);
my $email = $q->param($EMAIL);
my $description = $q->param($DESCRIPTION);
my $title = $q->param($TITLE);
my $language = $q->param($LANGUAGE);
my $code = $q->param($CODE);
my $codefile = $q->param($CODEFILE);
my $pid = $q->param($PID);
my $vote = $q->param($VOTE);

# Main logic
if (!$to || $to eq $VOTING_PAGE) {
 my $dbh = DBI->connect("dbi:ODBC:$DSN");
 displayVotingPageHeader();
 # Display Main page:
 if ($pid || $vote) {
  print $q->a({href=>"$SELF?$TO=$SUBMIT_FORM"},'Submit your own code'),
     $q->p;
  # Store vote and display result.
  exit 0 unless saveVoteToDb($pid, $vote,$dbh);
  displayVotingResult($pid, $vote, $dbh);
 }
 else {
  displayWelcomeMessage();
 }
 unless (displayNextCodeForVoting($pid,$dbh)) {
  print "Sorry, no code yet.  You are the first visitor.  Please submit code.",
     $q->p;
  displaySubmissionForm();
 }
 displayVotingPageFooter();
 $dbh->disconnect;
}
else {
 if ($to eq $SUBMIT_FORM) {
  displaySubmissionPageHeader();
  displaySubmissionForm();
  displaySubmissionPageFooter();
 }
 else {
  if ($to eq $SUCCESSFUL_SUBMISSION) {
   exit 0 unless saveSubmissionToDb($name, $email, $description, $title, $language, $code, $codefile);
   displaySubmissionAcknowledgement();
  }
  else {
   # Error.
   displayErrorPage('An unknown error has occurred.  Please look for future version.');
  }
 }
}
exit 0;    #   main.

# Display the header of the voting page.
sub displayVotingPageHeader {
 print $q->header,
    $q->start_html(-title=>'Submission Form for IsMyCodeHotOrNot',
                         -author=>$AUTHOR_EMAIL,
                         -meta=>{'keywords'=>'program code survey',
                                 'copyright'=>$COPYRIGHT},
                         -style=>{'src'=>$STYLE_FILE}),
    $q->h2('Is my code hot or not?'),
} # displayVotingPageHeader

# Save the vote to the database.
sub saveVoteToDb {
 my ($pid,$vote,$dbh) = @_;
 return unless $pid && $vote;
 my $votecount = 1; # Number of count for the vote.

    my $sql = "select votecount from IsMyCodeHotOrNotVote " .
     "where pid = $pid and vote = $vote";
 #  debug: print $q->header, "SQL => $sql<p>";
 my $sth = $dbh->prepare($sql) || die $q->header . $dbh->errstr;
 $sth->execute();

 # Check whether someone has voted for the pid with
 # the same vote before.
 if (my @row = $sth->fetchrow_array) {
  # Vote already exists.  Increment the count.
  $votecount = $row[0] + 1;
  $sql = "update IsMyCodeHotOrNotVote " .
      "set votecount = $votecount " .
      "where pid = $pid and vote = $vote";
 }
 else {
  $sql = "insert into IsMyCodeHotOrNotVote(pid, vote, votecount) " .
      "values($pid, $vote, $votecount)";
 }
 # debug: print $q->header, "SQL => $sql<p>";
 $sth = $dbh->prepare($sql) || die $q->header . $dbh->errstr;
 $sth->execute();
} # saveVoteToDb

# display the voting result.  The current code does not handle the
# case where there is no voting.
sub displayVotingResult {
 my $averageVote = 0;
 my $numVote = 0;
 my ($pid, $vote, $dbh) = @_;

 # Get total number of votes.
    my $sql = "select sum(votecount) from IsMyCodeHotOrNotVote " .
     "where pid = $pid " .
     "group by pid";
   # debug: print $q->header, "SQL => $sql<p>";
 my $sth = $dbh->prepare($sql);
 $sth->execute();
 if (my @row = $sth->fetchrow_array) {
  # Vote already exists.  Increment the count.
  $numVote = $row[0];
 }

 # Get average number of votes.
 if ($numVote) {
     $sql = "select sum(vote*votecount)/sum(votecount) from IsMyCodeHotOrNotVote " .
      "where pid = $pid " .
      "group by pid";
    # debug: print $q->header, "SQL => $sql<p>";
  my $sth = $dbh->prepare($sql);
  $sth->execute();
  if (my @row = $sth->fetchrow_array) {
   # Vote already exists.  Increment the count.
   $averageVote = $row[0];
  }
 }

 $sql = "select name, title, language from IsMyCodeHotOrNot " .
     "where pid = $pid";
 $sth = $dbh->prepare($sql);
 $sth->execute();
 my ($name, $title, $language) = undef;
 if (my @row = $sth->fetchrow_array) {
  # Vote already exists.  Increment the count.
  ($name, $title, $language) = @row;
 }

 print 'For the ',
    $q->span({-class=>'lightEmp'}, $language),
    ' program titled ',
    $q->span({-class=>'lightEmp'}, $title),
    ' written by ',
      $q->span({-class=>'person'}, $name),
    ':',
    $q->p,
    "Your rating on the program: $vote.",
    $q->br,
    'Total number of votes: ' , int($numVote), '.',
    $q->br,
    'Average rating: ', sprintf("%-.2f", $averageVote), '.',
    $q->p;
} # displayVotingResult

# Display the next code for voting.
sub displayNextCodeForVoting {
 my ($currentPid,$dbh) = @_;
 my $nextPid = 0;
 my $sql = '';
 my $sth = undef;

 # Get the next Pid to be displayed.
 if ($currentPid) {
     $sql = "select max(pid) from IsMyCodeHotOrNot " .
      "where pid < $currentPid ";
    # debug: print $q->header, "SQL => $sql<p>";
  $sth = $dbh->prepare($sql);
  $sth->execute();
  if (my @row = $sth->fetchrow_array) {
   $nextPid = $row[0];
  }
 }

 unless ($nextPid) {
  $sql = "select max(pid) from IsMyCodeHotOrNot ";
  $sth = $dbh->prepare($sql);
  $sth->execute();
  if (my @row = $sth->fetchrow_array) {
   $nextPid = $row[0];
  }
 }

 # The table is empty.
 return 0 unless $nextPid;

 # display the result.
 print $q->h3('Please give your vote for the following code'),
    $q->p;

 # Print voting choices as links.
 print '(NOT)&nbsp;&nbsp;';
 for (my $i=$MIN_VOTE; $i <= $MAX_VOTE; $i++) {
  print $q->a({href=>"$SELF?$TO=$VOTING_PAGE&$PID=$nextPid&$VOTE=$i"},$i),
        '&nbsp;&nbsp;';
 }
 print '(HOT)';

 # Print the code:
 print $q->p;

 $sql = "select name, email, title, language, description, code, when from IsMyCodeHotOrNot " .
     "where pid = $nextPid";
 $sth = $dbh->prepare($sql);
 $sth->{LongReadLen} = 1000000; # Set large buffer to read memo.
 $sth->execute();
 my ($name, $email, $title, $language, $description, $code, $when) = undef;
 if (my @row = $sth->fetchrow_array) {
  # Vote already exists.  Increment the count.
  ($name, $email, $title, $language, $description, $code, $when) = @row;
 }

 $code =~ s/ /&nbsp;/g;
 print $q->span({-class=>'lightEmp'}, $language),
    ' program submitted by ',
      $q->span({-class=>'person'}, $name),
    ' at ',
    $q->a({href=>"mailto:$email"},$email),
    " on $when:",
    $q->p,
    $q->span({-class=>'lightEmp'}, 'Title:'),
      $title,
    $q->p,
    $q->span({-class=>'lightEmp'}, 'Description:'),
    $q->p,
    $description,
    $q->p,
    $q->span({-class=>'lightEmp'}, 'Code:'),
    $q->p,
      $q->div({-class=>'code'}, $code);
 1;
} # displayNextCodeForVoting
 
 

# print the footer of the voting page.
sub displayVotingPageFooter {
 print end_html;
}

# Display initial welcome messgae.
sub displayWelcomeMessage {
 print 'Welcome to "Is My Code Hot or not?" ',
       'In this page, you can vote for the hotness of ',
    'other people code or you can ',
    $q->a({href=>"$SELF?$TO=$SUBMIT_FORM"},'submit your own code'),
    ' and let other people to vote on it.',
    $q->p;
} # displayWelcomeMessage

#    Display the submission page header.
sub displaySubmissionPageHeader {
 print $q->header,
    $q->start_html(-title=>'Submission Form for IsMyCodeHotOrNot',
                         -author=>$AUTHOR_EMAIL,
                         -meta=>{'keywords'=>'program code survey',
                                 'copyright'=>$COPYRIGHT},
                         -style=>{'src'=>$STYLE_FILE}),
    $q->h2('Is my code hot or not?'),
    $q->h4('Submit your code here'),
    $q->p;
} # displaySubmissionPageHeade

#    Display the form for accepting code submission.
sub displaySubmissionForm {
 # This is added because CGI.pm 'nosticky' pragma does
 # not appy to hidden field.  It is necessary to change
 # it explicitly.
 $q->param($TO, $SUCCESSFUL_SUBMISSION);
 print $q->start_multipart_form(-action=>$SELF),
#    $q->hidden(-name=>$FROM,
#                     -default=>[$SUBMIT_FORM]),
    $q->hidden(-name=>$TO,
                     -default=>[$SUCCESSFUL_SUBMISSION]),
    'Your name: &nbsp;&nbsp;',
    $q->textfield(-name=>$NAME,
                        -size=>20,
                        -maxlength=>50),
    $q->br,
    'Email address: &nbsp;&nbsp;',
    $q->textfield(-name=>$EMAIL,
                        -size=>30,
                        -maxlength=>80),
    $q->br,
    'Code title: &nbsp;&nbsp;',
    $q->textfield(-name=>$TITLE,
                        -size=>40,
                        -maxlength=>120),
    $q->p,
    'Code description:',
    $q->br,
    $q->textarea(-name=>$DESCRIPTION,
                       -rows=>5,
                       -columns=>50),
    $q->p,
    'Language of the code: &nbsp;&nbsp;',
          $q->scrolling_list(-name=>$LANGUAGE,
                             -values=>['Perl','Java','C','Visual Basic'],
                             -size=>1),
    $q->p,
    'Upload your code file: &nbsp;&nbsp;',
    $q->filefield(-name=>$CODEFILE,
                        -size=>30,
                        -maxlength=>80),
    $q->br,
    'Or cut and paste your code below: ',
    $q->br,
    $q->textarea(-name=>$CODE,
                       -rows=>5,
                       -columns=>50),
    $q->p,
    $q->submit(-name=>'submit',
                     -value=>'Submit Code!'),
    ' &nbsp;&nbsp;&nbsp;',
    $q->reset,
    $q->endform;
    $q->end_html;
} # displaySubmissionForm

#    Display the submission page footer.
sub displaySubmissionPageFooter {
 print $q->end_html;
} # displaySubmissionPageFooterer
 

# Display an acknowledgement page that the submission
# has been successful.
sub displaySubmissionAcknowledgement {
 print $q->header,
    $q->start_html(-title=>'Submission Result for IsMyCodeHotOrNot',
                         -author=>$AUTHOR_EMAIL,
                         -meta=>{'keywords'=>'program code survey',
                                 'copyright'=>$COPYRIGHT},
                         -style=>{'src'=>$STYLE_FILE}),
    $q->h2('Your submission is successful'),
    $q->h4('Thank you for your code submission'),
    'You may go to the ',
    $q->a({href=>"$SELF?$TO=$VOTING_PAGE"},'voting'),
    ' page.',
          $q->end_html;
} # displaySubmissionAcknowledgement

# Save the submission to the database.
sub saveSubmissionToDb {
 my ($name, $email, $description, $title, $language, $code, $codefile) = @_;

 # If there is a non-empty uploaded file, use it instead of the code
 # from the text area.
 if ($codefile) {
  my $tempCode = '';
  my $fh = $q->upload($CODEFILE);
  if ($fh) {
   while (<$fh>) {
           $tempCode .= $_;
      }
  }
  $code = $tempCode? $tempCode : $code;
 }

 # Insert the submission.
 $name = HTML::Entities::decode($name);
 $email = HTML::Entities::decode($email);
 $description = HTML::Entities::decode($description);
 $title = HTML::Entities::decode($title);
 $language = HTML::Entities::decode($language);
 $code = HTML::Entities::decode($code);

 # Change ' to two 's
 $name =~ s/'/''/g;
 $email =~ s/'/''/g;
 $description =~ s/'/''/g;
 $title =~ s/'/''/g;
 $language =~ s/'/''/g;

 # Add <br>.
 $code =~ s/\n/<br>/sg;

 my $dbh = DBI->connect("dbi:ODBC:$DSN");
    my $sql = "insert into IsMyCodeHotOrNot(name,email,description,title,language,code,when) " .
           "values('$name','$email','$description', '$title', '$language', ?, Now())";
 #  debug:  print $q->header, "SQL => $sql<p>";
 my $sth = $dbh->prepare($sql);
 $sth->bind_param(1, $code, DBI::SQL_LONGVARCHAR);
 unless ($sth->execute()) {
  displayErrorPage('An DB error has occured.  Please consult the administrator.' .
                      $dbh->errstr);
  return 0;
 }
 $dbh->disconnect;
 1;
}

# Error Page that should never be displayed.
sub displayErrorPage {
 my $message = shift;
 print $q->header,
    $q->start_html(-title=>'Error Page for IsMyCodeHotOrNot',
                         -author=>$AUTHOR_EMAIL,
                         -meta=>{'keywords'=>'program code survey',
                                 'copyright'=>$COPYRIGHT},
                         -style=>{'src'=>$STYLE_FILE}),
    $q->h2('Is Your Code Hot Or Not: Error Page'),
    $q->h4($message),
    $q->end_html;
} # displayErrorPage
 

IsMyCodeHotOrNot.css:

body, table, tr, td, input, textarea, select {font-size:10pt; font-family:verdana;}
h1, h2, h3, h4, h5, h6 {font-family:verdana;}
body {background-color:#ccccff;}
A:link     {color:#80000; font-size:10pt; text-decoration:none;}
A:visited {color:#008000; font-size:10pt; text-decoration:none;}
A:active  {color:#000080; font-size:10pt; text-decoration:none;}
A:hover  {color:#c00000; font size:10pt; text-decoration:none; font-weight:bold;}

.lightEmp {color:red}
.person {color:blue}
.code {background-color:#ffffff; font-family:Arial; font-size:9pt;}