Making a Forum

Making a Forum

by Louis Stowasser

In this tutorial we are going to learn how to create a forum. Note that this tutorial isn't going to be just a copy and paste tutorial (Although doing that will give you a fully functional forum). You will hopefully learn the basics and if you get stuck, you can email me at louisstow AT hotmail DOT com or I am on the forums 24/7 so just make a topic there. There will be no extra processing pages for this tutorial. All the form processing will be done on the same page.

PLEASE READ THE COMMENTS TO UNDERSTAND HOW THE CODE WORKS! - I use comments in the code to tell you what I am doing and why. At then end of the code I give a brief summary of what the code does and give links to other resources that may help.

To get all the files used to make this forum, including the SQL Dump file, click here.

INGREDIENTS

  • ColdFusion 6+
  • MySQL, MsSQL or any other database. (I tried testing with MS Access but hardly anything worked. So those with a decent DBMS, your safe. To access users, www.mysql.com)
  • A decent code editor
  • A pot of coffee. This may take a while

DATABASE SETUP

I am going to assume you are using MySQL. If so, the link to all the files contains an SQL Dump file of the database. If not, you can change the data types to whatever you see necessary. You will need 4 tables:

Users Posts Topics Forums
userID - PK
user_name - varchar
pass_word - varchar
joinDate - date
posts - integer
role - integer
postID - PK
postBody - text
topicID - integer
userID - integer
postTime - DateTime
topicID - PK
topicTitle - varchar
userID - integer
forumID - integer
sticky - integer
views - integer
forumID - PK
forumName - varchar
forumDescr - varchar
startUser - varchar

Now that we have the backbone of the forum, we will start on the pages. Now I'm not going to design this forum for you, but what I will do is make it easy for you to implement your own design.

PAGES

  • layout.cfm - This will be where you put your blasted HTML code so I don't have to do it.
  • Application.cfm - Where we setup the main variables.
  • index.cfm - The main page where the forum categories are displayed, including info about the last poster and how many post/topics.
  • login.cfm - Where the user logs in.
  • logout.cfm - Where the user logs out (if we're lucky).
  • register.cfm - The user registers their account here.
  • viewforum.cfm - Where all the topics are listed and more info including views, replies and the last poster is displayed.
  • viewtopic.cfm - Will display a topic/thread whatever you like to call it.
  • newpost.cfm - This will be the page where users reply to topics.
  • newtopic.cfm - This is where users will create a new topic/thread/discussion.
  • editpost.cfm - Where users can edit their post.
  • edittopic.cfm - Where users edit their topic.

LAYOUT.CFM

This will be a custom tag to automatically fill in the HTML code. You should put the start of your code before the <cfelse> and the rest after it. Ray Camden has an article about this here: http://www.coldfusionjedi.com/index.cfm/2007/9/3/ColdFusion-custom-tag-for-layout-example

Code:

<cfif thisTag.executionMode EQ "start">

<cfparam name="attributes.title" default="myForum">

<!--- Display the header of our design --->
<html>
<head>
<title>
<cfoutput>#attributes.title#</cfoutput></title>
<body>
<div align="center" style="background:#ffd579; border-bottom:4px #ffcc00 solid"><h1 id="top">My Forum</h1>
<cfif structKeyExists(session,'userID')>
<!--- This checks to see if the session userID is set. --->
<cfquery name="uname" datasource="#request.dsn#">
<!--- If the above statement returns true, we query the database to find out their username --->
SELECT user_name FROM Users WHERE userID = <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">
</cfquery>
Welcome <strong><cfoutput>#uname.user_name#</cfoutput>!</strong> - [<a href="logout.cfm">Logout</a>] <!--- Then we greet the user and display the logout link --->
<cfelse> <!--- If the session is not set, meaning the user is not logged in, we display the login or register links --->
[<a href="Login.cfm">Login</a>] [<a href="register.cfm">Register</a>]
</cfif>
</div>

<cfelse>
<!--- Display the footer of our design --->
<hr>
<div align="center">
&copy; My Forum 2008</div>
</body>
</html>


</cfif>

So what we are doing is checking if the custom tag is in the starting stage. If so then display the html for the top half of our design. If not, display the bottom half. I added an attribute to the custom tag called title, which will be the page title. I defaulted it to "myForum" so I only have to set the title when I want to. The HTML is very basic so I recommend putting in your own design. Something you should keep in there though, is the query and checking if the session is set.

APPLICATION.CFM

This is where we setup the variables and start the application. I would normally use Application.cfc but let's just keep this simple.

Code:

<cfapplication name="MyForum" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,30,0)#" applicationtimeout="#createTimeSpan(0,0,20,0)#" clientmanagement="no">
<cfset request.dsn = "myforumdsn">

