Ask Ben: Flagging Threaded Discussions As New Or Unread

Posted November 13, 2008 at 8:57 AM by Ben Nadel

Tags: ColdFusion, Ask Ben

My request is a tutorial or advice on how to work out the threads / comments on a forum that a forum user hasn't read yet or that were posted since the last time he logged in and marking them as such.

First off, we have to recognize that flagging "unread" threads and "new" threads are very different gestures. A thread is new based on a given point in time. A thread is unread based on a particular relationship that is specific to the current user and the thread in question.

By far, the easiest gesture here is the "new" thread. In order to denote threads as new, all you have to do is store the previous login date of the user. This doesn't even have to be a server-stored value; you could easily store this kind of non-mission-critical data as a cookie value just as easily as you could in a user record in the database. Once you have this value, any thread that was created or updated since the user's previous login date could be visually flagged as new.

The pseudo code for this might look something like (I have no way to test this):

  • <cfloop query="qThread">
  •  
  • <tr>
  • <td>
  • <!---
  • Check to see if this thread was created or updated since
  • the user last logged into the message board.
  • --->
  • <cfif (qThread.date_updated LT SESSION.User.DateLastLoggedIn)>
  • [NEW]
  • </cfif>
  •  
  • #qThread.name# (#qThread.reply_count# replies)
  • </td>
  • </tr>
  •  
  • </cfloop>

Denoting threads as unread is a more complicated gesture. Now, you have to be storing the user's view relationship with each thread. In order to do this, we need to have some sort of a data table whose job it is to record this data. Perhaps something like this:

thread_view
---------------------
user_id :: int
thread_id :: int
date_created :: datetime

Now, every time a user views a thread, you can insert a new record into this table for that given user and that given thread. If you want to make it more simple, you can remove the date_created field (the date the user viewed the thread) and use this table a binary-flag (viewed or did not view ) rather than a sort of audit trail. Once you have this data in place, you can join it to queries that collect thread information.

The pseudo code for this might look something like (I have no way to test this):

  • SELECT
  • t.id,
  • t.name,
  •  
  • <!---
  • Check to see if we have a view record for this
  • thread and this user. If we do not (key in joined
  • table is NULL), then this thread is UNREAD.
  • --->
  • (
  • CASE
  • WHEN
  • v.thread_id IS NULL
  • THEN
  • 1
  • ELSE
  • 0
  • END
  • ) AS is_unread
  • FROM
  • thread t
  • LEFT OUTER JOIN
  • (
  • SELECT
  • thread_id
  • FROM
  • thread_view
  • WHERE
  • user_id = #SESSION.User.ID#
  • GROUP BY
  • thread_id
  • ) AS v
  • ON
  • t.id = v.thread_id

As you can see, as we gather the thread data, we are checking to see if the given thread record has a "view" record in our thread_view table. Now that we have a query like this, the output might look something like this:

  • <cfloop query="qThread">
  •  
  • <tr>
  • <td>
  • <!--- Check to see if this thread is unread. --->
  • <cfif qThread.is_unread>
  • [UNREAD]
  • </cfif>
  •  
  • #qThread.name# (#qThread.reply_count# replies)
  • </td>
  • </tr>
  •  
  • </cfloop>

I am sorry if the code above has odd bugs; as this example is very contextual, it's hard for me to test. I hope, however that this can point you in the right direction or give you a little inspiration.



Reader Comments

Nov 13, 2008 at 9:36 AM // reply »
16 Comments

Hi,

I think on some forums they do it another way around.

First, they assume only posts made since the last time you logged in should be marked as not viewed.

Then they make a list of posts that you HAVE viewed during this session and it is from this list that they can then show you which ones you have read, or assumed to have read or ignored from previous sessions.

This way they can keep a temporary record of the posts you have currently read in this session, and then when you leave the session they can dispose of the info and when you start the next session, the unread posts are the new ones with read ones being added to the read list.

hope that made sense??
cheers,
Mat


Nov 13, 2008 at 10:15 AM // reply »
10,640 Comments

@Mat,

What is the relationship between sessions? You lost me a little bit there. When a new session starts, it assumes all threads since the last login are "new". Then you view several, and they keep that in the user's session and use it to update the display whenever the user returns to the listing page (in the same session). But then when the session closes, all we store is the last login date?

Ok, I think that makes sense. I guess it depends on how accurate it needs to be. My hunch is that in most cases, that is going to be accurate enough :)


Nov 13, 2008 at 10:26 AM // reply »
16 Comments

@Ben,

Sorry I used sessions in a fairly fuzzy way there.
But your description was good.

I think it's just a more efficient way of providing that sort of functionality.
I've seen pretty massive forums, and with 10000 users and a million posts a table holding which posts have been looked at is gonna get pretty big.

As you said, it depends how accurate you want to be, and in reality I dont think people really need to know if they haven't read a thread from a couple of years ago.

Mat


Nov 13, 2008 at 10:50 AM // reply »
10,640 Comments

@Mat,

100% agree.


Nov 15, 2008 at 11:00 AM // reply »
12 Comments

http://pastebin.com/m37427566

Lets see if I can remember how I do it...
Basically, until a user looks in a topic, that topic remains new unless the last reply date is 14 days or older.
When the user looks in the topic, store the userid, date, and topic id.
Now, whenever a new reply for that topic id is added, remove all stored references of that topicid, so when the user comes back, we'll go back to square 1.
Each time the the system looks for a stored status, it'll trigger deleting anything older then 14 days, so like someone mentioned above the table doesnt get out of hand.

And of course adding things like locked, sticky, has the user replied in that topic, with new & old all in one status icon

If you want to see all the source for how I do it let me know, its a bunch of stuff... hmm also looks like I need to clean up the source in the pastebin.


Nov 15, 2008 at 1:12 PM // reply »
10,640 Comments

@Don,

I like the idea of deleting anything older than 14 days. I agree that at that point flagging things as new simply loses value.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 12, 2012 at 3:37 AM
Learning ColdFusion 8: CFImage Part III - Watermarks And Transparency
Hi Ben, Just to ask currently it is placed bottom right corner, if i need to replace the same rendered image on the bottom left side or in the bottom center, how that can be calculated. bottom ce ... read »
Feb 11, 2012 at 9:29 PM
Use jQuery's SlideDown() With Fixed-Width Elements To Prevent Jumping
I can't say how glad I am that I found your post. Thank you very much. ... read »
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »