|
|
|
Write a Guestbook!
Introduction
OK, so there are many free providers of guestbooks out there, so
why on earth would you want to write your own?
Well, there are a number of reasons, including:-
- It's a good, relatively simple project that will introduce
you to the concept of server-side coding using PHP and MySQL.
- It means you can say that you wrote that part of your website
yourself!
- You can have your guestbook look exactly as you want it - no
advertising, no undesirable links back to the hosting site, no
learning how to customise that particular guestbook, and the like.
- If you decide to use frames for your site, you can specify in
the coding here that you want linked sites to open fresh windows,
or throw away your frameset totally. It is usually considered bad
form to "frame" someone's site in your frameset, but
I have seen some guestbooks do exactly that when linking to
external sites.
- You can obfuscate people's e-mail addresses but still make them
contactable through a clickable link. This is very important in this
day and age when there are programs out there harvesting millions and
millions of e-mail addresses for spamming purposes. You can shield
your visitors from being harvested from your site.
Please see my article about spam (new window)
for more information regarding this problem.
You can probably think of more reasons than I have outlined here.
In this article, I have been fairly basic in terms of what I have
implemented, so be creative! Put something in that will make
your guestbook yours, and not a clone of mine! You'll soon
get a feel for what can and cannot be done.
This tutorial is intended for those who already have
a working knowledge of HTML, although I am going to prefer XHTML.
One day I'll write an article on converting from HTML 4.0 Transitional
to XHTML 1.0 Transitional - it really is not too difficult. Having
said that, those having a good working knowledge of HTML will see
that the main differences are that code must be well-formed, i.e.
all tags must be closed in the correct order, and that all tags must
be closed, even those that do not have closing tags in HTML. This is
why some of the tags (such as <br> becomes <br /> - the
space follwed by '/'
is used to denote the end tag.
Although some elements and tags will be
explained, it will be generally assumed that you are able to
write a simple page of HTML yourself.
So, with no further ado, let's get on with it!
Decide what you want
Before you even turn your computer on to start coding, think about
how you want your guestbook to look. Think about how you want it
to feel to the users who will use it. Scribble a layout out on
paper, play with different backgrounds and colours, font styles
and the like.
This step is very important as it will affect the design phase, and
if you don't know what your end result is going to be like how can
you write the code for it?
There is very little I can say here because everyone will want
something slightly different; I'm not here to tell you what to
do, instead I'm here to give you some technical guidelines on
how to do it.
Design the user input form
This will be a standard HTML form that the user will fill out
when he or she clicks on your "Sign my Guestbook" form.
Basically, what we will do here is to have a basic form that will
ask for the information we require, and then eventually this data
will be sent to the guestbook database.
Let's say that we want to collect the following data:
- Name
- E-mail address
- Website address
- Verdict - radio buttons giving a general impression of the site
- Comments
It is important to decide this information now for two reasons: firstly
it will affect the form we are about to build, and secondly it
will affect the fields, or columns, that we store in the database table.
Let's design a form that will look like this as far as the user input
goes:-
I'm only showing the user interface area as everything else will be
dependent on your site - the header, the footer, the navigation etc.
Remember that everything here will form a complete working project
within the framework of your own site (i.e. HTML document type lines,
document headers, and styling are all omitted), but it is still only an
example of what you can do. You can see an example of what this
can look like on this site by signing my
guestbook.
You'll notice that some letters are underlined in the prompts. This
is because I will be using "accelerator" keys - keys you can
press with the ALT key to move the cursor directly to the field. There
is an HTML element that permits this, and we'll see it in a little
while. Note that this functionality does not work in certain older
browsers though, such as Netscape Navigator 4.
Here is the code that produces the form layout above; this code can be placed
into a file called feedback.php (remember that the HTML document type, header
tags, etc. are not included):
<div align="center"><center>
<form method="post" action="gbookin.php">
<table border="1" cellpadding="5" bgcolor="#ffffcc"><tbody>
<tr>
<td><u>N</u>ame</td>
<td><input type="text" name="guestname" size="60" maxlength="60" accesskey="N" /></td>
</tr>
<tr>
<td>E-ma<u>i</u>l</td>
<td><input type="text" name="guestemail" size="60" maxlength="60" accesskey="I" /></td>
</tr>
<tr>
<td><u>W</u>ebsite</td>
<td>
<input type="text" name="guestsite" size="60" maxlength="60" value="" accesskey="W" />
</td>
</tr>
<tr>
<td colspan="2">
<div align="center"><center>
<table border="1" cellpadding="5" bgcolor="#ffff99"><tbody>
<tr>
<td align="center">What do you think<br />of this website?</td>
<td align="center">
<input type="radio" name="verdict" value="U" accesskey="u" />It s<u>u</u>cks
</td>
<td align="center">
<input type="radio" name="verdict" value="O" checked="checked" accesskey="O" />
It's <u>O</u>K
</td>
<td align="center">
<input type="radio" name="verdict" value="C" accesskey="c" />It ro<u>c</u>ks</td>
</tr>
</tbody></table>
</center></div>
</td>
</tr>
<tr>
<td colspan="2">
<center>Please enter any co<u>m</u>ments in the box below<br />
<textarea name="comments" rows="5" cols="56" accesskey="m"></textarea>
</center>
</td>
</tr>
<tr>
<td colspan="2">
<center>
<input type="submit" name="submit" value="Sign it!" />
<input type="reset" name="reset" value="Clear it!" />
</center>
</td>
</tr>
</tbody>
</table>
</form>
</center></div>
|
This code is valid XHTML 1.0 Transitional, so if you are building your site
to conform to that standard you should have no problems in copying and
pasting that code directly.
Just one or two things to point out here:
- It is the "accesskey" attribute that lets you use the accelerator keys.
The actual letters on the form must be underlined by way of <u> and </u> tags;
just because they are specified as access keys does not make them display underlined
automatically.
- The form has 'method="post"'; there are two main ways of passing information
from page to page in PHP, one is called Get and the other is called Post. Get is beyond
the scope of this article, as is an explanation of how Get and Post both work. But in
your form it must be post, as in this example.
- The 'action="gbookin.php"' section specifies what will happen once the user
has pressed the Send It button. It will take you to the page named - in this case
gbookin.php held in the same path (directory) as this feedback form page (unless
explicitly otherwise specified by giving the path in full).
- The names of the enterable fields are passed through as the variable names in PHP
when the guestbook entry is added. For example, the name field is entered via a
field called guestname, and when we come to pick this up for adding to the guestbook
it will be called $guestname. This happens because we are using the 'post' method outlined
earlier - more of that later.
Creating the Guestbook database table
OK, really you should design the data layout before anything else, but I wanted to build
the form up first for two reasons: firstly (in this case) it defines the data that we
want to store anyway, and secondly so we could begin to see some fruits of our labours
before getting very far!
You can probably use some kind of graphical user interface to create the table, depending
on how your hosting service works here. This will not be put into a webpage - this is
something you will do once when you set the system up.
Almost certainly there will be provision to
create the database table using MySQL syntax, so here is a create statement you might
want to use, after any appropriate modifications:
CREATE TABLE guestbook
(guestid INT(6) UNSIGNED NOT NULL AUTO_INCREMENT,
guestname VARCHAR(60),
guestemail VARCHAR(60),
guestsite VARCHAR(60),
verdict VARCHAR(1),
comments TEXT,
visitdate DATETIME NOT NULL,
PRIMARY KEY (guestid),
UNIQUE INDEX guestbook_ux1 (guestid));
|
Basically, we want to create a table called 'guestbook', and it will have a total of seven
fields, or columns in it. The first one is a special unique sequence number that will
uniquely identify any record (row) that is created. We are specifiying that it must
always have something in it (i.e. never be null (NOT NULL)), and that it will be
automatically incremented every time a new row is added.
The other columns are the data we are going to store, as per the form we have just
designed, plus the date / time of the user's visit.
Finally, the guestid column has been specified as the primary and unique key. Every
MySQL database table needs a primary key, and it will usually be a unique key. It is
my considered opinion that every database table you create should have at least one
unique key so any given row can be individually identified as required - failing to do
this may result in problems in more complex applications where it is simply not
possible to guarantee that exactly one row will be returned where required, and complex
work-around code then gets written to compensate for this lack of design thought.
In this case, we only have this one table, and it's a very simple application so no
problems should arise ... we hope!
If you enter syntax directly to create the table, it's a good idea to save what you did
into a text file for future reference. This way, if you need to check what columns you
defined and how you defined them, you have the source text available at hand, and
also if there is a problem and you mistakenly drop (delete) the table, you can at
least easily recreate it (even though all your data will be lost).
Picking up your user input
The variables are passed through directly from the form (provided the PHP server setting
REGISTER_GLOBALS is set). Thus a field entered called
"guestname" can be referenced by the variable name $guestname, and so on.
If REGISTER_GLOBALS is not set, you must access the variables using the construct
$guestname = $_POST['guestname'];
|
This second method will always work, whereas the first method will only work with
REGISTER_GLOBALS set. Therefore, the second method is preferrable.
Inserting the data into the Guestbook and displaying it
Now we have the database table and the input form. When the user clicks the 'Sign it!'
button, it would be really nice if the data entered was put into the guestbook table,
and the user transferred to a page where he/she can see their own entry, placed above
the other entries that are already there. So, how do we do this?
We have already seen that as soon as the Sign It button is clicked, the next thing
that will happen is that the page specified in the form's action will be loaded.
In this case it's guestin.php. It's this page that will do the insertion of the
guestbook entry into the database table and display the guestbook's contents.
So, in guestin.php, here is what we need to do (remember that all PHP scripting
must be enclosed in <?php and ?> tags):-
-
Connect to the database.
We can use the following piece of PHP script to do this:-
mysql_connect(localhost,"username","password");
@mysql_select_db("dbname") or die( "Unable to select database");
|
The dbname is the name of your database; when you first set up your database
you'll need to give it a name. Typically it must not have the same name as
any other database hosted on that server, and any attempt to create a database
with the same name as another will fail. If this happens, choose another
database name. Once you've set up the database and given it a name, that's the
name you use here.
The username and password are a username and password that you have set up for this
database; any username and password that have the required permissions is acceptable
here. When you first set your database up, you will have created at least one
user, and you may use that user. Don't worry about the password being in plain text
here because this script is held in a file with a .php extension, thus will always
be executed on the server - you cannot see the source code if you try to
download it from the server because it will execute instead!
-
Insert the guestbook entry.
This is the 'guts' of what we are about here. The following statement will
insert a new row into the guestbook table, the table we created earlier.
// Get current date and time and plug into a variable
$result = mysql_query("select now() visitdate");
$visitdate = mysql_result($result, 0, "visitdate");
// Insert the values into the guestbook
$guestname = $_POST['guestname'];
$guestemail = $_POST['guestemail'];
$guestsite = $_POST['guestsite'];
$verdict = $_POST['verdict'];
$comments = $_POST['comments'];
$query = "INSERT INTO guestbook VALUES
('', '$guestname', '$guestemail',
'$guestsite', '$verdict',
'$comments', '$visitdate')";
mysql_query($query); // Insert values into the database
|
Hopefully, that's fairly self-explanatory. However, just to point out a few
things:
Firstly, we want to get the date and time
of the visitor's entry. We do this by way of 'SELECT now() visitdate'.
The SELECT statement is passed to MySQL by way of the mysql_query function, and the
result of the function is returned to the PHP variable $result.
The name visitdate is a column alias - I've chosen to use this to keep
variable names and the like consistent throughout the application. Note that there
is not a comma between the now() and the visitdate - it is this which tells MySQL
that visitdate is a column alias (another name by which another column is to be
referred to on a temporary basis) and not a column in its own right.
The variables themselves are picked up from the form by way of $_POST[...] ...
note that the names given in the brackets are the same as those used on the form.
On that, please note that I have chosen to use the same names on the form, as the
variables, and on the database. This is not essential, but it makes tracking what's
going where much easier when it comes to debugging!
The INSERT statement breaks down as follows:-
-
You tell it to INSERT INTO the required table, in this case guestbook.
-
You can specify which columns you will be inserting if you are not inserting every
column. In this case, we want to put something into each and every column so there
is no need to do this.
-
You specify the VALUES you want to insert. The actual values themselves come as
a set specified in round brackets, as in the example.
-
The actual values themselves come next, in the order specified when the table was
created or in the order given in the second bullet point above, if present.
The single quotes around the values are just that - text - and will serve to place
the data from the variables into those quote marks. All string values in MySQL
must be enclosed within single quote marks, and this is precisely what this is doing.
-
Note the '' for the first column ... what's that all about?
It's because that first column was specified to be an auto-incremented column that will
therefore be set up automatically by MySQL when new rows are added. We do
not need to put anything in it, in fact must not put anything in it, but it is required.
-
Display the guestbook contents.
The following code can be used to display the guestbook. It is not yet finished, but
it will do for now:-
<div align="center"><center>
<font color="ff0000" size="4">Guestbook</font>
<br /><br />
<?php
/*
// Get the guestbook data in so it can be
// presented as an XHTML table
*/
$result = mysql_query("select guestname, guestemail, guestsite
verdict, comments, visitdate
from guestbook
order by guestid desc");
$numrows = mysql_numrows($result);
for ($i = 0; $i < $numrows; $i ++)
{
$guestname = mysql_result($result, $i, "guestname");
$guestemail = mysql_result($result, $i, "guestemail");
$guestsite = mysql_result($result, $i, "guestsite");
$verdict = mysql_result($result, $i, "verdict");
$comments = mysql_result($result, $i, "comments");
$visitdate = mysql_result($result, $i, "visitdate");
?>
<table border="1" cellpadding="5" bgcolor="#ffffcc">
<tr>
<td width="50%" bgcolor="#ffff99">
<?php echo "$visitdate\n"; ?>
</td>
<td width="50%" bgcolor="#ffff99">
<?php
if ($guestsite == "")
{
echo "No website supplied";
}
else
{
echo nl2br(htmlspecialchars(stripslashes($guestsite), ENT_QUOTES));
}
?>
</td>
</tr>
<tr>
<td width="50%" bgcolor="#ffff99">
<?php
echo nl2br(htmlspecialchars(stripslashes($guestname), ENT_QUOTES));
?>
</td>
<td width="50%" bgcolor="#ffff99">
<?php
if ($verdict == "U" || $verdict == "u")
{
echo "It sucks\n";
}
else if ($verdict == "C" || $verdict == "c")
{
echo "It rocks\n";
}
else
{
echo "It's OK\n";
}
?>
</td>
</tr>
<tr>
<td colspan="2">
<p align="center">
<?php
if ($comments == "")
{
echo "(No comment made)";
}
else
{
echo nl2br(htmlspecialchars(stripslashes($comments), ENT_QUOTES));
}
?>
</p>
</td>
</tr>
</table>
<br /><br />
<?php
}
?>
</center></div>
|
Note that this code is always dipping in and out of PHP, with static XHTML embedded
within it.
-
Disconnect from the database.
This is very important, as it can clog the server up to leave database connections
open all over the place. It's also very easy to do:-
mysql_close(); /* Close connection to database! Very important! */
|
That's all there is to it!
So we can put all that together into a complete document, and when the guestbook
is signed, the new entry is entered and all the entries displayed in reverse
chronological order (i.e. most recent first).
Displaying the Guestbook without Signing It
So far, the guestook can only be viewed immediately after it has been signed. That's
not much good, is it?! We need to be able to view it without forcing it to be signed
at the same time.
This one is easy to solve. Just have another page, for example called gbook.php, and
let it only contain the code to connect to the database, display the guestbook, and
disconnect from the database afterwards. In other words, it's a copy of the page
that you get after signing the guestbook but without the code to insert a new entry.
That's simple enough to do, but there's a drawback. Supposing you want to change the
display code, perhaps to make a change, an improvement, or fix a bug? You now have to
make the change in two places! Not nice, and if there's one thing that most programmers
hate intensely is having to make their changes more than once! So, how do we get round
this?
The answer lies in "include files". We can take the piece of PHP that we want
to re-use, and place it into a separate file all by itself. We then include it at the
relevant points.
So, in this example, we can take the code that displays the guestbook and place it
into a new file, called gbookvu.php for example. This code therefore resides in a file
by itself, with nothing else in it at all:-
<?php ?>
<div align="center"><center>
<font color="ff0000" size="4">Guestbook</font>
<br /><br />
<?php
/*
// Get the guestbook data in so it can be
// presented as an XHTML table
*/
$result = mysql_query("select guestname, guestemail, guestsite,
verdict, comments, visitdate
from guestbook
order by guestid desc");
$numrows = mysql_numrows($result);
for ($i = 0; $i < $numrows; $i ++)
{
$guestname = mysql_result($result, $i, "guestname");
$guestemail = mysql_result($result, $i, "guestemail");
$guestsite = mysql_result($result, $i, "guestsite");
$verdict = mysql_result($result, $i, "verdict");
$comments = mysql_result($result, $i, "comments");
$visitdate = mysql_result($result, $i, "visitdate");
?>
<table border="1" cellpadding="5" bgcolor="#ffffcc">
<tr>
<td width="50%" bgcolor="#ffff99">
<?php echo "$visitdate\n"; ?>
</td>
<td width="50%" bgcolor="#ffff99">
<?php
if ($guestsite == "")
{
echo "No website supplied";
}
else
{
echo nl2br(htmlspecialchars(stripslashes($guestsite), ENT_QUOTES));
}
?>
</td>
</tr>
<tr>
<td width="50%" bgcolor="#ffff99">
<?php
echo nl2br(htmlspecialchars(stripslashes($guestname), ENT_QUOTES));
?>
</td>
<td width="50%" bgcolor="#ffff99">
<?php
if ($verdict == "U" || $verdict == "u")
{
echo "It sucks\n";
}
else if ($verdict == "C" || $verdict == "c")
{
echo "It rocks\n";
}
else
{
echo "It's OK\n";
}
?>
</td>
</tr>
<tr>
<td colspan="2">
<p align="center">
<?php
if ($comments == "")
{
echo "(No comment made)";
}
else
{
echo nl2br(htmlspecialchars(stripslashes($comments), ENT_QUOTES));
}
?>
</p>
</td>
</tr>
</table>
<br /><br />
<?php
}
?>
</center></div>
<?php ?>
|
Now we replace that selfsame code in the gbookin.php file with the following:-
<?php
include("gbookvu.php");
?>
|
Now it's a good idea to copy gbookin.php to gbook.php (view-only version) and
remove the insert code.
So at this stage we have a working guestbook! It is spread out across 4 files and one
MySQL database. The files are: feedback.php (to collect the guestbook data),
gbookin.php (to insert the entry and display the guestbook), gbook.php (to view the
guestbook only), and gbookvu.php (the actual code to display guestbook entries).
We have got the complete file for gbookvu.php listed above, so for completeness
here is the code for gbookin.php:-
/* Connect to the database */
mysql_connect(localhost,"username","password");
@mysql_select_db("dbname") or die( "Unable to select database");
/* Insert guestbook entry */
// Get current date and time
$result = mysql_query("select now() visitdate");
// Plug it into a variable
$visitdate = mysql_result($result, 0, "visitdate");
$guestname = $_POST['guestname'];
$guestemail = $_POST['guestemail'];
$guestsite = $_POST['guestsite'];
$verdict = $_POST['verdict'];
$comments = $_POST['comments'];
$query = "INSERT INTO guestbook VALUES
('', '$guestname', '$guestemail', '$guestsite',
'$verdict', '$comments', '$visitdate')";
mysql_query($query); // Insert values into the database
/* Display the guestbook by way of include file */
include("gbookvu.php");
/* Disconnect from the database */
mysql_close(); /* Close connection to database! Very important! */
|
gbook.php will be identical to this except that there will be no code to
insert values into the guestbook.
Making the Website Links Work
Note that the website URLs, if given, are not displayed as links. This means that
they are not clickable. Fat lot of good having links to websites that are not links
at all! What we need to do is modify the code that outputs them to make them
into hyperlinks.
Find the following code in the view guestbook include file:
if ($guestsite == "")
{
echo "No website supplied";
}
else
{
echo nl2br(htmlspecialchars(stripslashes($guestsite), ENT_QUOTES));
}
|
and replace it with this:-
if ($guestsite == "")
{
echo "No website supplied";
}
else
{
echo "<a href=\"$guestsite\" target=\"_top\">";
echo nl2br(htmlspecialchars(stripslashes($guestsite), ENT_QUOTES));
echo "</a>";
}
|
That's all there is to it! Just a one line change!
Test it out, and you'll see that the websites now become
links. The 'target="_top"' will force the site to use the full browser window
when opened, i.e. escape any frameset that your site might be using. You can use _blank
instead of _top to cause the website to open in a new window. Doing one or the other is
highly recommended, even if you are not using frames, because you may well make a future
modification to your site that does involve frames, and you'll never know you've got
the error until you discover it by mistake!
Making the Date and Time of visit look better
MySQL outputs dates and times in the following format:-
2003-07-21 08:45:39
That is, year-month-day, hours, minutes, and seconds. What would be nicer
is a display such as the following:
21st July 2003 08:45
We don't want to see 21/07/2003 because it depends on which side of the Atlantic you are
as to what it means. In the UK it means the 21st of the 7th month, 2003, whereas in the
USA is means the 21st month, 7th day, 2003. OK, in this example it's obvious because there
is no such thing as month 21. But supposing the date was 06/07/2003? Is that the sixth
of July 2003 or June the 7th 2003? That's going to
be mighty ambiguous, and as this site specialises in extradisambiguation, we need to
ensure that that dates are fully disambiguated. So, let's modify the code to do it!
Find the line that outputs the date and time:
<td width="50%" bgcolor="#ffff99"><?php echo "$visitdate\n"; ?></td>
|
...and replace it with code such as the following:
<td width="50%" bgcolor="#ffff99">
<?php
$vyear = substr($visitdate, 0, 4); /* Visit year */
$vmonth = substr($visitdate, 5, 2); /* Visit month */
$vday = substr($visitdate, 8, 2); /* Visit day of month */
$vtime = substr($visitdate, 11, 5); /* Visit time */
$vday *= 1; /* Convert to number */
$vmonth *= 1; /* Convert to number */
$stndrd = "th"; /* Assume Nth */
if ($vday == 1 || $vday == 21 || $vday == 31)
{
$stndrd = "st"; /* 1st, 21st, 31st */
}
if ($vday == 2 || $vday == 22)
{
$stndrd = "nd"; /* 2nd, 22nd */
}
if ($vday == 3 || $vday == 23)
{
$stndrd = "rd"; /* 3rd, 23rd */
}
if ($vmonth == 1) $dmonth = "January";
if ($vmonth == 2) $dmonth = "February";
if ($vmonth == 3) $dmonth = "March";
if ($vmonth == 4) $dmonth = "April";
if ($vmonth == 5) $dmonth = "May";
if ($vmonth == 6) $dmonth = "June";
if ($vmonth == 7) $dmonth = "July";
if ($vmonth == 8) $dmonth = "August";
if ($vmonth == 9) $dmonth = "September";
if ($vmonth == 10) $dmonth = "October";
if ($vmonth == 11) $dmonth = "November";
if ($vmonth == 12) $dmonth = "December";
echo "$vday$stndrd $dmonth $vyear $vtime\n";
?>
</td>
|
Hopefully there are enough comments in that to help you understand what is going
on; I know the months conversion table is somewhat clumsy so maybe it would have
been better to have used an array instead of this straight-line code!
Permitting visitors to be e-mailed
Now this one is interesting. Instead of making each name into a mailto link so
you can click on them to send an e-mail, we're going to use an e-mail form instead.
Why do this? Well, two reasons. Firstly, not everybody uses e-mail software, thus
clicking on a mailto link will have no effect or will attempt to fire up a program that
is not used on that machine, and secondly (far more serious) is having e-mail addresses
posted for all and sundry to see makes them available to spambots, programs that scour
the Web looking for e-mail addresses to which they can send spam, or junk e-mail.
Everybody hates spam, except for the people who send it and the few who
actually encourage the practice by purchasing from spammers, so this is a good
trick to hide e-mail addresses away such that they can never be harvested in this
manner. Nice, eh?
Where an e-mail address has been given, the name will be clickable. A form
will then open up that lets you send e-mail. The guestid of the person
to whom you are sending mail will be passed over using the GET method so the form
knows which e-mail address to use.
In the guestbook view code, locate the following code fragment:-
$result = mysql_query("select guestname, guestemail, guestsite,
verdict, comments, visitdate
from guestbook
order by guestid desc");
$numrows = mysql_numrows($result);
for ($i = 0; $i < $numrows; $i ++)
{
$guestname = mysql_result($result, $i, "guestname");
|
and replace it with the following:-
$result = mysql_query("select guestid, guestname, guestemail,
guestsite, verdict, comments,
visitdate
from guestbook
order by guestid desc");
$numrows = mysql_numrows($result);
for ($i = 0; $i < $numrows; $i ++)
{
$guestid = mysql_result($result, $i, "guestid");
$guestname = mysql_result($result, $i, "guestname");
|
This merely includes the guest ID in the select statement so it's available
for use as a link from the guest's name.
Secondly, find the code that displays the guest's name:
<td width="50%" bgcolor="#ffff99">
<?php
echo nl2br(htmlspecialchars(stripslashes($guestname), ENT_QUOTES));
?>
</td>
|
and replace it with something such as this:
<td width="50%" bgcolor="#ffff99">
<?php
if ($guestemail != "")
{
/* E-mail address given. Make a link using guestid, NOT e-mail address! */
echo "<a href=\"email.php?guestid=$guestid\" target=\"_top\">";
echo nl2br(htmlspecialchars(stripslashes($guestname), ENT_QUOTES));
echo "</a>\n";
}
else
{
/* No e-mail address supplied */
echo nl2br(htmlspecialchars(stripslashes($guestname), ENT_QUOTES));
}
?>
</td>
|
Where no e-mail address has been given, the name is displayed as plain text. Where
an e-mail address has been given, the name is made into a link to a page called
email.php. Note the '?guestid=$guestid' after it - it is this that forms the
"GET" method that will pass the guest ID through to the e-mail form page
(email.php).
That's great .... terrific! Now we need to write the code in email.php to
pick this up, allow a message to be entered, and an e-mail to be sent!
Handling the e-mail form
This is probably the trickiest part of the whole project. I'm going to have
a new page, email.php, that will display the form and process the input
entered thereon. Here is the entire active piece of code:
<div align="center"><center>
<?php
$gcount = count($_GET); // Non-zero if come from guestbook
$pcount = count($_POST); // Non-zero if come from this same page
if ($gcount > 0)
{
$guestid=$_GET['guestid']; /* Get the guestID from the URL */
$questid *= 1; // Make sure it's a number
}
else
{
$guestid = 0; // POST method used, or an error has occurred
}
if ($guestid > 0)
{
$query = "select guestname, guestemail
from guestbook
where guestid=".$guestid;
$result = mysql_query($query); // Get required Guestbook entry
$numrows = mysql_numrows($result); // Ensure sensible result
if ($numrows >= 1)
{
// We have a row: get its details
$guestname = mysql_result($result, 0, "guestname");
$guestemail = mysql_result($result, 0, "guestemail");
}
else
{
$guestid = 0; // Use guestid as a flag to indicate duff
}
}
if ($guestid > 0)
{
// Have a sensible result - build form to send e-mail from
?>
<font color="ff0000" size="4">Send e-mail to
<?php
echo htmlspecialchars(stripslashes($guestname), ENT_QUOTES);
?>
</font><br /><br />
<form method="post" action="<?php echo "$php_self"; ?>">
<table border="1" cellpadding="5" bgcolor="#ffffcc"><tbody>
<tr>
<td>Your <u>N</u>ame</td>
<td><input type="text" name="yourname" size="60" maxlength="60" accesskey="N" /></td>
</tr>
<tr>
<td>Your E-ma<u>i</u>l</td>
<td><input type="text" name="youremail" size="60" maxlength="60" accesskey="I" /></td>
</tr>
<tr>
<td colspan="2">
<center>Please enter your <u>M</u>essage here<br />
<textarea name="message" rows="5" cols="56" accesskey="m">
</textarea>
</center>
<input type="hidden" name="guestid" value="<?php echo "$guestid"; ?>" />
</td>
</tr>
<tr>
<td colspan="2">
<center>
<input type="submit" name="submit" value="Send it!" />
<input type="reset" name="reset" value="Clear it!" />
</center>
</td>
</tr>
</tbody>
</table>
</form>
<?php
}
else
{
// No rows returned;
// either we have an error or it's the POST route
if ($pcount > 0)
{
// POST used; send the e-mail
$numrows = 1; /* Assume it will be good */
$yourname = $_POST['yourname'];
if ($yourname == "")
{
echo "You have not given your name! No e-mail will be sent<br />\n";
$numrows = 0; /* Bu hao */
}
$youremail = $_POST['youremail'];
if ($youremail == "")
{
echo "You have not given your e-mail address! No e-mail will be sent<br />\n";
$numrows = 0; /* Bu hao */
}
else
{
if (!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $youremail))
{
echo "Your e-mail address is not valid! No e-mail wil be sent<br />\n";
$numrows = 0; /* Bu hao */
}
}
$message = $_POST['message'];
if ($message == "")
{
echo "No message has been entered. No e-mail will be sent<br />\n";
$numrows = 0; /* Bu hao */
}
if ($numrows == 1)
{
$guestid = $_POST['guestid'];
$query = "select guestname, guestemail from guestbook where guestid=".$guestid;
$result = mysql_query($query); // Get the required Guestbook entry
$numrows = mysql_numrows($result); // Ensure we have a sensible result
}
if ($numrows >= 1)
{
// We have a row: get its details
$guestname = mysql_result($result, 0, "guestname");
$guestemail = mysql_result($result, 0, "guestemail");
$from = "From: \"".$yourname."\" <".$youremail.">\n";
$sendit = mail($guestemail, "Contact from Extradisambiguator guestbook", $message, $from);
if ($sendit)
{
echo "Message sent successfully!\n";
}
else
{
echo "Your message was not sent. ";
echo "Error code $sendit returned\n";
}
}
else
{
echo "Unfortunately an error occurred; the e-mail could not be sent.\n";
}
}
else
{
// No rows returned; we have an error
echo "Unfortunately an error has occured, and no e-mail can be sent.\n";
}
?>
<br /><br />
Please <a href="gbook.php">click here</a>
to return to the Guestbook
<br /><br />
<?php
}
?>
</center></div>
|
I'm not going to give a blow-by-blow account of what each line is doing, but
rather give an overview of what is going on so you can gain an understanding.
Firstly, we need to see how many _GET and _POST entries there are. Either one
or the other will be non-zero; they will not both be zero or both non-zero.
If the number of _GETs is non-zero, this indicates we have come in from the
guestbook, whereas if the number of _POSTs is non-zero, this implies that
the Send e-mail button has been clicked. The required behaviour is entirely
different for each path.
If we have come in from the guestbook (number of _GETs is greater than zero),
then we need to get the guestid from the addressbar (the page will have been
called with "?guestid=99" - that numeric value represented by 99
will come in via $_GET['guestid'].
This can then be used to pick up details for the person to whom an e-mail
is to be sent from the database - guestid is a unique key so we should get
one, and only one, row when the database is queried. If there is an
error, set the variables to indicate the fact and drop out.
Normal HTML (XHTML) can then be used to display the e-mail form; there
is nothing new here except for the action of the form. Here, I want to come
back to the selfsame page once the "Send It" button is clicked,
and we can either do this by explicitly giving the name of this page again
or by echoing the server variable $SERVER['PHP_SELF'], which contains the name of
the current page. This is preferrable because if the name of this page
is changed, you don't have to hunt for this to change it also.
When the "Send It" button is clicked, the page is re-entered
but this time the number of _POST entries will be greater than zero, while
the number of _GET entries will be zero. This can be used to indicate
that the page has been re-entered. Clever, no?
Now some variables are set up that will enable us to send the required
e-mail: $_POST['yourname'] will contain the name of the person sending
the mail, $_POST['youremail'] will contain that person's e-mail address,
and so on. These have come in from the e-mail form that was just
filled in, and is exactly the same technique that was used for picking up
the guestbook entry details for insertion into the guestbook.
There is a degree of validation going on to make sure the person filling up the
form provides a name and e-mail address as well as a message. It is thus
possible to prevent someone from sending silly empty e-mails. Furthermore,
a modicum of e-mail address validation is performed (see that horrendous line
using the PHP eregi() function - it is well beyond the scope of this article to
provide a discussion on 'regular expressions'). However, the manner in which
the validation is performed here is not ideal as it does not allow the user to go
back and correct mistakes. This will be left, as they say, as an exercise for the
reader; it involves a degree of re-arranging the code and judicious setting of
the flags to get this to work properly.
Finally, once all the data has been collected, we can use
the PHP mail() command to send the message itself!
The mail() command takes 4 parameters:
- The e-mail address of the person to whom the message is to be sent
- The subject of the e-mail. In this case we're using fixed text
- The actual message body itself
- The headers, usually the name and e-mail address of the sender.
Note the "\n" at the end of this field; it is essential that this is
included. I've used the format 'From: "NAME" <EMAILADDR>\n'
here. If you omit the "\n" at the end, your message will fail.
The result is tested, and an appropriate message output to indicate whether
the message was sent successfully or not.
Finally, we have a clickable link to let the visitor return to the guestbook.
Hardly rocket science!
Validating the Guestbook Data
We need to carry out some basic validity checks on the Guestbook data input.
Basically this means making sure the user has entered a name, some comments, and
if an e-mail address has been entered that it looks valid. If anything is missing
or considered to be duff, then we ought to re-present the form to the user so
appropriate modifications can be made.
Unfortunately, this is going to be a bit tricky. This is partly because of the
way the project has been designed so-far, so this will have to be shoehorned in
using the cleanest methods possible. Let's tackle it this way:
The file feedback.php contains the XHTML to input the form and send it off for
processing. This is the page that contains the following XHTML code:
<div align="center"><center>
<form method="post" action="gbookin.php">
...
...
</form>
</center></div>
|
The two main changes we need to make to the form are (a) pre-prompt any values
that have already been entered and (b) display a one-line error message explaining
what was wrong.
To this end, let's modify the whole form as follows:
<div align="center"><center>
<?php
$selected = "selected=\"selected\"";
if (!isset($guestname)) $guestname = "";
if (!isset($guestemail)) $guestemail = "";
if (!isset($guestsite)) $guestsite = "";
if (!isset($verdict)) $verdict = "O";
if (!isset($comments)) $comments = "";
if (!isset($errmes)) $errmes = "";
if ($errmes != "") echo "$errmes<br />"; // Output any error message present
?>
<form method="post" action="gbookin.php">
<table border="1" cellpadding="5" bgcolor="#ffffcc"><tbody>
<tr>
<td><u>N</u>ame</td>
<td><input type="text" name="guestname" size="60" maxlength="60" accesskey="N" value="<?php echo "$guestname"; ?>" /></td>
</tr>
<tr>
<td>E-ma<u>i</u>l</td>
<td><input type="text" name="guestemail" size="60" maxlength="60" accesskey="I" value="<?php echo "$guestemail"; ?>" /></td>
</tr>
<tr>
<td><u>W</u>ebsite</td>
<td>
<input type="text" name="guestsite" size="60" maxlength="60" value="" accesskey="W" value="<?php echo "$guestsite"; ?>" />
</td>
</tr>
<tr>
<td colspan="2" >
<div align="center"><center>
<table border="1" cellpadding="5" bgcolor="#ffff99"><tbody>
<tr>
<td align="center">What do you think<br />of this website?</td>
<td align="center">
<input type="radio" name="verdict" value="U" accesskey="u" <?php if ($verdict == "U") echo "$selected"; ?> />It s<u>u</u>cks
</td>
<td align="center">
<input type="radio" name="verdict" value="O" checked="checked" accesskey="O" <?php if ($verdict == "O") echo "$selected"; ?> />
It's <u>O</u>K
</td>
<td align="center">
<input type="radio" name="verdict" value="C" accesskey="c" <?php if ($verdict == "C") echo "$selected"; ?> />It ro<u>c</u>ks</td>
</tr>
</tbody></table>
</center></div>
</td>
</tr>
<tr>
<td colspan="2">
<center>Please enter any co<u>m</u>ments in the box below<br />
<textarea name="comments" rows="5" cols="56" accesskey="m"><?php echo "$comments"; ?></textarea>
</center>
</td>
</tr>
<tr>
<td colspan="2">
<center>
<input type="submit" name="submit" value="Sign it!" />
<input type="reset" name="reset" value="Clear it!" />
</center>
</td>
</tr>
</tbody>
</table>
</form>
</center></div>
|
The differences between this and the original version is the presence of some embedded PHP
within the table. Note the use of a potentially non-existent variable, $errmes. If this
exists and has something in it then it will contain an error message advising the user what
the problem is. The code to set this up is now about to be written.
In the file gbookin.php, the page that is called when the form is submitted, changes
need to be made to ascertain whether the input is valid, and if not, send the user back
to the form to make corrections. To do this, we need to pick up ans validate the user's
entry BEFORE the HTML headers are sent to the browser - this means even before the document
type is declared. Here's one way this may be achieved:
$errmes = ""; // Let's assume there will be no problems
$guestname = (isset($_POST['guestname'])) ? $_POST['guestname'] : "";
if ($guestname == "") $errmes = "You didn't enter your name!<br />\n";
$guestemail = (isset($_POST['guestemail'])) ? $_POST['guestemail'] : "";
if ($guestemail != "")
{
if (!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $guestemail))
{
$errmes = "Your e-mail address is not valid!<br />";
}
}
$guestsite = (isset($_POST['guestsite'])) ? $_POST['guestsite'] : "";
$verdict = (isset($_POST['verdict'])) ? $_POST['verdict'] : "O";
$comments = (isset($_POST['comments'])) ? $_POST['comments'] : "";
if ($comments == "") $errmes = "You didn't enter any comments!<br />\n";
if ($errmes != "")
{
include ("feedback.php");
}
else
{
// Send the document type, and HTML headers here
...
// Now insert the Guestbook entry and display it
mysql_connect(localhost,"username","password");
@mysql_select_db("dbname") or die( "Unable to select database");
// Get current date and time and plug into a variable
$result = mysql_query("select now() visitdate");
$visitdate = mysql_result($result, 0, "visitdate");
// Insert the values into the guestbook
$query = "INSERT INTO guestbook VALUES
('', '$guestname', '$guestemail',
'$guestsite', '$verdict',
'$comments', '$visitdate')";
mysql_query($query); // Insert values into the database
include("gbookvu.php");
mysql_close(); /* Close connection to database! Very important! */
// Send the end tags for the HTML body section here
...
}
|
Basically, we're picking up the user's input and validating it before committing
ourselves to anything. If the input is invalid, we re-prompt for the missing data
by including the form (now the variable $errmes which was missing before is now
present) whereas if the data was valid, we proceed as before.
Known Issues
There are a couple of problems I have yet to find a way round. As and when I
discover a solution, this tutorial will be updated to take them into account.
These are as follows:
-
On the page that inserts your guestbook entry and displays the entire guestbook,
using the browser's "refresh" button can cause you to double-post.
You may get a question asking you to re-send the data, and if you click yes you'll
get your guestbook entry posted twice! This needs to be prevented.
-
Once an e-mail has been sent, it is possible to keep clicking on the browser's
refresh button and re-sending the e-mail. Prevent this if possible.
|