The cfapplication will create our application. You may change the duration of the session time out by changing the createtimespan variable in the sessiontimeout attribute.

We also set a request variable of our datasource which in my case is "myforumdsn".

INDEX.CFM

This will be our main page and the first time we use our layout custom tag.

Code:

<cfquery datasource="#request.dsn#" name="forums">
SELECT f.forumID,f.forumName,t.total
FROM Forums f
     LEFT OUTER JOIN ( <!--- We join a sub query of the post table and topics table in another join to return the post count for each forum --->
SELECT Topics.forumID,COUNT(Posts.postID) AS total FROM Topics LEFT JOIN Posts ON Posts.topicID = Topics.topicID GROUP BY Topics.forumID
) t
ON t.forumID = f.forumID
</cfquery>

<cf_layout title="Welcome to MyForum">
<!--- Our first use of the layout custom tag. This will automatically inject our design code written in layout.cfm --->
<h2>Welcome to MyForum</h2>
<table width="100%"><tr>
<th>Forum</th><th>Posts</th>
</tr>
<cfoutput query="forums"> <!--- Here we are outputing the "forums" query. The forums query will return every forum with a post count --->
<tr height="35"<cfif currentrow MOD 2> bgcolor="##ebebeb"</cfif>> <!--- The simple if statement checks if the outputed row is even, if it is then it makes the background color grey. So basically it makes alternate row colours --->
<td><a href="viewforum.cfm?f=#forumID#">#forumName#</a></td><td align="center">#val(total)#</td>
</tr>
</cfoutput>
</table>
</cf_layout>

The query may look complicated. That's because it is. What we are doing is joining 3 tables together to tell us the post count of each forum. We use the custom <cf_layout> tag that we created earlier to implement the design and do a simple query output to display the data.

For more information on the alternating row colors, check out this tutorial by Pablo: http://tutorial22.easycfm.com/

LOGIN.CFM

The user will be able to login on this page.

Code:

<cfif NOT structIsEmpty(form)> <!--- This statement checks if the form has been submited. If so then continue with the next bit of code --->
<cfquery datasource="#request.dsn#" name="verify">
SELECT userID,user_name,pass_word
FROM Users
WHERE user_name = <cfqueryparam value="#form.user#" cfsqltype="cf_sql_char"> AND <!--- We are comparing the submited form details with the database to find a match. --->
pass_word = <cfqueryparam value="#form.pass#" cfsqltype="cf_sql_char">
</cfquery>

<cfif verify.recordcount> <!--- If a match was found then continue --->
<cfset session.userID = verify.userID>
<!--- We set the session variables to the userID found. This will be used throughout the forum --->
<cflocation url="index.cfm" addtoken="no">
<!--- The user is now logged in so send them back to the starting page --->
<cfelse>
<!--- If a match was NOT found then the following code will be executed --->
<script>
alert("We could not find a user with those details. Please try again"); <!--- Alert them that the user was not found --->
self.location = 'login.cfm'; <!--- Send them to the login page to try and login again --->
</script>
</cfif>
</cfif>

<cf_layout>
<h2>Login</h2>

<form action="login.cfm" method="post"> <!--- Display the login form. We use 2 input text forms. For the username and password --->
Username: <input type="text" name="user" /><br />
Password: <input type="password" name="pass" /><br />
<input type="submit" name="submit" value="Login" />
<input type="button" value="Back" onclick="history.go(-1);" /> <!--- The back button contains simple javascript that sends them back in history --->

</cf_layout>

So we query the database where the username and password are equal to the one specified. If we find a match, we set their session to the user id found. If not, we give them a javascript popup. You can change this to whatever you like. You may want to log this failed attempt incase anyone is trying to login by brute force.

Here is a tutorial by pabdog regarding user authentication: http://tutorial8.easycfm.com

LOGOUT.CFM

