2013-12-21 19:42:07 -05:00
var winston = require ( 'winston' ) ,
nconf = require ( 'nconf' ) ,
async = require ( 'async' ) ,
db = require ( './database' ) ,
2013-11-27 11:27:20 -05:00
topics = require ( './topics' ) ,
categories = require ( './categories' ) ,
CategoryTools = require ( './categoryTools' ) ,
user = require ( './user' ) ,
notifications = require ( './notifications' ) ,
2013-08-08 11:40:31 -04:00
posts = require ( './posts' ) ,
2013-11-27 11:27:20 -05:00
meta = require ( './meta' ) ,
2014-01-09 20:13:17 -05:00
websockets = require ( './socket.io' ) ,
2013-12-21 19:42:07 -05:00
events = require ( './events' ) ;
2013-05-23 12:52:16 -04:00
( function ( ThreadTools ) {
2013-07-05 16:44:11 -04:00
ThreadTools . exists = function ( tid , callback ) {
2013-12-04 23:36:52 -05:00
2013-12-02 21:20:55 -05:00
db . isSetMember ( 'topics:tid' , tid , function ( err , ismember ) {
2013-12-04 23:36:52 -05:00
2013-12-02 21:20:55 -05:00
if ( err ) {
callback ( false ) ;
}
2013-12-04 15:11:17 -05:00
callback ( ismember ) ;
2013-07-05 16:44:11 -04:00
} ) ;
}
2013-08-23 13:14:36 -04:00
2013-05-23 12:52:16 -04:00
ThreadTools . privileges = function ( tid , uid , callback ) {
2013-11-27 11:27:20 -05:00
async . parallel ( {
categoryPrivs : function ( next ) {
topics . getTopicField ( tid , 'cid' , function ( err , cid ) {
CategoryTools . privileges ( cid , uid , next ) ;
2013-05-23 12:52:16 -04:00
} ) ;
2013-11-27 11:27:20 -05:00
} ,
hasEnoughRep : function ( next ) {
2013-12-08 10:55:34 -05:00
if ( parseInt ( meta . config [ 'privileges:disabled' ] , 10 ) ) {
2013-12-08 10:49:42 -05:00
return next ( null , false ) ;
} else {
user . getUserField ( uid , 'reputation' , function ( err , reputation ) {
if ( err ) return next ( null , false ) ;
next ( null , parseInt ( reputation , 10 ) >= parseInt ( meta . config [ 'privileges:manage_topic' ] , 10 ) ) ;
} ) ;
}
2013-11-27 11:27:20 -05:00
}
} , function ( err , results ) {
callback ( err , ! results ? undefined : {
read : results . categoryPrivs . read ,
write : results . categoryPrivs . write ,
editable : results . categoryPrivs . editable || results . hasEnoughRep ,
2014-01-19 17:28:08 -05:00
view _deleted : results . categoryPrivs . view _deleted || results . hasEnoughRep ,
moderator : results . categoryPrivs . moderator ,
admin : results . categoryPrivs . admin
2013-05-23 12:52:16 -04:00
} ) ;
} ) ;
}
2014-01-01 14:15:53 -05:00
ThreadTools . delete = function ( tid , uid , callback ) {
2014-01-22 23:58:21 -05:00
topics . getTopicField ( tid , 'deleted' , function ( err , deleted ) {
if ( err ) {
return callback ( err ) ;
}
2013-10-16 13:04:28 -04:00
2014-01-22 23:58:21 -05:00
if ( parseInt ( deleted , 10 ) ) {
return callback ( new Error ( 'topic-already-deleted' ) ) ;
}
2013-05-23 12:52:16 -04:00
2014-01-22 23:58:21 -05:00
topics . delete ( tid ) ;
2013-08-08 11:40:31 -04:00
2014-01-22 23:58:21 -05:00
db . decrObjectField ( 'global' , 'topicCount' ) ;
2013-06-20 16:19:17 -04:00
2014-01-22 23:58:21 -05:00
ThreadTools . lock ( tid ) ;
2013-12-21 19:42:07 -05:00
2014-01-22 23:58:21 -05:00
db . searchRemove ( 'topic' , tid ) ;
2014-01-16 19:58:57 -05:00
2014-01-22 23:58:21 -05:00
events . logTopicDelete ( uid , tid ) ;
websockets . emitTopicPostStats ( ) ;
websockets . in ( 'topic_' + tid ) . emit ( 'event:topic_deleted' , {
tid : tid
} ) ;
2013-05-23 12:52:16 -04:00
2014-01-16 19:58:57 -05:00
callback ( null , {
tid : tid
} ) ;
2014-01-22 23:58:21 -05:00
} ) ;
2013-11-25 17:20:44 -05:00
}
2013-06-20 16:19:17 -04:00
2014-01-01 14:15:53 -05:00
ThreadTools . restore = function ( tid , uid , callback ) {
2014-01-22 23:58:21 -05:00
topics . getTopicField ( tid , 'deleted' , function ( err , deleted ) {
if ( err ) {
return callback ( err ) ;
}
2013-10-16 13:04:28 -04:00
2014-01-22 23:58:21 -05:00
if ( ! parseInt ( deleted , 10 ) ) {
return callback ( new Error ( 'topic-already-restored' ) ) ;
}
2013-12-21 19:42:07 -05:00
2014-01-22 23:58:21 -05:00
topics . restore ( tid ) ;
2014-01-16 19:58:57 -05:00
2014-01-22 23:58:21 -05:00
db . incrObjectField ( 'global' , 'topicCount' ) ;
2013-05-23 12:52:16 -04:00
2014-01-22 23:58:21 -05:00
ThreadTools . unlock ( tid ) ;
events . logTopicRestore ( uid , tid ) ;
websockets . emitTopicPostStats ( ) ;
websockets . in ( 'topic_' + tid ) . emit ( 'event:topic_restored' , {
tid : tid
} ) ;
topics . getTopicField ( tid , 'title' , function ( err , title ) {
db . searchIndex ( 'topic' , title , tid ) ;
} ) ;
2013-08-23 13:14:36 -04:00
2014-01-16 19:58:57 -05:00
callback ( null , {
tid : tid
} ) ;
2014-01-22 23:58:21 -05:00
} ) ;
2013-11-25 17:20:44 -05:00
}
2013-06-20 16:19:17 -04:00
2014-01-16 19:58:57 -05:00
ThreadTools . lock = function ( tid , uid , callback ) {
2014-01-10 10:46:26 -05:00
topics . setTopicField ( tid , 'locked' , 1 ) ;
websockets . in ( 'topic_' + tid ) . emit ( 'event:topic_locked' , {
2014-01-16 19:58:57 -05:00
tid : tid
2014-01-10 10:46:26 -05:00
} ) ;
if ( callback ) {
2014-01-16 19:58:57 -05:00
callback ( null , {
2014-01-10 10:46:26 -05:00
tid : tid
} ) ;
}
}
2014-01-16 19:58:57 -05:00
ThreadTools . unlock = function ( tid , uid , callback ) {
2014-01-10 10:46:26 -05:00
topics . setTopicField ( tid , 'locked' , 0 ) ;
websockets . in ( 'topic_' + tid ) . emit ( 'event:topic_unlocked' , {
2014-01-16 19:58:57 -05:00
tid : tid
2014-01-10 10:46:26 -05:00
} ) ;
if ( callback ) {
2014-01-16 19:58:57 -05:00
callback ( null , {
2014-01-10 10:46:26 -05:00
tid : tid
} ) ;
}
}
2014-01-16 19:58:57 -05:00
ThreadTools . pin = function ( tid , uid , callback ) {
2013-11-25 17:20:44 -05:00
topics . setTopicField ( tid , 'pinned' , 1 ) ;
topics . getTopicField ( tid , 'cid' , function ( err , cid ) {
2013-12-02 21:20:55 -05:00
db . sortedSetAdd ( 'categories:' + cid + ':tid' , Math . pow ( 2 , 53 ) , tid ) ;
2013-05-23 12:52:16 -04:00
} ) ;
2014-01-10 10:46:26 -05:00
websockets . in ( 'topic_' + tid ) . emit ( 'event:topic_pinned' , {
2014-01-16 19:58:57 -05:00
tid : tid
2014-01-10 10:46:26 -05:00
} ) ;
2013-08-23 13:14:36 -04:00
2014-01-10 10:46:26 -05:00
if ( callback ) {
2014-01-16 19:58:57 -05:00
callback ( null , {
2014-01-10 10:46:26 -05:00
tid : tid
} ) ;
2013-11-25 17:20:44 -05:00
}
}
2013-06-20 16:19:17 -04:00
2014-01-16 19:58:57 -05:00
ThreadTools . unpin = function ( tid , uid , callback ) {
2013-11-25 17:20:44 -05:00
topics . setTopicField ( tid , 'pinned' , 0 ) ;
topics . getTopicFields ( tid , [ 'cid' , 'lastposttime' ] , function ( err , topicData ) {
2013-12-02 21:20:55 -05:00
db . sortedSetAdd ( 'categories:' + topicData . cid + ':tid' , topicData . lastposttime , tid ) ;
2013-05-23 12:52:16 -04:00
} ) ;
2013-11-25 17:20:44 -05:00
2014-01-10 10:46:26 -05:00
websockets . in ( 'topic_' + tid ) . emit ( 'event:topic_unpinned' , {
2014-01-16 19:58:57 -05:00
tid : tid
2014-01-10 10:46:26 -05:00
} ) ;
if ( callback ) {
2014-01-16 19:58:57 -05:00
callback ( null , {
2014-01-10 10:46:26 -05:00
tid : tid
} ) ;
2013-11-25 17:20:44 -05:00
}
2013-05-23 12:52:16 -04:00
}
2014-01-16 16:10:23 -05:00
ThreadTools . move = function ( tid , cid , callback ) {
2013-08-24 18:19:01 -04:00
topics . getTopicFields ( tid , [ 'cid' , 'lastposttime' ] , function ( err , topicData ) {
2013-08-09 20:03:19 -04:00
var oldCid = topicData . cid ;
2013-12-04 16:58:06 -05:00
db . sortedSetRemove ( 'categories:' + oldCid + ':tid' , tid , function ( err , result ) {
db . sortedSetAdd ( 'categories:' + cid + ':tid' , topicData . lastposttime , tid , function ( err , result ) {
2013-08-23 13:14:36 -04:00
2013-12-04 16:58:06 -05:00
if ( err ) {
2014-01-16 19:58:57 -05:00
return callback ( err ) ;
2013-12-04 16:58:06 -05:00
}
2013-07-03 13:08:32 -04:00
topics . setTopicField ( tid , 'cid' , cid ) ;
2013-08-09 20:03:19 -04:00
2013-07-22 12:07:05 -04:00
categories . moveRecentReplies ( tid , oldCid , cid , function ( err , data ) {
2013-09-17 13:09:37 -04:00
if ( err ) {
2013-08-13 16:00:24 -04:00
winston . err ( err ) ;
2013-07-22 12:07:05 -04:00
}
} ) ;
2014-01-14 13:10:31 -05:00
categories . moveActiveUsers ( tid , oldCid , cid ) ;
2013-08-27 13:32:43 -04:00
2013-07-04 13:29:29 -04:00
categories . incrementCategoryFieldBy ( oldCid , 'topic_count' , - 1 ) ;
categories . incrementCategoryFieldBy ( cid , 'topic_count' , 1 ) ;
2013-07-03 13:08:32 -04:00
2014-01-16 19:58:57 -05:00
callback ( null ) ;
2013-12-04 16:58:06 -05:00
} ) ;
2013-05-23 12:52:16 -04:00
} ) ;
} ) ;
}
2014-01-16 20:53:32 -05:00
ThreadTools . isFollowing = function ( tid , uid , callback ) {
db . isSetMember ( 'tid:' + tid + ':followers' , uid , callback ) ;
2013-06-06 20:39:45 -04:00
}
2014-01-16 20:53:32 -05:00
ThreadTools . toggleFollow = function ( tid , uid , callback ) {
ThreadTools . isFollowing ( tid , uid , function ( err , following ) {
if ( err ) {
return callback ( err ) ;
2013-06-06 20:39:45 -04:00
}
2014-01-16 20:53:32 -05:00
db [ following ? 'setRemove' : 'setAdd' ] ( 'tid:' + tid + ':followers' , uid , function ( err , success ) {
if ( callback ) {
if ( err ) {
return callback ( err ) ;
}
callback ( null , ! following ) ;
}
} ) ;
2013-06-06 20:39:45 -04:00
} ) ;
}
2013-11-01 13:04:15 -04:00
ThreadTools . getFollowers = function ( tid , callback ) {
2013-12-02 21:20:55 -05:00
db . getSetMembers ( 'tid:' + tid + ':followers' , function ( err , followers ) {
2014-01-16 20:53:32 -05:00
if ( err ) {
return callback ( err ) ;
}
if ( followers ) {
followers = followers . map ( function ( follower ) {
return parseInt ( follower , 10 ) ;
} ) ;
}
callback ( null , followers ) ;
2013-06-06 20:39:45 -04:00
} ) ;
}
2013-11-01 13:04:15 -04:00
ThreadTools . notifyFollowers = function ( tid , exceptUid ) {
2013-06-06 20:39:45 -04:00
async . parallel ( [
function ( next ) {
2013-08-11 16:12:20 -04:00
topics . getTopicField ( tid , 'title' , function ( err , title ) {
2013-07-15 15:03:42 -04:00
topics . getTeaser ( tid , function ( err , teaser ) {
if ( ! err ) {
2013-10-03 20:35:16 -04:00
notifications . create ( '<strong>' + teaser . username + '</strong> has posted a reply to: "<strong>' + title + '</strong>"' , nconf . get ( 'relative_path' ) + '/topic/' + tid , 'topic:' + tid , function ( nid ) {
2013-07-15 15:03:42 -04:00
next ( null , nid ) ;
} ) ;
} else next ( err ) ;
2013-06-06 20:39:45 -04:00
} ) ;
} ) ;
} ,
function ( next ) {
2013-11-01 13:04:15 -04:00
ThreadTools . getFollowers ( tid , function ( err , followers ) {
2013-10-03 21:18:59 -04:00
exceptUid = parseInt ( exceptUid , 10 ) ;
2013-06-06 20:39:45 -04:00
if ( followers . indexOf ( exceptUid ) !== - 1 ) followers . splice ( followers . indexOf ( exceptUid ) , 1 ) ;
next ( null , followers ) ;
} ) ;
}
] , function ( err , results ) {
2013-12-02 21:20:55 -05:00
if ( ! err ) {
notifications . push ( results [ 0 ] , results [ 1 ] ) ;
}
2013-06-06 20:39:45 -04:00
} ) ;
}
2014-01-18 23:18:58 -05:00
ThreadTools . getLatestUndeletedPost = function ( tid , callback ) {
ThreadTools . getLatestUndeletedPid ( tid , function ( err , pid ) {
if ( err ) {
return callback ( err ) ;
}
posts . getPostData ( pid , callback ) ;
} ) ;
}
2013-09-22 13:33:05 -04:00
ThreadTools . getLatestUndeletedPid = function ( tid , callback ) {
2014-01-13 18:02:06 -05:00
db . getSortedSetRevRange ( 'tid:' + tid + ':posts' , 0 , - 1 , function ( err , pids ) {
2014-01-07 17:30:29 -05:00
if ( err ) {
return callback ( err ) ;
}
2013-12-02 21:20:55 -05:00
if ( pids . length === 0 ) {
return callback ( new Error ( 'no-undeleted-pids-found' ) ) ;
}
2013-09-22 12:55:03 -04:00
async . detectSeries ( pids , function ( pid , next ) {
2013-11-15 14:57:50 -05:00
posts . getPostField ( pid , 'deleted' , function ( err , deleted ) {
2014-01-13 18:02:06 -05:00
next ( parseInt ( deleted , 10 ) === 0 ) ;
2013-09-22 12:55:03 -04:00
} ) ;
} , function ( pid ) {
2013-12-02 21:20:55 -05:00
if ( pid ) {
callback ( null , pid ) ;
} else {
callback ( new Error ( 'no-undeleted-pids-found' ) ) ;
}
2013-09-22 12:55:03 -04:00
} ) ;
2013-08-14 13:32:07 -04:00
} ) ;
2013-07-15 15:08:54 -04:00
}
2013-05-23 12:52:16 -04:00
} ( exports ) ) ;