A very simple forum
Abstract
A simple forum web application. Modelled on the Joel on Software forums. Installation should be just dropping a PHP file onto your web server and that's all. Everything is taken care of after that. There's no user registration, no ownership of usernames. Posting is trivial. There's no markup language when posting stuff. It's designed to get out of your way and just let you post.
Handles one and only one forum. If you want two forums, create two folders and put a copy of the script in each. Everything in the script is self-contained, so updating the forum software means just replacing index.php. Don't break this principle.
Basics
The whole forum is file-based, not database-based. Posting a new topic creates a new HTML file; adding a followup to a topic edits that HTML file. Displaying a topic just means including that HTML file in the output, which is fast and simple.
There are the following files:
- index.php -- this does all the actual work. All URLs to the forum are to index.php with parameters
- topics.html -- an HTML snippet, which is a list of all topics, with links (back to index.php) to display them
- n.html -- n is an integer. One page per topic, which is a list of all posts to that topic
"Virtual" pages (i.e., things embedded in index.php, which will be visited like index.php?a=foo, which might be actual separate pages in some other system)
- new -- a form to enter a post that starts a topic
- new2 -- where the "new" form submits to
- reply -- a form to enter a followup to an existing topic
- reply2 -- where the "reply" form submits to
- style -- a stylesheet for the forum
- js -- javascript for the forum
- topics -- displays topics.html with header above and footer below
- topic -- displays n.html for the specified topic with header and footer
- header -- header HTML
- footer -- footer HTML
Page descriptions
topics.html
The list of all topics (i.e., the index page for the forum) A snippet like this:
<ol id="topics"> <!-- insert new topic here --> <li><a href="index.php?a=topic&topic=2&replies=0">Topic 2 title</a></li> <li><a href="index.php?a=topic&topic=1&replies=3">Topic 1 title</a></li> </ol> <p><a href="index.php?a=new">Start a new topic</a></p>
Note the replies=n on the URLs; these reply counts are updated by adding a new reply to the topic, which means that the user's browser will show updated topics in a different colour (and topics with new replies can be styled with a:visited and so on).
n.html
A page for one topic.
A snippet like this:
<h2>This topic title</h2> <ul id="posts"> <li> <p>The first paragraph of the first post to this topic.</p> <p>The second paragraph of the first post to this topic.</p> <p class="person"><cite>John Q. Random</cite></p> </li> <li> <p>The first paragraph of the first followup to this topic.</p> <p>The second paragraph of the first followup to this topic.</p> <p class="person"><cite>Dr Fred Mbogo</cite></p> </li> <li> <p>The first paragraph of the second followup to this topic.</p> <p>The second paragraph of the second followup to this topic.</p> <p class="person"><a href="http://fish.example.org"><cite>Kevin Fish</cite></a></p> </li> <!-- insert new reply here --> </ul> <p> <a href="index.php?a=reply&topic=1">Reply to this topic</a> <a href="index.php">Recent topics</a> </p>
Note the topic=1 in the "Reply to this topic" link; that number is the number of this topic. Note that people must have a name and can have a URL.
virtual page "new"
An HTML snippet, which doesn't change, and looks like this:
<h2>Start a new topic</h2> <form id="new" action="index.php?a=new2"> <p><textarea name="text"></textarea></p> <p><label for="name">Name: <input type="text" name="name"></label></p> <p><label for="url">Website: <input type="text" name="url"></label> (not required)</p> <p> <input type="submit" name="cancel" value="Cancel"> <input type="submit" name="post" value="Post"> </p> </form>
virtual page "new2"
- Create a UL snippet containing the submitted text in a single LI, as per n.html
- Calculate the highest post number and add 1 to get the new n
- (possible race condition here? maybe flock(calculate-number, LOCK_EX)?)
- flock(n.html, LOCK_EX)
- open n.html and write the snippet to it
- flock(n.html, LOCK_UN)
- flock(topics.html, LOCK_EX)
- open topics.html and read all the text
- at <!-- insert new topic here --> insert a new link, <li><a href="index.php?a=topic&topic=n&replies=0">Topic title</a></li>
- save topics.html
- flock(topics.html, LOCK_UN)
virtual page "reply"
Takes a parameter "topic", so a URL would be index.php?a=reply&topic=1.
An HTML snippet which looks like this:
<h2>Reply to this topic</h2> <form id="reply" action="index.php?a=reply2"> <input type="hidden" name="topic" value="1"> <p><textarea name="text"></textarea></p> <p><label for="name">Name: <input type="text" name="name"></label></p> <p><label for="url">Website: <input type="text" name="url"></label> (not required)</p> <p> <input type="submit" name="cancel" value="Cancel"> <input type="submit" name="post" value="Post reply"> </p> </form>
Note that this must be dynamic, to correctly set the value of the hidden topic field. It does not display the topic title, so as to avoid hitting the disc to work it out.
virtual page "reply2"
- Create a UL snippet containing the submitted text in a single LI, as per n.html
- flock(n.html, LOCK_EX)
- open n.html and write the snippet to it at <!-- insert new reply here -->
- count replies in n.html
- flock(n.html, LOCK_UN)
- flock(topics.html, LOCK_EX)
- open topics.html and read all the text
- find the link, <li><a href="index.php?a=topic&topic=(TOPICNO)&replies=(SOMETHING)">Topic title</a></li>, replace (SOMETHING) with (SOMETHING+1)
- save topics.html
- flock(topics.html, LOCK_UN)
virtual page "style"
A stylesheet. Unspecified; make it look OK and that'll do.
virtual page "js"
Some JavaScript. At the moment there's no JS required, and there probably won't need to be much (there's no Ajax here, really), but a few judicious focus() calls might not go amiss, as well as pre-filling the name and url fields on post forms from a cookie. We don't want to do that server-side because that'll require more parsing of the HTML, which is slow and boring; remembering your name isn't critical, so we can do it client-side and if it doesn't work then whatever.
The index page itself, and how it works
On startup, check if "topics.html" exists. If it doesn't, branch to the install step (described below). If it does, check for a parameter "a". If non-existent, assume it was "topics", and do what it says.
The installation step
- Try writing a file and then reading it back. If it fails, die with an error explaining that you can't write files
- write out an empty topics.html
- redirect to yourself
Future enhancements
- More than one forum -- need a "bridge" page which can look in all the folders under it and if any contain forums display a link to them. How do we handle replies so you know where there's something new?
- Paginating topics.html. How? Say there are 20 topic links per page; adding a 21st topic needs to either (a) move topics.html to topics1.html and add 21st topic link (and just that) to topics.html, which is a bit stupid (the front page will look empty even though the forum is full), (b) move link 1 from topics.html to topics1.html and then add link 21 to the top of topics.html (which is hard), or (c) not do pagination.