It is very doubtful that users will actually logout by clicking this, but it's good to have it anyway. This is the simplest page yet.

Code:

<cfset StructClear(session)> <!--- We clear the session structure --->
<cflocation url="login.cfm" addtoken="no"> <!--- Then direct them back to the login page --->

And that's all the code needed!!! Simple code for once.

For a tutorial on clearing the session structure, see this tutorial: http://tutorial24.easycfm.com/

REGISTER.CFM

There are many things you will want to change on this page. For example you may want to send a confirmation email. But to make this forum work, we will create a basic registration page.

Code:

<cfif NOT structIsEmpty(form)> <!--- You should know what this does already but incase you forgot, it checks if the form has been submit. I'm not going to tell you again so remember it!! :) --->
<cfquery name="insuser" datasource="#request.dsn#"> <!--- Insert the provided form values in the database. Normally we would validate what goes in here but I think your old enough to do that yourself hehe --->
INSERT INTO users(user_name,pass_word,posts,role,joinDate)
VALUES (
<cfqueryparam value="#form.user#" cfsqltype="cf_sql_char">
, <!--- CFQUERYPARAM. CJ's first love! This is a great tag and you should ALWAYS use it --->
<cfqueryparam value="#form.pass#" cfsqltype="cf_sql_char">
,
0,
<cfif structKeyExists(form,'role')>
2<cfelse>1</cfif>, <!--- If the checkbox to decide if the user is to be a mod has been selected, then insert the number 2 which from now on will represent a moderator --->
<cfqueryparam value="#CreateODBCDate(now())#" cfsqltype="cf_sql_date">
<!--- This will enter the date that the user registered on --->
)
</cfquery>


<cflocation url="login.cfm" addtoken="no"> <!--- Now that they are a valid user, get them to login --->
</cfif>

<cf_layout>
<h2>Register</h2>
<form action="register.cfm" method="post">

<p>
Username: <input type="text" name="user" /><br />
Password: <input type="password" name="pass" />
</p>

<p>
<input type="checkbox" name="role" /> Moderator <!--- Obviously you will want to restrict this ability to mods only, but I leave that up to you --->
</p>

<p>
<input type="submit" value="Register!" />
<input type="button" value="Back" onclick="self.location='index.cfm';" />

</p>
</form>

</cf_layout>

There would be 10,000,000.034 things you want to change here. For starters you would want to check if the username has been taken. If it has than throw an error. You would want to make sure no special characters are used in the username. But I'll leave all the validation up to you. This is just to give you a head start.

VIEWFORUM.CFM

This page will display all the topics in the chosen forum.

Code:

<cfquery datasource="#request.dsn#" name="forumInfo"> <!--- Return the name of the chosen forum --->
SELECT forumName FROM Forums WHERE forumID = <cfqueryparam value="#URL.f#" cfsqltype="cf_sql_integer">
</cfquery>

<cfquery datasource="#request.dsn#" name="topics"> <!--- This query will return all the topics in the chosen forum as well as post count, if its sticky, author, views and last post --->
SELECT t.topicID,t.topicTitle,t.views,t.sticky,u.userID,u.user_name, COUNT(p.postID) AS replies, MAX(p.postTime) AS lastpost
FROM Topics t LEFT OUTER JOIN Posts p ON t.topicID = p.topicID
              LEFT OUTER JOIN Users u ON u.userID = t.userID
WHERE t.forumID = <cfqueryparam value="#URL.f#" cfsqltype="cf_sql_integer">
GROUP BY t.topicID
ORDER BY t.sticky desc,lastpost desc <!--- We want the sticky topics to be displayed first. Then the one with the most recent post is next --->
</cfquery>

<cfif structKeyExists(session,'userID')> <!--- If the user is logged in, do a query to see what role they have --->
<cfquery name="role" datasource="#request.dsn#">
SELECT role FROM Users WHERE userID = <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer"> </cfquery>
</cfif>

<cf_layout>
<cfoutput><h2>#forumInfo.forumName#</h2> <!--- Output the forum name as the heading --->
<a href="index.cfm">Home</a> &gt; #forumInfo.forumName#<br /> <!--- Display the breadcrumbs from the start --->
<div align="center">
[<a href="newtopic.cfm?f=#url.f#">New Topic</a>]</div></cfoutput>

