@ -9,39 +9,94 @@ var RDB = require('./redis.js')
threadTools = require ( './threadTools.js' ) ,
postTools = require ( './postTools' ) ,
async = require ( 'async' ) ,
feed = require ( './feed.js' ) ;
feed = require ( './feed.js' ) ,
favourites = require ( './favourites.js' ) ;
marked . setOptions ( {
breaks : true
} ) ;
( function ( Topics ) {
Topics . getTopicById = function ( tid , current _user , callback ) {
function getTopicData ( next ) {
RDB . multi ( )
. get ( schema . topics ( tid ) . title )
. get ( schema . topics ( tid ) . locked )
. get ( schema . topics ( tid ) . category _name )
. get ( schema . topics ( tid ) . category _slug )
. get ( schema . topics ( tid ) . deleted )
. get ( schema . topics ( tid ) . pinned )
. get ( schema . topics ( tid ) . slug )
. exec ( function ( err , replies ) {
next ( null , {
topic _name : replies [ 0 ] ,
locked : replies [ 1 ] || 0 ,
category _name : replies [ 2 ] ,
category _slug : replies [ 3 ] ,
deleted : replies [ 4 ] || 0 ,
pinned : replies [ 5 ] || 0 ,
slug : replies [ 6 ]
} ) ;
Topics . getTopicData = function ( tid , callback ) {
RDB . hgetall ( 'topic:' + tid , function ( err , data ) {
if ( err === null )
callback ( data ) ;
else
console . log ( err ) ;
} ) ;
}
Topics . getTopicDataWithUsername = function ( tid , callback ) {
Topics . getTopicData ( tid , function ( topic ) {
user . getUserField ( topic . uid , 'username' , function ( username ) {
topic . username = username ;
callback ( topic ) ;
} ) ;
} ) ;
}
Topics . getTopicPosts = function ( tid , start , end , current _user , callback ) {
posts . getPostsByTid ( tid , start , end , function ( postData ) {
function getFavouritesData ( next ) {
var pids = [ ] ;
for ( var i = 0 ; i < postData . length ; ++ i )
pids . push ( postData [ i ] . pid ) ;
favourites . getFavouritesByPostIDs ( pids , current _user , function ( fav _data ) {
next ( null , fav _data ) ;
} ) ;
}
function addUserInfoToPosts ( next ) {
var done = 0 ;
for ( var i = 0 , ii = postData . length ; i < ii ; ++ i ) {
posts . addUserInfoToPost ( postData [ i ] , function ( ) {
++ done ;
if ( done === postData . length )
next ( null , null ) ;
} ) ;
}
}
function getPrivileges ( next ) {
threadTools . privileges ( tid , current _user , function ( privData ) {
next ( null , privData ) ;
} ) ;
}
async . parallel ( [ getFavouritesData , addUserInfoToPosts , getPrivileges ] , function ( err , results ) {
var fav _data = results [ 0 ] ,
privileges = results [ 2 ] ;
for ( var i = 0 ; i < postData . length ; ++ i ) {
postData [ i ] . fav _star _class = fav _data [ postData [ i ] . pid ] ? 'icon-star' : 'icon-star-empty' ;
postData [ i ] [ 'display_moderator_tools' ] = ( postData [ i ] . uid == current _user || privileges . editable ) ? 'show' : 'none' ;
}
callback ( postData ) ;
} ) ;
} ) ;
}
Topics . getTopicWithPosts = function ( tid , current _user , callback ) {
Topics . markAsRead ( tid , current _user ) ;
function getTopicData ( next ) {
Topics . getTopicData ( tid , function ( topicData ) {
next ( null , topicData ) ;
} ) ;
}
function getTopicPosts ( next ) {
posts . getPostsByTid ( tid , current _user , 0 , 9 , function ( postData ) {
next ( null , postData ) ;
Topics . getTopicPosts ( tid , 0 , - 1 , current _user , function ( topicPosts , privileges ) {
next ( null , topicPosts ) ;
} ) ;
}
@ -49,100 +104,71 @@ marked.setOptions({
threadTools . privileges ( tid , current _user , function ( privData ) {
next ( null , privData ) ;
} ) ;
}
}
async . parallel ( [ getTopicData , getTopicPosts , getPrivileges ] , function ( err , results ) {
var topicData = results [ 0 ] ,
topicPosts = results [ 1 ] ,
privileges = results [ 2 ] ;
postTools . constructPostObject ( results [ 1 ] , tid , current _user , privileges , function ( postObj ) {
if ( postObj . length ) {
var main _posts = postObj . splice ( 0 , 1 ) ;
callback ( {
'topic_name' : topicData . topic _name ,
'category_name' : topicData . category _name ,
'category_slug' : topicData . category _slug ,
'locked' : parseInt ( topicData . locked ) || 0 ,
'deleted' : parseInt ( topicData . deleted ) || 0 ,
'pinned' : parseInt ( topicData . pinned ) || 0 ,
'slug' : topicData . slug ,
'topic_id' : tid ,
'expose_tools' : privileges . editable ? 1 : 0 ,
'posts' : postObj ,
'main_posts' : main _posts
} ) ;
} else {
return callback ( false ) ;
}
var main _posts = topicPosts . splice ( 0 , 1 ) ;
callback ( {
'topic_name' : topicData . title ,
'category_name' : topicData . category _name ,
'category_slug' : topicData . category _slug ,
'locked' : topicData . locked ,
'deleted' : topicData . deleted ,
'pinned' : topicData . pinned ,
'slug' : topicData . slug ,
'topic_id' : tid ,
'expose_tools' : privileges . editable ? 1 : 0 ,
'posts' : topicPosts ,
'main_posts' : main _posts
} ) ;
} ) ;
}
Topics . get _topic = function ( tid , uid , callback ) {
var topicData = { } ;
function get _topic _data ( next ) {
RDB . mget ( [
schema . topics ( tid ) . title ,
schema . topics ( tid ) . uid ,
schema . topics ( tid ) . timestamp ,
schema . topics ( tid ) . slug ,
schema . topics ( tid ) . postcount ,
schema . topics ( tid ) . locked ,
schema . topics ( tid ) . pinned ,
schema . topics ( tid ) . deleted
] , function ( err , topic ) {
if ( err ) {
throw new Error ( err ) ;
}
topicData . title = topic [ 0 ] ;
topicData . uid = topic [ 1 ] ;
topicData . timestamp = topic [ 2 ] ;
topicData . relativeTime = utils . relativeTime ( topic [ 2 ] ) ,
topicData . slug = topic [ 3 ] ;
topicData . post _count = topic [ 4 ] ;
topicData . locked = topic [ 5 ] ;
topicData . pinned = topic [ 6 ] ;
topicData . deleted = topic [ 7 ] ;
user . getUserField ( topic [ 1 ] , 'username' , function ( username ) {
topicData . username = username ;
next ( ) ;
} ) ;
Topics . getTopicForCategoryView = function ( tid , uid , callback ) {
function getTopicData ( next ) {
Topics . getTopicDataWithUsername ( tid , function ( topic ) {
next ( null , topic ) ;
} ) ;
}
function get _read _status ( next ) {
// posts.create calls this function - should be an option to skip this because its always true
function getReadStatus ( next ) {
if ( uid && parseInt ( uid ) > 0 ) {
RDB . sismember ( schema . topics ( tid ) . read _by _uid , uid , function ( err , read ) {
topicData . badgeclass = read ? '' : 'badge-important' ;
next ( ) ;
next ( null , read ) ;
} ) ;
} else {
next ( ) ;
next ( null , null ) ;
}
}
function get _teaser ( next ) {
Topics . get _teaser ( tid , function ( teaser ) {
topicData . teaser _text = teaser . text ;
topicData . teaser _username = teaser . username ;
next ( ) ;
function getTeaser ( next ) {
Topics . getTeaser ( tid , function ( teaser ) {
next ( null , teaser ) ;
} ) ;
}
async . parallel ( [ get _topic _data , get _read _status , get _t easer] , function ( err ) {
async . parallel ( [ getTopicData , getReadStatus , getTeaser ] , function ( err , results ) {
if ( err ) {
throw new Error ( err ) ;
}
var topicData = results [ 0 ] ,
hasRead = results [ 1 ] ,
teaser = results [ 2 ] ;
topicData . relativeTime = utils . relativeTime ( topicData . timestamp ) ;
topicData . badgeclass = hasRead ? '' : 'badge-important' ;
topicData . teaser _text = teaser . text ;
topicData . teaser _username = teaser . username ;
topicData . teaser _timestamp = utils . relativeTime ( teaser . timestamp ) ;
topicData . tid = tid ;
callback ( topicData ) ;
} ) ;
}
@ -175,7 +201,7 @@ marked.setOptions({
tids . sort ( function ( a , b ) { return b - a ; } ) ;
async . each ( tids , function ( tid , next ) {
Topics . get _topic ( tid , 0 , function ( topicData ) {
Topics . get TopicDataWithUsername( tid , function ( topicData ) {
topics . push ( topicData ) ;
next ( ) ;
} ) ;
@ -185,35 +211,11 @@ marked.setOptions({
} ) ;
}
Topics . get _cid _by _tid = function ( tid , callback ) {
RDB . get ( schema . topics ( tid ) . cid , function ( err , cid ) {
if ( cid && parseInt ( cid ) > 0 ) {
callback ( cid ) ;
} else {
callback ( false ) ;
}
} ) ;
}
Topics . getTitle = function ( tid , callback ) {
RDB . get ( 'tid:' + tid + ':title' , function ( err , title ) {
callback ( title ) ;
} ) ;
}
Topics . getSlug = function ( tid , callback ) {
RDB . get ( 'tid:' + tid + ':slug' , function ( err , slug ) {
callback ( slug ) ;
} ) ;
}
Topics . getTitleByPid = function ( pid , callback ) {
RDB . get ( 'pid:' + pid + ':tid' , function ( err , tid ) {
if ( ! err ) {
Topics . getTitle ( tid , function ( title ) {
callback ( title ) ;
} ) ;
} else callback ( 'Could not grab title' ) ;
posts . getPostField ( pid , 'tid' , function ( tid ) {
Topics . getTopicField ( tid , 'title' , function ( title ) {
callback ( title ) ;
} ) ;
} ) ;
}
@ -221,7 +223,7 @@ marked.setOptions({
RDB . sadd ( schema . topics ( tid ) . read _by _uid , uid ) ;
Topics . get _cid _by _tid ( tid , function ( cid ) {
Topics . get TopicField( tid , 'cid' , function ( cid ) {
categories . isTopicsRead ( cid , uid , function ( read ) {
if ( read ) {
@ -243,13 +245,13 @@ marked.setOptions({
} ) ;
}
Topics . get _t easers = function ( tids , callback ) {
Topics . get T easers = function ( tids , callback ) {
var requests = [ ] ;
if ( Array . isArray ( tids ) ) {
for ( x = 0 , numTids = tids . length ; x < numTids ; x ++ ) {
( function ( tid ) {
requests . push ( function ( next ) {
Topics . get _t easer( tid , function ( teaser _info ) {
Topics . get T easer( tid , function ( teaser _info ) {
next ( null , teaser _info ) ;
} ) ;
} ) ;
@ -263,42 +265,37 @@ marked.setOptions({
}
}
// start: probably should be moved into posts
Topics . get _latest _undeleted _pid = function ( tid , callback ) {
RDB . lrange ( schema . topics ( tid ) . posts , 0 , - 1 , function ( err , pids ) {
var pidKeys = [ ] ,
numPids = pids . length ;
if ( numPids === 0 ) return callback ( null ) ;
posts . getPostsByTid ( tid , 0 , - 1 , function ( posts ) {
for ( var x = 0 , numPids = pids . length ; x < numPids ; x ++ ) {
pidKeys . push ( 'pid:' + pids [ x ] + ':deleted' ) ;
}
RDB . mget ( pidKeys , function ( err , posts ) {
var numPosts = posts . length ;
while ( numPosts -- ) {
if ( posts [ numPosts ] !== '1' ) {
callback ( pids [ numPosts ] ) ;
break ;
}
var numPosts = posts . length ;
if ( ! numPosts )
callback ( null ) ;
while ( numPosts -- ) {
if ( posts [ numPosts ] . deleted !== '1' ) {
callback ( posts [ numPosts ] . pid ) ;
break ;
}
} ) ;
} ) ;
}
} ) ;
}
Topics . get _t easer = function ( tid , callback ) {
Topics . get T easer = function ( tid , callback ) {
Topics . get _latest _undeleted _pid ( tid , function ( pid ) {
if ( pid !== null ) {
RDB . mget ( [
'pid:' + pid + ':content' ,
'pid:' + pid + ':uid' ,
'pid:' + pid + ':timestamp'
] , function ( err , content ) {
user . getUserField ( content [ 1 ] , 'username' , function ( username ) {
var stripped = content [ 0 ] ,
timestamp = content [ 2 ] ;
if ( content [ 0 ] )
stripped = utils . strip _tags ( marked ( content [ 0 ] ) ) ;
posts . getPostFields ( pid , [ 'content' , 'uid' , 'timestamp' ] , function ( postData ) {
user . getUserField ( postData . uid , 'username' , function ( username ) {
var stripped = postData . content ,
timestamp = postData . timestamp ;
if ( postData . content )
stripped = utils . strip _tags ( marked ( postData . content ) ) ;
callback ( {
"text" : stripped ,
"username" : username ,
@ -309,10 +306,10 @@ marked.setOptions({
}
} ) ;
}
// end: probably should be moved into posts
Topics . post = function ( socket , uid , title , content , category _id ) {
if ( ! category _id ) throw new Error ( 'Attempted to post without a category_id' ) ;
if ( ! category _id )
throw new Error ( 'Attempted to post without a category_id' ) ;
if ( uid === 0 ) {
socket . emit ( 'event:alert' , {
@ -352,16 +349,24 @@ marked.setOptions({
}
var slug = tid + '/' + utils . slugify ( title ) ;
// Topic Info
RDB . set ( schema . topics ( tid ) . title , title ) ;
RDB . set ( schema . topics ( tid ) . uid , uid ) ;
RDB . set ( schema . topics ( tid ) . slug , slug ) ;
RDB . set ( schema . topics ( tid ) . timestamp , Date . now ( ) ) ;
var timestamp = Date . now ( ) ;
RDB . hmset ( 'topic:' + tid , {
'tid' : tid ,
'uid' : uid ,
'cid' : category _id ,
'title' : title ,
'slug' : slug ,
'timestamp' : timestamp ,
'lastposttime' : 0 ,
'postcount' : 0 ,
'locked' : 0 ,
'deleted' : 0 ,
'pinned' : 0
} ) ;
RDB . set ( 'topic:slug:' + slug + ':tid' , tid ) ;
RDB . set ( 'topic slug:' + slug + ':tid' , tid ) ;
// Posts
posts . create ( uid , tid , content , function ( pid ) {
if ( pid > 0 ) {
RDB . lpush ( schema . topics ( tid ) . posts , pid ) ;
@ -370,7 +375,7 @@ marked.setOptions({
threadTools . toggleFollow ( tid , uid ) ;
// Notify any users looking at the category that a new topic has arrived
Topics . get _topic ( tid , uid , function ( topicData ) {
Topics . get TopicForCategoryView ( tid , uid , function ( topicData ) {
io . sockets . in ( 'category_' + category _id ) . emit ( 'event:new_topic' , topicData ) ;
io . sockets . in ( 'recent_posts' ) . emit ( 'event:new_topic' , topicData ) ;
} ) ;
@ -379,40 +384,62 @@ marked.setOptions({
}
} ) ;
// User Details - move this out later
RDB . lpush ( 'uid:' + uid + ':topics' , tid ) ;
socket . emit ( 'event:alert' , {
title : 'Thank you for posting' ,
message : 'You have successfully posted. Click here to view your post.' ,
type : 'notify' ,
timeout : 2000
} ) ;
user . addTopicIdToUser ( uid , tid ) ;
// let everyone know that there is an unread topic in this category
RDB . del ( 'cid:' + category _id + ':read_by_uid' , function ( err , data ) {
Topics . markAsRead ( tid , uid ) ;
} ) ;
RDB . zadd ( schema . topics ( ) . recent , Date . now ( ) , tid ) ;
//RDB.zadd('topics:active', tid);
// in future it may be possible to add topics to several categories, so leaving the door open here.
RDB . sadd ( 'categories:' + category _id + ':tid' , tid ) ;
RDB . set ( schema . topics ( tid ) . cid , category _id ) ;
categories . getCategories ( [ category _id ] , function ( data ) {
RDB. set ( schema . topics ( tid ) . category _name , data . categories [ 0 ] . name ) ;
RDB. set ( schema . topics ( tid ) . category _slug , data . categories [ 0 ] . slug ) ;
Topics. setTopicField ( tid , 'category_name' , data . categories [ 0 ] . name ) ;
Topics. setTopicField ( tid , 'category_slug' , data . categories [ 0 ] . slug ) ;
} ) ;
RDB . incr( 'cid:' + category _id + ':topiccount' ) ;
RDB . hincrby( 'category:' + category _id , 'topic_count' , 1 ) ;
RDB . incr ( 'totaltopiccount' ) ;
feed . updateCategory ( category _id ) ;
socket . emit ( 'event:alert' , {
title : 'Thank you for posting' ,
message : 'You have successfully posted. Click here to view your post.' ,
type : 'notify' ,
timeout : 2000
} ) ;
} ) ;
} ) ;
} ;
Topics . getTopicField = function ( tid , field , callback ) {
RDB . hget ( 'topic:' + tid , field , function ( err , data ) {
if ( err === null )
callback ( data ) ;
else
console . log ( err ) ;
} ) ;
}
Topics . setTopicField = function ( tid , field , value ) {
RDB . hset ( 'topic:' + tid , field , value ) ;
}
Topics . increasePostCount = function ( tid ) {
RDB . hincrby ( 'topic:' + tid , 'postcount' , 1 ) ;
}
Topics . isLocked = function ( tid , callback ) {
Topics . getTopicField ( tid , 'locked' , function ( locked ) {
callback ( locked ) ;
} ) ;
}
Topics . addToRecent = function ( tid , timestamp ) {
RDB . zadd ( schema . topics ( ) . recent , timestamp , tid ) ;
}
} ( exports ) ) ;