#!/usr/bin/perl -w

###############################################################################
# Program name: guestBook.cgi                                                 #
# Program URL: http://krypton.mnsu.edu/~sahedm/www/cgi/                       #
# This is a data management program powered by MySQL.                         # 
#                                                                             #
# Copyright (C) 2002 Mohammad Sahed                                           # 
# This program can only be used as long as the copyright and header remains   # 
# intact. This copyright notice cannot be removed. Selling the code for this  # 
# program without prior written consent is expressly forbidden.               #
#                                                                             #
# This program is free software; you can modify it under the terms of the GNU #
# General Public License as published by the Free Software Foundation.        # 
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                #
# GNU General Public License for more details.                                #
#                                                                             #
# Contact Info:                                                               #
#                 Mohammad Sahed                                              #
#                 mohammad.sahed@mnsu.edu                                     #
#                 http://krypton.mnsu.edu/~sahedm/                            #
###############################################################################

use strict;
use CGI::Carp "fatalsToBrowser";
use CGI ":standard";
use DBI;

# -----------------------------------------------------------------------------
# User editable variables:
# (You have to change the value of the following variables:)

my $db = "guestBook";            # Database name
my $user = "mysqlUserName";      # The MySQL user name for the database connection
my $passwd = "mysqlPassword";    # The MySQL password for the database connection

my $tableName = "guestBook";   # The table name of the database
my $keyField = "id";   # This will be the keyfield of the table
my $sortField = "name";   # This will be the sortfield of the table
my $cgi = "guestBook.cgi";   # This will be the name of the program
my $dbTitle = "My Guest Book";   # This will be the title of the program
my $adminPasswd = "guestBookAdmin";   # This will be the 'admin password' of the 
                                      # program.
# This is a HASH which will generate the labels for the database columns. 
# The first values of the HASH (e.g. id, name) have to be same as the database
# column name. But you can change or customize the corresponding values (e.g. 
# ID, Your Name). 
my %COLUMNNAME = (
  'id','ID',
  'name','Your Name',
  'email','Email Address',
  'url','Web URL',
  'web','Web Title',
  'comments','Your Comment'
);

# Required Fields (These have to be same as the database column name.)
my $requiredField1 = 'name';
my $requiredField2 = 'email';
my $requiredField3 = 'comments';

# End of user editable variables.
# -----------------------------------------------------------------------------

my ($dbh, $sth);

$dbh = DBI->connect("DBI:mysql:$db:localhost","$user","$passwd");

print
   header,
   start_html(-title=>$dbTitle,
              -bgcolor=>'white'),
   h3("$dbTitle");

unless (param('action')) {
   &addForm;
} elsif (param('action') eq 'Add') {
   if (param('name') && param('email') && param('comments')) { &add; } 
   else { &blank; }
} elsif (param('action') eq 'View') {
   &view;
} elsif (param('action') eq 'Admin') {
   &admin;
} elsif (param('action') eq 'selectModify'
      || param('action') eq 'selectDelete') {
   &select;
} elsif (param('action') eq 'Modify') {
   &modifyForm;
} elsif (param('action') eq 'Update') {
   &update;
} elsif (param('action') eq 'Delete') {
   &delete;
} elsif (param('action') eq 'tableStructure') {
   &tableStructure;
} else {
   &addForm;
}

&copyright;
print
   end_html();

$dbh->disconnect;