<table width="100%"><tr>
<th width="15"></th><th>Topic</th><th width="150">Posted By</th><th width="60">Views</th><th width="60">Replies</th><th width="150">Last Post</th></tr>

<cfoutput query="topics"> <!--- Output that big complicated query with all the topics in it --->
<tr height="35"<cfif currentrow MOD 2> bgcolor="##ebebeb"</cfif>> <!--- That ol' alternating row color thingo --->
<td align="center"><strong>#currentrow#</strong></td> <!--- This will output its current position in the query. It isnt necessary so you may delete if you dislike it --->
<td><a href="viewtopic.cfm?t=#topicID#">#topicTitle#</a> <cfif sticky>(Topic Pinned)</cfif> <!--- If the topic is sticky, notify the user --->
<cfif structKeyExists(session,'userID') AND (session.userID EQ userID OR role.role EQ 2)>
<!--- Using the query to find out the users role, we check if the user is the original poster or a mod. If they are one of the 2, display the edit link --->
[<a href="edittopic.cfm?t=#topicID#">Edit</a>]
</cfif></td>
<td>#user_name#</td> <!--- Here we just output the returned query variables into columns --->
<td align="center">
#views#</td>
<td align="center">
#replies-1#</td>
<td width="150">
#TimeFormat(lastpost,"h:mm tt")#, #DateFormat(lastpost,"d mmm")#</td> <!--- TimeFormat() and DateFormat() format the time to our pleasing. I like displaying the day then the month as 3 letters. ie 26 Jun. And as for time, we just use the basic format (10:07 PM). This link will show you how to format your dates:
DateFormat() - http://livedocs.adobe.com/coldfusion/7/htmldocs/00000441.htm
TimeFormat() - http://livedocs.adobe.com/coldfusion/7/htmldocs/00000651.htm --->

</tr>
</cfoutput>

</table>
</cf_layout>

This is fairly similar to viewforum.cfm. The query gets the details for each topic and gets a count of the replies from the posts table. We then output that data into a table. I have subtracted the replies column by 1 otherwise the main body of the topic will be counted as a reply when it is not.

VIEWTOPIC.CFM

I have decided to not include pagination in this tutorial due to the fact that there would be too much code to go through, so I have listed some tutorials about record paging to help you if you are stuck and remember you can always contact me if you are stuck. I am here to help!:

Code:

<cfquery datasource="#request.dsn#" name="topic"> <!--- This query will return the forum name and topic name to build our breadcrumbs and various other things --->
SELECT t.topicTitle,t.forumID,f.forumName
FROM topics t INNER JOIN forums f ON t.forumID = f.forumID
WHERE t.topicID = <cfqueryparam value="#URL.t#" cfsqltype="cf_sql_integer">
</cfquery>

<cfquery datasource="#request.dsn#" name="posts"> <!--- This returns the posts from the chosen topic and joins the user table to get info like the poster username, post count, join date and role --->
SELECT p.postID,p.postBody,p.userID,p.post Time,u.joinDate,u.user_name,u.posts,u.role
FROM posts p INNER JOIN users u ON p.userID = u.userID
WHERE p.topicID = <cfqueryparam value="#URL.t#" cfsqltype="cf_sql_integer">
ORDER BY p.postID asc
</cfquery>

<cfquery datasource="#request.dsn#" name="views"> <!--- This simple query is basically a hit counter and will update the views for the topic -->
UPDATE topics
SET views = views + 1
WHERE topicID = <cfqueryparam value="#url.t#" cfsqltype="cf_sql_integer">
</cfquery>

<cfif structKeyExists(session,'userID')> <!--- You've seen this before. It does the same thing as last time. Grabs the users role --->
<cfquery name="role" datasource="#request.dsn#">
SELECT role FROM Users WHERE userID = <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer"> </cfquery>
</cfif>

