Firefox PHP

Case Study: Phorum Article / Comments system

Posted by sheik 
Case Study: Phorum Article / Comments system
December 13, 2005 10:08PM
This case study is intended to help people in the future who wish to integrate elements of Phorum 5 into their own website. Experienced Phorum hackers won't find anything clever here, but it will hopefully save beginners from trawling the support forums for useful titbits of information.

Background

My client (when I say client, I actually mean friend, which translates to unpaid work - bah...) runs a new website, www.moviedeaths.com, which contains several hundred "reviews" of film deaths, stored in a MySQL database system that I wrote, allowing visitors to run such academically worthwhile queries as "which characters have died by being both electrocuted and impaled?".
He wanted a forum on the site, as well as the facility for people to add comments to each review.
The forum was no problem :-) but writing a comment system from scratch seemed a bit redundant, given the availability of the Phorum API.
My plan therefore, was to :
- create a dedicated forum for reviews
- restrict that forum to be "reply only" for everyone apart from my script (to do this just go into the Phorum admin screen, edit the forum's "public users" options and untick "Create New Topics")
- write a function that automatically inserts a new thread to the forum, every time a new death was added to the main site database.
- write a script to be run once only, that takes all the existing deaths and creates threads for them, along with the correct datestamp (when the review was written).
- write a function that can show a "This review has 'x' comments" for each death, along with a link to the relevant thread.

Phase 1 - Database Design

Users
The review database already uses a custom CMS (content management system) that I wrote, with each editor having their own secure login, enabling them to add or edit death reviews. Each editor now has a Phorum user account too, and their reviews would need to be posted using this login.
I was initially going to store a separate field next to their editor ID, called phorum_user_id. However, for various reasons I made the decision to just make the editor ID be the same as the corresponding Phorum account, meaning I will use Phorum's user management. If the CMS had been more mature I may have kept them separate.

Linking Articles to Phorum Posts
There needed to be a way of linking articles to their corresponding threads. To do this, I created a table in my CMS :

tblArticles
- article_id (the unique ID of the article)
- forum_id (the unique ID of the forum used to store the comments)
- thread_id (the unique ID of the Phorum comment thread)

For the time being, the forum_id field will be hard coded to my single review forum, but this may expand in future.
Each review will exist in Phorum as an individual thread, so I don't need to worry about message IDs (Phorum's convention is that threads with just one post have the same message ID as the thread ID)
So, every time I create a comment thread, I just update tblArticles with the relevant IDs.

Phase 2 - Coding

Posting a new thread in Phorum
To post a new thread in Phorum, I initially wasted a bit of time exploring the possibilities of using the Phorum "portable" code (located in the phorum directory of the same name). The portable code isn't needed for my requirements though.
Instead, I used the Phorum API. This is the set of functions contained in your database wrapper, which will almost certainly be in
/include/db/mysql.php

Before we can start using the API though, we need to set up our script to act (a bit) like Phorum.
First of all, we need to change to the Phorum directory and include common.php.
So, at the top of your script, do:

chdir("/your/path/to/phorum"); // if we don't do this, common.php will die when it is included
include_once("./common.php");
chdir("/your/original/path"); // you will probably need to do this for the rest of your script "includes" to work

It is important you do this in your main script, if you try and do it within a function, you will run into global scope errors and things will
start breaking.

The function which does most of the work is (unsurprisingly)
phorum_db_post_message($new_message);

Again though, before we can use this function, we need to do some work.
Firstly, we need to "trick" Phorum that we have already selected a particular forum.
We do this by setting a request variable:

$_REQUEST["forum_id"]=5;
(5 happens to be my comments forum, so I'm hard coding it)

phorum_db_post_message also needs its own include file, which for some reason it wouldn't get itself for me. So, to ensure it is present, do:

chdir("/your/path/to/phorum"); 
include_once("./include/thread_info.php");

Phorum's Message Array
To post a message, we must first initialise various array elements. We throw this array at the Phorum API and some of the elements will be updated as it is posted.

First of all, we'll use the Phorum API to get the correct username from our ID:
// get phorum user info
$user = phorum_db_user_get($user_id,false); // the second argument is for getting detailed info
$author = $user["username"];

(Optional) If you use Phorum Mail, one field needs to contain a unique string, to be used as a MIME e-mail identifier. Phorum does this by using a random MD5() string, with the forum name (minus spaces) appended at the end, eg:
$msgid = md5(uniqid(rand())) . "." . preg_replace("/[^a-z0-9]/i", "", "YourForumName");

Once we have these values, we set up the message array:

$message["forum_id"] = 5; // the forum we want to post it in
$message["datestamp"] = '';  // leave blank to post using the current 
time. Use a UNIX timestamp value to manually set the date.
$message["subject"] = $article_title; // the subject for the post
$message["body"] = "Comments for $article_title"; // the body of the post
$message["thread"] = 0;  // once posted, this will contain the new thread ID
$message["message_id"] = 0;  // once posted, this will also contain the new thread ID
$message["parent_id"] = 0; // new threads don't need this value to be set
$message["user_id"] = $user_id; // the Phorum user ID of the poster
$message["author"] = '$author'; // this is a string, containing the username. 
Even if you are posting as a registered user, you should still 
set this value, in case the original user is ever wiped. See above for how we lookup the value
$message["email"] = ''; // the e-mail address of the user - entirely optional and possibly 
not used for registered posters anyway.
$message["ip"] = ''; // this is a string, and can be left blank if you want
$message["moderator_post"] = 0; // leave this as 0 for normal posts
$message["status"] = PHORUM_STATUS_APPROVED; // if this isn't set correctly your post may be hidden
$message["sort"] = PHORUM_SORT_DEFAULT; // I'm not sure what this does (blm: this is used to determine if a post is an Announcement, Sticky or regular post)
$message["msgid"] = $msgid; // only used for Phorum Mail (see above)

After all that work, we can post the message! :

phorum_db_post_message($message);

You can now check your forum and should see your message. However, your forum's message count will be wrong, both in list and read views. This is because Phorum caches thread/message counts in separate fields, to save them having to be calculated every time someone visits your forum.
To update the cached values, we need to call two more API functions:

phorum_update_thread_info($message["thread"]);
phorum_db_update_forum_stats(false, 1, $message["datestamp"],1);
Briefly, the second function's arguments mean: don't refresh the whole forum, add one to the message count, update the "last post" time to our new message's timestamp and increment the thread count by one.

Putting all of this together gives me a function I can use to both post new and old articles to my forum, and stores the new message ID in tblArticles. Please note the function below is intended for reference only, it isn't a complete solution - I'm using my own DB wrapper to connect to my DB for example.

function phorum_insert_death_post($article_id,$user_id,$forum_id,$subject,$body,$datestamp=""){
	// date is optional, default will be current time
	// returns Phorum URL

	$dblink = openDB(); // this is my DB wrapper

	if ((int)$article_id==0){
		return;
	}

	chdir("/my/path/moviedeaths/phorum/");
	include_once("./include/thread_info.php");

	chdir("/my/path/moviedeaths/");

	// get phorum user info
	$user = phorum_db_user_get($user_id,false);
	$author = $user["username"];

	// first check death hasn't already been inserted
	$sql = "SELECT * FROM tblArticles WHERE `article_id` = $article_id";
	$result = sqlQuery($sql,$dblink);
	if (mysql_num_rows($result)>0){
		// we've already added this death, bomb out
		return;
	}

	// if we get to here, we need to insert the death
	$message["forum_id"] = $forum_id;
	$message["thread"]=0;
	$message["parent_id"]=0;
	$message["message_id"]=0;
	$message["status"] = PHORUM_STATUS_APPROVED;
	$message["sort"] = PHORUM_SORT_DEFAULT;
	$message["closed"] = 0;
	$message["user_id"]=$user_id;
	$message["author"]=$author;
	$message["subject"]=$subject;
	$message["moderator_post"]=0;
	$message["msgid"] = md5(uniqid(rand())) . "." . preg_replace("/[^a-z0-9]/i", "", "DeathReviews");

	if (strlen($datestamp)>0){
		$message["datestamp"]=$datestamp;
		$convert = true;
	}
	else{
		$convert = false;
	}
	$message["body"]=$body;

	$success = phorum_db_post_message($message,$convert);

	if ($success){
		phorum_update_thread_info($message["thread"]);
		phorum_db_update_forum_stats(false, 1, $message["datestamp"],1);
		// message ID updated by reference, insert it into tblArticles
		$sql = "INSERT INTO tblArticles (article_id,forum_id,thread_id)
			VALUES ($article_id,{$message["forum_id"]},{$message["thread"]})";
          $result = sqlQuery($sql,$dblink);
	  $returnvalue = yoursite.com/phorum/read.php?{$message["forum_id"]},{$message["thread"]}";
	}

	mysql_close();
	return $returnvalue;

}

One nice thing about the above function is that it returns the new Phorum thread if it is successful. This was useful in debugging as it meant I could easily output a link to the new post.

Adding all my old articles to Phorum with the correct datestamp.
I'm not going to go into too much detail how I did this as if you can follow the above it should be pretty trivial. Suffice to say, I looped through my articles and used the above function, making sure I passed the correct UNIX timestamp to it. I did it in ascending date order just to be tidy, but provided you use float-to-top in your forum I don't think it matters if older messages have a higher message_id value than newer ones.

Counting replies to a message in Phorum.
Now that messages are getting created, comments can be posted by visitors to the site as normal, completely within Phorum.
It would be nice to show how many comments each article has though, along with a link to the relevant thread.

So, within the script which displays an article, we need to query the new tblArticles to get the correct forum/thread ID:

SELECT forum_id,thread_id FROM tblArticles WHERE article_id = $article_id

Unfortunately, the Phorum API doesn't seem to have a lightweight "get message count in thread" function. (blm: the first message in a thread has a thread_count field in the meta data. so, just get the first message.)
The closest I could see was: phorum_db_get_messages($thread) which returns an array of messages.
It would obviously be more efficient for my purposes to write my own function containing "SELECT count(*) FROM phorum_messages WHERE forum_id = $forum_id and thread = $thread";

However, in case I ever want to show a summary of the messages below my article, here is a Phorum API solution:

Remember we need to do the standard includes at the top of our script before we can use the API

$comment_count = count(phorum_db_get_messages($thread)) - 2;

For some reason I haven't looked into yet, the above count shows as "2" for a single message with no replies. (blm: that function returns the messages AND a users array for the users that are involved in the array) I am therefore subtracting 2 from the value, because I want to show "0" comments for such an article.
Please note, I haven't yet tested the above technique for threads with replies over multiple pages.

Now we have the comment count, we can display the information on the article page.

$comment_link = "<your phorum URL>/read.php?$forum_id,$thread_id";
echo "Comments for this article: $comment_count. <a href=\"$commentlink\">Add a comment now</a>\n";

You can see examples of the article system here (please don't post test comments unless they are relevant to the site - it is live):

www.moviedeaths.com/species/dr._stephen_arden/ (contains nudity)
www.moviedeaths.com/predator/billy/

If you have any questions or corrections to make about this case study, please post them in this thread.

/\dam

--
My notable Phorum sites:
Movie Deaths Database - "review comments" system mostly powered by Phorum
Learn Chinese! - integrated forum quiz




Edited 6 time(s). Last edit at 12/13/2005 10:50PM by brianlmoon.
Re: Case Study: Phorum Article / Comments system
December 13, 2005 10:49PM
Adam, I put some comments inline above. You can adjust the post as you see fit. Just search for blm:.

Brian - Cowboy Ninja Coder - Personal Blog - Twitter



Edited 1 time(s). Last edit at 12/13/2005 10:51PM by brianlmoon.
Re: Case Study: Phorum Article / Comments system
December 14, 2005 04:23AM
Thank you Brian, I will rewrite later.

/\

--
My notable Phorum sites:
Movie Deaths Database - "review comments" system mostly powered by Phorum
Learn Chinese! - integrated forum quiz
pat
Re: Case Study: Phorum Article / Comments system
December 14, 2005 11:10AM
Great stuff adam !

Thank you.
Re: Case Study: Phorum Article / Comments system
December 15, 2005 10:25AM
Oh starting hacking with Phorum is nowadays so much easier than it used to be :) Thanks!

---
-=[ Panu ]=-
Re: Case Study: Phorum Article / Comments system
March 21, 2006 09:18PM
Ok, I tried this approach, but I couldn't make it to work.

Basically even when I start loading common.php, I got this error message.
NOTE: The error message is being generated by MODx.

--- BEGIN ---------------------------------------------------
« MODx Parse Error »
MODx encountered the following error while attempting to parse the requested resource:
« PHP Parse Error »

PHP error debug
Error: mysql_connect(): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
Error type/ Nr.: Warning - 2
File: /home/.gasman/djamoer/djamoer.net/MODx_Test/assets/modules/phorum/include/db/mysql.php
Line: 4147
Line 4147 source: $conn = mysql_connect($PHORUM["DBCONFIG"]["server"], $PHORUM["DBCONFIG"]["user"], $PHORUM["DBCONFIG"]["password"], true);

Parser timing
MySQL: 0.0198 s s (3 Requests)
PHP: 0.1721 s s
Total: 0.1919 s s
--- ENDED ---------------------------------------------------

I'm not quite sure about this clashing with current mysql connection on MODx, but as far as I know, when I use embed phorum or portable code, I'm able to run MODx inside MODx system.

Do I need to use function to run this whole thing inside function? Will this actually helped?
Re: Case Study: Phorum Article / Comments system
March 21, 2006 09:42PM
I got it to work now.
I forgot that this thing running inside function on MODx system, so I need to declare global $PHORUM before including this common.php file.
Re: Case Study: Phorum Article / Comments system
March 26, 2006 09:26AM
Very thanx!

One small addon... Recount of number of threads and messages in top list of forums didn't work untill I add string $PHORUM['forum_id'] = $forum_id;

	if ($success){
		$PHORUM['forum_id'] = $forum_id;
		phorum_update_thread_info($message["thread"]);
		phorum_db_update_forum_stats(false, 1, $message["datestamp"], 1);

		// message ID updated by reference, insert it into tblArticles
		$returnvalue = "/forum/read.php?{$message["forum_id"]},{$message["thread"]}";
	}
Re: Case Study: Phorum Article / Comments system
April 22, 2006 07:08AM
Just as an update, I now show the forum posts beneath each article, it really wasn't difficult at all (it probably took longer to do the style sheet formatting in fact).

eg: [www.moviedeaths.com]

/\dam

--
My notable Phorum sites:
Movie Deaths Database - "review comments" system mostly powered by Phorum
Learn Chinese! - integrated forum quiz
Re: Case Study: Phorum Article / Comments system
September 26, 2006 06:43AM
Hey Adam,

Just checking this, does this still work good for ya with the latest version of Phorum and the 5.x followers as well?
I am thinking about using this. It will save me a ton of work on integrating article systems the whole time.

Sergej

------------------------------------------
^AU^ Assassins United
[www.assassinsunited.com]
------------------------------------------
Sorry, only registered users may post in this forum.

Click here to login