###############################################################################
sub addForm {

my ($arrayReference, $length, $arrayIndex, $tableHead);

$sth = $dbh->prepare("SELECT *
                      FROM $tableName");
$sth->execute;
$arrayReference = $sth->{NAME};
$length = $sth->{PRECISION};

print
   "Please fill out the form below to sign my guest book.",
   hr,
   start_form(-method=>'POST',
              -action=>$cgi),
   "<TABLE>";

$arrayIndex = 0;
foreach $tableHead (@$arrayReference) {
   if ($tableHead ne $keyField) {
      print "<TR>",
         td({-align=>'RIGHT'},b($COLUMNNAME{$tableHead}));
         if (@$length[$arrayIndex] eq '65535') {
            print td(textarea(-name=>$tableHead,
                              -rows=>8,
                              -columns=>50,
                              -wrap=>'physical'));
         } else {
            print td(textfield(-name=>$tableHead,
                               -size=>@$length[$arrayIndex],
                               -maxlength=>@$length[$arrayIndex]));
         }
      print "</TR>";
   }
   $arrayIndex++;
}
$sth->finish;
print
   "</TABLE>",
   "Once you have completed the form, submit using the button below.",
   br,
   submit(-name=>'action', -value=>'Add'),
   submit(-name=>'action', -value=>'View'),
   end_form();

print
   hr,
   b("Administrative Window"),
   start_form(-action=>$cgi,
              -method=>'POST'),
   "Admin password: ",
   password_field(-name=>'adminPasswd'), br,
   submit(-name=>'action',
          -value=>'Admin'),
   end_form();
}

###############################################################################
sub add {

my ($tableHead, $keyValue, $exist, $tableColumns, $tableValues, 
    $quotedTableValues, $success, $errorCode, $errorMessage);
my (@allParam, @tableData);

@allParam = param();
foreach $tableHead (@allParam) {
   push @tableData, param($tableHead);
}
pop @allParam;
pop @tableData;

$keyValue = $dbh->quote($tableData[0]);
$sth = $dbh->prepare("SELECT *
                      FROM $tableName 
                      WHERE $sortField=$keyValue");
$sth->execute;
$exist = $sth->fetch;
$sth->finish;

if ($exist) {
   print
      b({-style=>'Color: red;'},"Sorry (Duplicate entry)"),
      hr,
      "Your name ", b($keyValue), " already exist in $db.", p,
      "Please note that you can be able to sing $dbTitle once. Thank you.";

&backToMain;
}

else {
   $tableColumns = join ',' , @allParam;
   $tableValues = join '","' , @tableData;
   $quotedTableValues = "\"".$tableValues."\"";

   $sth = $dbh->prepare("INSERT INTO $tableName ($tableColumns)
                         VALUES ($quotedTableValues)");
   $success = $sth->execute;

   if ($success != 1 && $success != -1) {
      $errorCode = $sth->err;
      $errorMessage = $sth->errstr;
      print b({-style=>'Color: red;'},"Failure"),
         hr,
         "Your entry hasn't been added for the following reason(s):",
         ul(
          li("Error code: " ,b("$errorCode")),
          li("Error message: ",b("$errorMessage")),
         );
      $sth->finish;
      &backToMain;
   }
   else {
      print 
         b("Thank you for signing $dbTitle"), 
         hr,
         "Thank you for filling in $dbTitle. Your entry has been added.";
      $sth->finish;
      &backToMain;
   }
}

}
###############################################################################
sub view {

my ($arrayReference, $numberOfRows, $tableHead, $tableData);
my @rowOfTd;

$sth = $dbh->prepare("SELECT *
                      FROM $tableName");
$sth->execute;

$arrayReference = $sth->{NAME};
$numberOfRows = $sth->rows;

print
   b("$dbTitle Entries"),
   hr,
   "Number of entries: ", b("$numberOfRows"), p,
   "<TABLE BORDER>
   <TR BGCOLOR=gray>";
foreach $tableHead (@$arrayReference) {
   print th($COLUMNNAME{$tableHead});
}
print "</TR>";

while (@rowOfTd = $sth->fetchrow_array) {
   print "<TR VALIGN=TOP>";
   foreach $tableData (@rowOfTd) {
      if ($tableData) { print td(pre($tableData)); }
      else { print td('&nbsp;'); }
   }
   print "</TR>";
}
$sth->finish;
print
   "</TABLE>";

&backToMain;

}
###############################################################################
sub admin {

my $submittedPasswd;
$submittedPasswd = param('adminPasswd');

if ($submittedPasswd eq $adminPasswd) {

   my %ACTION = (
      'selectDelete','Delete an entry',
      'selectModify','Modify an entry',
      'tableStructure','Online Table Structure'
   );

   print
      startform(-action=>$cgi,
                -method=>'POST'),
      b("Administrative Window:"),
      hr,
      "Welcome to Admin Window Menu. From this menu you can be able to 
       delete and modify entries of $db database.",
      p,
      "There is a option called ", i("Online Table Structure"), " to view the 
       structure of $tableName table.",
      hr,
      popup_menu(-name=>'action',
              -values=>['selectDelete','selectModify','tableStructure'],
              -labels=>\%ACTION,
              -default=>'tableStructure',
              -size=>3),
      br,
      submit(),
      endform(),
} else {
   print
      b({-style=>'Color: red;'},"Invalid password"),
      hr,
      "Your admin password ", b("$submittedPasswd"), " was invalid. 
       Please try again.";
   &backToMain;
}

}
###############################################################################
sub select {

my $actionName;
my (@rowOfTd, @valueOfTd);

if (param('action') eq 'selectModify') {
   $actionName = 'Modify';
} elsif (param('action') eq 'selectDelete') {
   $actionName = 'Delete';
}

$sth = $dbh->prepare("SELECT $sortField
                      FROM $tableName
                      ORDER by $sortField");
$sth->execute;

while(@rowOfTd = $sth->fetchrow_array) {
   push @valueOfTd, $rowOfTd[0];
}

print
   b("Select"),
   hr,
   "To ", i("$actionName"), " select an entry from the popup list. 
    Here the sortfield is ", b("$sortField"),
   startform(-action=>$cgi,
             -method=>'POST'),
   popup_menu(-name=>'item',
              -values=>\@valueOfTd),
   br,
   submit(-name=>'action',-value=>$actionName),
   endform();

&backToMain;

}
###############################################################################
sub modifyForm {

my ($item, $quotedString, $arrayReference, $length, $arrayIndex, $tableHead);
my @rowOfTd;

$item = param('item');
$quotedString = $dbh->quote($item);

$sth = $dbh->prepare("SELECT *
                      FROM $tableName
                      WHERE $sortField = $quotedString");
$sth->execute;

print
   b("Update Form"),
   hr,
   "Note: Since ", b("$keyField"), " field is the primary key of ", 
   b("$tableName"), " table, you don't have the option to modify this field.", 
   start_form(-method=>'POST',
              -action=>$cgi),
   "<TABLE>";

@rowOfTd = $sth->fetchrow_array;
$arrayReference = $sth->{NAME}; 
$length = $sth->{PRECISION};

$arrayIndex = 0;
foreach $tableHead (@$arrayReference) {
   print 
      "<TR>", 
      td({-align=>'RIGHT'},b($COLUMNNAME{$tableHead}));
   if ($tableHead eq 'id') {
      print
         td( $rowOfTd[$arrayIndex],
         hidden(-name=>$tableHead,
                -value=>$rowOfTd[$arrayIndex]));
   } else {
      if (@$length[$arrayIndex] eq '65535') {
         print td(textarea(-name=> $tableHead,
                           -rows=> 8,
                           -columns=> 50,
                           -value=> $rowOfTd[$arrayIndex]));
      } else {
         print td(textfield(-name=> $tableHead,
                            -size=> @$length[$arrayIndex],
                            -value=> $rowOfTd[$arrayIndex]));
      }
      print "</TR>";
   }
   $arrayIndex++;
}
$sth->finish;
print "</TABLE>",
submit(-name=>'action',-value=>'Update'),
end_form();

&backToMain;

}
###############################################################################
sub update {

my ($tableHead, $keyValue, $arrayNum, $submittedValue, $success);
my (@allParam, @tableData, @databaseValue);

@allParam = param();
foreach $tableHead (@allParam) {
   push @tableData, param($tableHead);
}

pop @allParam;
pop @tableData;

$keyValue = $dbh->quote($tableData[0]);
$sth = $dbh->prepare("SELECT *
                      FROM $tableName
                      WHERE $keyField=$keyValue");
$sth->execute;
@databaseValue = $sth->fetchrow_array;
$sth->finish;

$arrayNum = 0;
foreach $submittedValue (@tableData) {
   unless ($submittedValue eq $databaseValue[$arrayNum]) {
      $submittedValue = $dbh->quote($submittedValue);
      $sth = $dbh->prepare("UPDATE $tableName
                            SET $allParam[$arrayNum]=$submittedValue
                            WHERE $allParam[0]=$keyValue");
      $success = $sth->execute;
   }
$arrayNum++;
}

if ($success != 1 && $success != -1) {
   print
      b({-style=>'Color: red;'},"Failure"),
      hr,
      "Oops! You haven't change any information.", br,
      "The entry $keyField was: ", b("$keyValue");
} else {
   print
      b("Update"),
      hr,
      "Your entry has been updated.", br,
      "The entry $keyField was: ", b("$keyValue");
}
$sth->finish;

&backToMain;

}
###############################################################################
sub delete {

my ($item, $quotedString);

$item = param('item');
$quotedString = $dbh->quote($item);

$sth = $dbh->prepare("DELETE FROM $tableName
                      WHERE $sortField=$quotedString");
$sth->execute;
$sth->finish;

print
   b("Delete"), 
   hr,
   "The entry of ", b("$quotedString"), " has been deleted.";

&backToMain;

}
###############################################################################
sub tableStructure {

my ($fields, $types, $null, $length, $numOfFields, $field);

my %SQLTYPE = (
   '1', 'SQL_CHAR',
   '2', 'SQL_NUMERIC',
   '3', 'SQL_DECIMAL',
   '4', 'SQL_INTEGER',
   '5', 'SQL_SMALLINT',
   '6', 'SQL_FLOAT',
   '7', 'SQL_REAL',
   '8', 'SQL_DOUBLE',
   '9', 'SQL_DATE',
  '10', 'SQL_TIME',
  '11', 'SQL_TIMESTAMP',
  '12', 'SQL_VARCHAR',
  '-1', 'SQL_LONGVARCHAR',
  '-2', 'SQL_BINARY',
  '-3', 'SQL_VARBINARY',
  '-4', 'SQL_LONGVARBINARY',
  '-5', 'SQL_BIGINT',
  '-6', 'SQL_TINYINT',
  '-7', 'SQL_BIT',
  '-8', 'SQL_WCHAR',
  '-9', 'SQL_WVARCHAR',
 '-10', 'SQL_WLONGVARCHAR'
);

$sth = $dbh->prepare("SELECT *
                      FROM $tableName");
$sth->execute;

$fields = $sth->{NAME};
$types = $sth->{TYPE};
$null = $sth->{NULLABLE};
$length = $sth->{PRECISION};
$numOfFields = $sth->{NUM_OF_FIELDS};

$sth->finish;

print
   b("Online Table Structure"),
   hr,
   "Database name: ", b("$db"), br,
   "Table name: ", b("$tableName"), p;

print "<TABLE BORDER>
       <TR BGCOLOR=gray><TH>Field<TH>Type<TH>Size<TH>Null</TR>";
foreach $field (0..$numOfFields - 1) {
   print "<TR>";
   print "<TD>@$fields[$field]
          <TD>$SQLTYPE{@$types[$field]}
          <TD>@$length[$field]
          <TD>";
   if (@$null[$field]) { print 'Yes'; } 
   else { print 'No';} 
   print "</TR>";
}
print "</TABLE>";

&backToMain;

}
###############################################################################
sub blank {

my $blank_field;
my @blank_field;

print h3("Oops!"),
   "The following ", b("required"), " field(s) appears to be blank.", br,
   "Please hit the BACK button of your browser and fill out the following ",
   "required field(s).", 
   p;

unless (param($requiredField1)) { push @blank_field, $requiredField1; }
unless (param($requiredField2)) { push @blank_field, $requiredField2; }
unless (param($requiredField3)) { push @blank_field, $requiredField3; }

foreach $blank_field(@blank_field) {
   print b($blank_field), br;
}

&backToMain;

}
###############################################################################
sub backToMain {

print
   start_form(-action=>$cgi,
              -method=>'POST'),
   submit(-value=>'Back to Main'),
   end_form();

}
###############################################################################
sub copyright {
 
print
   hr,
   "Powered by ",
   a({-href=>"http://krypton.mnsu.edu/~sahedm/www/cgi/"},
   b("guestBook.cgi")), br,
   hr;

}
###############################################################################


