require 'DB.php';
// a helpful database debugging function
function log_die($ob) { print '<pre>'; print_r($ob); print '</pre>'; }
// connect to the database
$dbh = DB::connect('mysql://test:@localhost/test') or die("Can't connect");
if (DB::isError($dbh)) { log_die($dbh); }
$dbh->setFetchMode(DB_FETCHMODE_OBJECT);
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK,'log_die');
// The value of $_REQUEST['cmd'] tells us what to do
switch ($_REQUEST['cmd']) {
case 'read': // read an individual message
pc_message_read();
break;
case 'post': // display the form to post a message
pc_message_post();
break;
case 'save': // save a posted message
if (pc_message_validate()) { // if the message is valid,
pc_message_save(); // then save it
pc_message_list(); // and display the message list
} else {
pc_message_post(); // otherwise, redisplay the posting form
}
break;
case 'list': // display a message list by default
default:
pc_message_list();
break;
}
// pc_message_save() saves the message to the database
function pc_message_save() {
global $dbh;
$parent_id = intval($_REQUEST['parent_id']);
/* MySQL syntax for making sure pc_message doesn't change while
* we're working with it. We also have to lock the tables that
* hold the thread and pc_message sequences
*/
$dbh->query('LOCK TABLES pc_message WRITE, thread_seq WRITE, pc_message_seq WRITE');
// is this message a reply?
if ($parent_id) {
// get the thread, level, and thread_pos of the parent message
$parent = $dbh->getRow("SELECT thread_id,level,thread_pos
FROM pc_message
WHERE id = $parent_id");
// a reply's level is one greater than its parents
$level = $parent->level + 1;
/* what's the biggest thread_pos in this thread among messages
with the same parent? */
$thread_pos = $dbh->getOne("SELECT MAX(thread_pos) FROM pc_message
WHERE thread_id = $parent->thread_id AND parent_id = $parent_id");
// are there existing replies to this parent?
if ($thread_pos) {
// this thread_pos goes after the biggest existing one
$thread_pos++;
} else {
// this is the first reply, so put it right after the parent
$thread_pos = $parent->thread_pos + 1;
}
/* increment the thread_pos of all messages in the thread that
come after this one */
$dbh->query("UPDATE pc_message SET thread_pos = thread_pos + 1
WHERE thread_id = $parent->thread_id AND thread_pos >= $thread_pos");
// the new message should be saved with the parent's thread_id
$thread_id = $parent->thread_id;
} else {
// the message is not a reply, so it's the start of a new thread
$thread_id = $dbh->nextId('thread');
$level = 0;
$thread_pos = 0;
}
// get a new id for this message
$id = $dbh->nextId('pc_message');
/* insert the message into the database. Using prepare() and execute()
makes sure that all fields are properly quoted */
$prh =
$dbh->prepare("INSERT INTO pc_message (id,thread_id,parent_id,
thread_pos,posted_on,level,author,subject,body)
VALUES (?,?,?,?,NOW(),?,?,?,?)");
$dbh->execute($prh,array($id,$thread_id,$parent_id,$thread_pos,$level,
$_REQUEST['author'],$_REQUEST['subject'],
$_REQUEST['body']));
// Tell MySQL that others can use the pc_message table now
$dbh->query('UNLOCK TABLES');
}
// pc_message_list() displays a list of all messages
function pc_message_list() {
global $dbh;
print '<h2>Message List</h2><p>';
/* order the messages by their thread (thread_id) and their position
within the thread (thread_pos) */
$sth = $dbh->query("SELECT id,author,subject,LENGTH(body) AS body_length,
posted_on,level FROM pc_message
ORDER BY thread_id,thread_pos");
while ($row = $sth->fetchRow()) {
// indent messages with level > 0
print str_repeat(' ,4 * $row->level);
// print out information about the message with a link to read it
print<<<_HTML_
<a href="$_SERVER[PHP_SELF]?cmd=read&id=$row->id">$row->subject</a> by
$row->author @ $row->posted_on ($row->body_length bytes)
<br>
_HTML_;
}
// provide a way to post a non-reply message
printf('<hr><a href="%s?cmd=post">Start a New Thread</a>',
$_SERVER['PHP_SELF']);
}
// pc_message_read() displays an individual message
function pc_message_read() {
global $dbh;
/* make sure the message id we're passed is an integer and really
represents a message */
$id = intval($_REQUEST['id']) or die("Bad message id");
if (! ($msg = $dbh->getRow(
"SELECT author,subject,body,posted_on FROM pc_message WHERE id = $id"))) {
die("Bad message id");
}
/* don't display user-entered HTML, but display newlines as
HTML line breaks */
$body = nl2br(strip_tags($msg->body));
// display the message with links to reply and return to the message list
print<<<_HTML_
<h2>$msg->subject</h2>
<h3>by $msg->author</h3>
<p>
$body
<hr>
<a href="$_SERVER[PHP_SELF]?cmd=post&parent_id=$id">Reply</a>
<br>
<a href="$_SERVER[PHP_SELF]?cmd=list">List Messages</a>
_HTML_;
}
// pc_message_post() displays the form for posting a message
function pc_message_post() {
global $dbh,$form_errors;
foreach (array('author','subject','body') as $field) {
// escape characters in default field values
$$field = htmlspecialchars($_REQUEST[$field]);
// make the error messages display in red
if ($form_errors[$field]) {
$form_errors[$field] = '<font color="red">' .
$form_errors[$field] . '</font><br>';
}
}
// is this message a reply
if ($parent_id = intval($_REQUEST['parent_id'])) {
// send the parent_id along when the form is submitted
$parent_field =
sprintf('<input type="hidden" name="parent_id" value="%d">',
$parent_id);
// if no subject's been passed in, use the subject of the parent
if (! $subject) {
$parent_subject = $dbh->getOne('SELECT subject FROM pc_message
WHERE id = ?',array($parent_id));
/* prefix 'Re: ' to the parent subject if it exists and
doesn't already have a 'Re:' */
$subject = htmlspecialchars($parent_subject);
if ($parent_subject && (! preg_match('/^re:/i',$parent_subject))) {
$subject = "Re: $subject";
}
}
}
// display the posting form, with errors and default values
print<<<_HTML_
<form method="post" action="$_SERVER[PHP_SELF]">
<table>
<tr>
<td>Your Name:</td>
<td>$form_errors[author]<input type="text" name="author" value="$author">
</td>
<tr>
<td>Subject:</td>
<td>$form_errors[subject]<input type="text" name="subject" value="$subject">
</td>
<tr>
<td>Message:</td>
<td>$form_errors[body]<textarea rows="4" cols="30" wrap="physical"
name="body">$body</textarea>
</td>
<tr><td colspan="2"><input type="submit" value="Post Message"></td></tr>
</table>
$parent_field
<input type="hidden" name="cmd" value="save">
</form>
_HTML_;
}
// pc_message_validate() makes sure something is entered in each field
function pc_message_validate() {
global $form_errors;
$form_errors = array();
if (! $_REQUEST['author']) {
$form_errors['author'] = 'Please enter your name.';
}
if (! $_REQUEST['subject']) {
$form_errors['subject'] = 'Please enter a message subject.';
}
if (! $_REQUEST['body']) {
$form_errors['body'] = 'Please enter a message body.';
}
if (count($form_errors)) {
return false;
} else {
return true;
}
}