<cf_layout title="#topic.topicTitle# - MyForum"> <!--- Here I wanted the title to be the topic name that the user is viewing --->
<style type="text/css"> <!--- Our first CSS!!! I wanted the posts to look slightly pretty --->
.posts { border:1px #0000CC solid; border-collapse: collapse; }
.posts h3 { padding:0; margin:0; color:#000099 }
.posts td { border:1px #0000CC solid; padding:5px }
.small { font-size:12px }
</style>

<cfoutput>
<h2>
#topic.topicTitle#</h2>
<a href="index.cfm">Home</a> &gt; <!--- This is the breadcrumbs for easy navigation --->
<a href="viewforum.cfm?f=#topic.forumID#">
#topic.forumName#</a> &gt;
#topic.topicTitle#
</cfoutput>

<p>
<cfoutput query="posts"> <!--- Output the posts query --->
<table width="100%" class="posts" cellpadding="0" cellpadding="0"<cfif NOT currentrow MOD 2> bgcolor="##ebebeb"</cfif>>
<tr>
<td width="200">
<h3>#user_name#</h3>
<cfif role EQ 2> Moderator <cfelse> Junior Member </cfif><br /> <!--- If their role is 2, it means they are a mod remember. So display that. If not then they are just some bozo. But don't display that. Call them what you like. I called them Junior Members ---->
Posts: #posts#<br />
Joined: #DateFormat(joinDate,"mmm yyyy")#
</td>
<td valign="top">#postBody#</td>
</tr>
<tr class="small">
<td>[<a href="#top">Back to top</a>]</td><td><strong>#DateFormat(postTime,"d mmm yy")#,</strong> #TimeFormat(postTime,"h:mm tt")# - [<a href="newpost.cfm?t=#url.t#">Reply</a>]
<cfif StructKeyExists(session,'userID') AND (session.userID EQ userID OR role.role EQ 2)> <!--- Again if the user logged in is a mod or the author of the post, display the edit link --->
[<a href="editpost.cfm?p=#postID#">Edit</a>]
</cfif>
</td>
</tr>
</table>
</cfoutput>
</p>

</cf_layout>

For starters, we have 2 queries to grab the information like the topic name and the forum name so we can build our bread crumbs. Then we make a query for all the posts in the chosen topic. We join the users tables together to grab user information that we want to output.

This is the first page to actually use CSS!! You can of course change the design. It is actually recommended to change the design, unless you like Times New Roman.

This page is fairly self explanatory. We output the data collected in the posts query. We also add links to reply or edit the post. The user may only edit their own post (unless they are a mod) so we do a simple if to check if the user is who they say they are.

NEWPOST.CFM

This is the page where users can reply to topics.

Code:

<cfif NOT structIsEmpty(form)>
<cfif structKeyExists(session,'userID')> <!--- Check if the user is logged in --->
<cfquery name="insert" datasource="#request.dsn#">
INSERT INTO posts(postBody,topicID,userID,postTime) <!--- Insert the form data into the database --->
VALUES (
<cfqueryparam value="#form.body#" cfsqltype="cf_sql_char">
,
<cfqueryparam value="#url.t#" cfsqltype="cf_sql_integer">,
<cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">,
<cfqueryparam value="#now()#" cfsqltype="cf_sql_timestamp">
)
</cfquery>


<cfquery name="postcount" datasource="#request.dsn#">

UPDATE users SET posts = posts + 1 <!--- Update the users post count by incrimenting it by 1 --->
</cfquery>
<cflocation url="viewtopic.cfm?t=#url.t#" addtoken="no">

<cfelse>
<!--- If the user is not logged in, send them to the login page --->
<cflocation url="login.cfm" addtoken="no">
</cfif>
</cfif>

<cf_layout>
<h2>Reply to topic</h2>
<form action="newpost.cfm?t=<cfoutput>#url.t#</cfoutput>"
method="post">
<textarea cols="56" rows="8" name="body"></textarea><br />
<input type="submit" value="Reply!" />
<cfoutput><input type="button" value="Back" onclick="self.location='viewtopic.cfm?t=#url.t#';" /></cfoutput> </form>

</cf_layout>

The first bit of code checks if the form has been submitted. If it has than it makes sure the user is logged in. If they are, it will insert the post in the database and update the users post count.

Now I have just put in a simple textarea but you will most likely want a WYSIWYG editor. If you have CF8 than all you have to do is set the richtext attribute in <cftextarea> to true. If you do not have CF8, there are a number of free WYSIWYG editors. Here is a small list:

NEWTOPIC.CFM

This will be similar to newpost.cfm except we add another form element.

Code:

<cfif NOT structIsEmpty(form)>
<cfif structKeyExists(session,'userID')>

<cftransaction>
<!--- this will make sure the last id inserted is always the one we just made ---->

<cfquery name="insert" datasource="#request.dsn#"> <!--- Insert the form data into the database --->
INSERT INTO topics(topicTitle,userID,forumID,sticky,views)
VALUES (
<cfqueryparam value="#form.subject#" cfsqltype="cf_sql_char">,
<cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">
,
<cfqueryparam value="#url.f#" cfsqltype="cf_sql_integer">
,
<cfif StructKeyExists(form,'sticky')>1<cfelse>0</cfif>,
0
)
</cfquery>
<cfquery name="lastID" datasource="#request.dsn#">
SELECT LAST_INSERT_ID() as newID <!--- Get the ID of the topic we just made --->
</cfquery>

</cftransaction>

<cfquery name="inspost" datasource="#request.dsn#">
INSERT INTO posts(postBody,topicID,userID,postTime)
VALUES (
<cfqueryparam value="#form.body#" cfsqltype="cf_sql_char">
,
<cfqueryparam value="#lastID.newID#" cfsqltype="cf_sql_integer">
,
<cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">
,
<cfqueryparam value="#now()#" cfsqltype="cf_sql_timestamp">

)
</cfquery>
<cflocation url="viewtopic.cfm?t=#lastID.newID#" addtoken="no">
<!--- send the user to the topic they just created --->
<cfelse>
<cflocation url="login.cfm" addtoken="no">
</cfif>

</cfif>


<cfif NOT structKeyExists(session,'userID')> <!--- Check if the user is logged in --->
<cflocation url="login.cfm" addtoken="no">
<cfabort>
</cfif>


<cfquery name="role" datasource="#request.dsn#"> <!--- Grab the role of the user logged in --->
SELECT role FROM Users WHERE userID = <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">
</cfquery>

<cf_layout>
<h2>Start a topic</h2>
<form action="newtopic.cfm?f=<cfoutput>#url.f#</cfoutput>"
method="post">
Subject: <input type="text" name="subject" /><br />
<textarea cols="56" rows="8" name="body">
</textarea><br />
<cfif role.role EQ 2> <!--- We only want admins/mods to be able to make a topic sticky and remember 2 means they are a mod --->
<input type="checkbox" name="sticky" />
Sticky<br />
</cfif>
<input type="submit" value="Reply!" />

<cfoutput><input type="button" value="Back" onclick="self.location='viewforum.cfm?f=#url.f#';" /></cfoutput>
</form>
</cf_layout>

This is fairly self explanatory. We use a tag called <cftransaction> to lock the 2 queries together and if another user accesses the page at the same time, they must wait till the original user has completed the query.

EDITPOST.CFM

This will be similar to newpost.cfm except users edit their post so the form is already filled in.

Code:

<cfif NOT structIsEmpty(form)>
<cfif structKeyExists(session,'userID')> <!--- Insert a "post edited" message --->
<cfset postb = form.body & "<br><br><font size='1'>Post edited: #DateFormat(now(),'d mmm')#</font>"
>

<cfquery name="insert" datasource="#request.dsn#"> <!--- Update post table ---->
UPDATE posts SET postBody = <cfqueryparam value="#postb#" cfsqltype="cf_sql_char">
WHERE postID = <cfqueryparam value="#url.p#" cfsqltype="cf_sql_integer">
</cfquery>

<cflocation url="viewtopic.cfm?t=#url.t#" addtoken="no">
<!--- Redirect the user back to the topic in which the post was made --->
<cfelse>
<cflocation url="login.cfm" addtoken="no"> <!--- If the user was not logged in, send them to the login page --->
</cfif>
</cfif>

<!--- Check if the user is logged in --->
<cfif NOT structKeyExists(session,'userID')>
<cflocation url="login.cfm" addtoken="no">
<cfabort>
</cfif>

<!--- Get the users role --->
<cfquery name="role" datasource="#request.dsn#">
SELECT role
FROM Users
WHERE userID = <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">
</cfquery>

<!--- Grab the information about the post so we can set the default form values --->
<cfquery datasource="#request.dsn#" name="postinfo">
SELECT postBody,topicID,userID
FROM posts
WHERE postID = <cfqueryparam value="#url.p#" cfsqltype="cf_sql_integer">
</cfquery>

<!--- Check if the author of the post is the user logged in --->
<cfif postinfo.userID NEQ session.userID AND role.role NEQ 2>
<!--- if they are not the right user, log them out and request they log in as the original author --->
<script>
alert("You are not the original author of this post");
self.location = 'logout.cfm';
</script>
</cfif>

<cf_layout>
<h2>Edit post</h2>
<form action="editpost.cfm?p=<cfoutput>#url.p#&t=#postinfo.topicID#</cfoutput>"
method="post">
<textarea cols="56" rows="8" name="body"><cfoutput>#postinfo.postBody#</cfoutput></textarea><br /> <!--- Fill the text area with the previously made post --->
<input type="submit" value="Edit" />
<cfoutput><input type="button" value="Back" onclick="self.location='viewtopic.cfm?t=#postinfo.topicID#';" /></cfoutput>
</form>
</cf_layout>

Instead of INSERTing the data into the database, we use the UPDATE method so we are not creating a new post but updating the one they want to edit. We also add a little message to the body text saying when the post was edited.

EDITTOPIC.CFM

This is the page that edits the topic details and it will be our last page to make.

Code:

<cfif NOT structIsEmpty(form)>
<cfif structKeyExists(session,'userID')>
<cfquery name="upd" datasource="#request.dsn#">
<!--- Update topics table ---->
UPDATE topics
SET
topicTitle = <cfqueryparam value="#form.subject#" cfsqltype="cf_sql_char">,
forumID = <cfqueryparam value="#form.forum#" cfsqltype="cf_sql_integer">,
sticky = <cfif StructKeyExists(form,'sticky')>1<cfelse>0</cfif> <!--- If the user checked the sticky box, make it sticky --->
WHERE topicID = <cfqueryparam value="#url.t#" cfsqltype="cf_sql_integer">
</cfquery>
<cflocation url="viewtopic.cfm?t=#url.t#" addtoken="no">
<cfelse>
<cflocation url="login.cfm" addtoken="no">
<!--- The user is not logged in so send them to the login page --->
</cfif>

</cfif>


<!--- Check if the user is logged in --->
<cfif NOT structKeyExists(session,'userID')>

<cflocation url="login.cfm" addtoken="no">

<cfabort>
</cfif>

<!--- Get topic info --->
<cfquery datasource="#request.dsn#" name="topicinfo">
SELECT topicID,topicTitle,forumID,userID,sticky
FROM topics
WHERE topicID = <cfqueryparam value="#url.t#" cfsqltype="cf_sql_integer">
</cfquery>

<!---- Get all the available forums --->
<cfquery datasource="#request.dsn#" name="forums">
SELECT forumID,forumName FROM forums
</cfquery>

<!--- Get the users role --->
<cfquery name="roles" datasource="#request.dsn#">

SELECT role
FROM Users
WHERE userID = <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">
</cfquery>

<!--- Check if the author of the post is the user logged in --->
<cfif topicinfo.userID NEQ session.userID AND roles.role NEQ 2>

<!--- if they are not the right user, log them out and request they log in as the original author --->
<script>
alert("You are not the original author of this topic");
self.location = 'logout.cfm';
</script>

</cfif>


<cf_layout>
<h2>
Edit topic</h2>
<form action="edittopic.cfm?t=<cfoutput>#url.t#</cfoutput>" method="post">
Subject: <cfoutput><input type="text" name="subject" value="#topicinfo.topicTitle#" /></cfoutput><br />
Forum:
<select name="forum">

<cfoutput query="forums">
<option value="#forumID#"<cfif topicinfo.forumID EQ forumID>
selected</cfif>>#forumName#</option>
</cfoutput>
</select>
<br />
<cfif roles.role EQ 2> <!--- If the user is a mod, allow them to make the topic sticky --->
<input type="checkbox" name="sticky"<cfif topicinfo.sticky> checked</cfif>> Sticky<br />
</cfif>

<input type="submit" value="Edit" />

<cfoutput>

<input type="button" value="Back" onclick="self.location='viewforum.cfm?f=#topicinfo.forumID#';" />
</cfoutput>
</form>

</cf_layout>

And that's it!!! Its over!!

As I have mentioned over and over, this should get you started. I leave the design and little features up to you. Even if you just copied the code and replicated the database you should have a working forum.

I hope this tutorial helped out. If you have any comments, questions or improvements - please email me at louisstow AT hotmail DOT com.



All ColdFusion Tutorials By Author: Louis Stowasser