Merge pull request #48 from designcreateplay/hash_changes

Hash changes
v1.18.x
psychobunny 12 years ago
commit 616af3d8ed

@ -61,27 +61,25 @@
});
socket.emit('api:categories.getRecentReplies', cid);
socket.on('api:categories.getRecentReplies', function(replies) {
if (replies === false) {
socket.on('api:categories.getRecentReplies', function(posts) {
if (!posts || posts.length === 0) {
return;
}
var users = replies.users,
posts = replies.posts,
recent_replies = document.getElementById('category_recent_replies');
var recent_replies = document.getElementById('category_recent_replies');
recent_replies.innerHTML = '';
for (var i=0, ii=posts.pids.length; i<ii; i++) {
for (var i=0, ii=posts.length; i<ii; i++) {
var a = document.createElement('a'),
ul = document.createElement('ul'),
username = users[posts.uid[i]].username,
picture = users[posts.uid[i]].picture;
username = posts[i].username,
picture = posts[i].picture;
//temp until design finalized
ul.innerHTML = '<li><img title="' + username + '" style="width: 48px; height: 48px; /*temporary*/" src="' + picture + '" class="" />'
+ '<p><strong>' + username + '</strong>: ' + posts.content[i] + '</p><span>posted ' + utils.relativeTime(posts.timestamp[i]) + ' ago</span></li>';
+ '<p><strong>' + username + '</strong>: ' + posts[i].content + '</p><span>posted ' + utils.relativeTime(posts[i].timestamp) + ' ago</span></li>';
a.appendChild(ul);
recent_replies.appendChild(a);

@ -14,7 +14,7 @@
socket.emit('post.stats');
socket.on('post.stats', function(data) {
post_stats.innerHTML = "Our uses have created " + data.topics + " topics and made " + data.posts + " posts.";
post_stats.innerHTML = "Our users have created " + data.topics + " topics and made " + data.posts + " posts.";
});
socket.emit('user.latest', {});

@ -434,14 +434,13 @@
var pid = $(this).parents('li').attr('data-pid');
socket.once('api:posts.getRawPost', function(data) {
quoted = '> ' + data.post.replace(/\n/g, '\n> ') + '\n\n';
require(['composer'], function(cmp) {
cmp.push(tid, null, null, quoted);
});
});
socket.emit('api:posts.getRawPost', { pid: pid });
// $('.post-window textarea').val('> ' + $('#content_' + pid).html() + '\n');
// });
}
});

@ -84,7 +84,7 @@
<a href="/topic/{posts.tid}/{posts.pid}">
<div class="topic-row img-polaroid clearfix">
<span>{posts.content}</span>
<span class="pull-right">{posts.timestamp} ago</span>
<span class="pull-right">{posts.relativeTime} ago</span>
</div>
</a>

@ -12,7 +12,7 @@
<a target="_blank" href="../../topic/{topics.slug}">{topics.title}</a>
<ul>
<li><i class="icon-time"></i> Posted {topics.relativeTime} ago by {topics.username}</li>
<li><i class="icon-comments"></i> {topics.post_count} post(s)</li>
<li><i class="icon-comments"></i> {topics.postcount} post(s)</li>
</ul>
<div class="clear"></div>
</li>

@ -30,7 +30,7 @@
</div>
</div>
<div>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.post_count}</span>{topics.title}</span></h3>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
<small>
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong>
Posted {topics.relativeTime} ago by

@ -6,7 +6,7 @@
<ul class="topics">
<!-- BEGIN topics -->
<li>
<a href="../../topic/{topics.slug}">{topics.title} ({topics.post_count})</a>
<a href="../../topic/{topics.slug}">{topics.title} ({topics.postcount})</a>
<div class="teaser">
<img class="img-polaroid" src="../../graph/users/{topics.teaser_username}/picture" />
<p>

@ -3,7 +3,7 @@
<li>
<div class="row-fluid">
<div class="span2">
<img src="{main_posts.gravatar}" /><br />
<img src="{main_posts.picture}" /><br />
{main_posts.username}
</div>
<div class="span10">
@ -16,7 +16,7 @@
<li>
<div class="row-fluid">
<div class="span2">
<img src="{posts.gravatar}" /><br />
<img src="{posts.picture}" /><br />
{posts.username}
</div>
<div class="span10">

@ -33,7 +33,7 @@
</div>
</div>
<div>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.post_count}</span>{topics.title}</span></h3>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
<small>
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong>
Posted {topics.relativeTime} ago by

@ -17,7 +17,7 @@
<div class="span12">
<div class="post-block">
<a class="main-avatar" href="/users/{main_posts.userslug}">
<img src="{main_posts.gravatar}?s=80&default=identicon" align="left" /><br />
<img src="{main_posts.picture}?s=80&default=identicon" align="left" /><br />
<div class="hover-overlay">
{main_posts.username}<br />
<i class="icon-star"></i><span class="user_rep_{main_posts.uid}">{main_posts.user_rep}</span>
@ -47,7 +47,7 @@
<div id="content_{main_posts.pid}" class="post-content">{main_posts.content}</div>
<div class="post-signature">{main_posts.signature}</div>
<div class="profile-block">
<img class="hidden-desktop" src="{main_posts.gravatar}?s=10&default=identicon" align="left" /> posted by <strong><a class="" href="/users/{main_posts.userslug}">{main_posts.username}</a></strong> {main_posts.relativeTime} ago
<img class="hidden-desktop" src="{main_posts.picture}?s=10&default=identicon" align="left" /> posted by <strong><a class="" href="/users/{main_posts.userslug}">{main_posts.username}</a></strong> {main_posts.relativeTime} ago
<span class="{main_posts.edited-class} hidden-phone">| last edited by <strong><a href="/users/{main_posts.editor}">{main_posts.editor}</a></strong> {main_posts.relativeEditTime} ago</span>
<span class="{main_posts.edited-class}"><i class="icon-edit visible-phone" title="edited by {main_posts.editor} {main_posts.relativeEditTime} ago"></i></span>
<div class="post-buttons visible-phone">
@ -65,7 +65,7 @@
<div class="span1 profile-image-block visible-desktop">
<!--<i class="icon-spinner icon-spin icon-2x pull-left"></i>-->
<a href="/users/{posts.userslug}">
<img src="{posts.gravatar}?s=80&default=identicon" align="left" />
<img src="{posts.picture}?s=80&default=identicon" align="left" />
</a>
<i class="icon-star"></i><span class="user_rep_{posts.uid} formatted-number">{posts.user_rep}</span>
<div id="ids_{posts.pid}_{posts.uid}" class="chat hidden-phone" title="Chat"><i class="icon-comment"></i></div>
@ -82,7 +82,7 @@
<div id="favs_{posts.pid}_{posts.uid}" class="favourite hidden-phone" title="Favourite"><span class="post_rep_{posts.pid}">{posts.post_rep} </span><i class="{posts.fav_star_class}"></i></div>
<div class="post_reply" title="Reply"><i class="icon-reply"></i></div>
</span>
<img class="hidden-desktop" src="{posts.gravatar}?s=10&default=identicon" align="left" /> posted by <strong><a class="username-field" href="/users/{posts.userslug}">{posts.username}</a></strong> {posts.relativeTime} ago
<img class="hidden-desktop" src="{posts.picture}?s=10&default=identicon" align="left" /> posted by <strong><a class="username-field" href="/users/{posts.userslug}">{posts.username}</a></strong> {posts.relativeTime} ago
<span class="{posts.edited-class} hidden-phone">| last edited by <strong><a href="/users/{posts.editor}">{posts.editor}</a></strong> {posts.relativeEditTime} ago</span>
<span class="{posts.edited-class}"><i class="icon-edit visible-phone" title="edited by {posts.editor} {posts.relativeEditTime} ago"></i></span>
</div>

@ -11,14 +11,17 @@ var RDB = require('./../redis.js'),
var slug = cid + '/' + utils.slugify(data.name);
RDB.rpush('categories:cid', cid);
// Topic Info
RDB.set('cid:' + cid + ':name', data.name);
RDB.set('cid:' + cid + ':description', data.description);
RDB.set('cid:' + cid + ':icon', data.icon);
RDB.set('cid:' + cid + ':blockclass', data.blockclass);
RDB.set('cid:' + cid + ':slug', slug);
RDB.set('category:slug:' + slug + ':cid', cid);
RDB.hmset('category:' + cid, {
cid: cid,
name: data.name,
description: data.description,
icon: data.icon,
blockclass: data.blockclass,
slug: slug,
topic_count: 0
});
RDB.set('categoryslug:' + slug + ':cid', cid);
if (callback) callback({'status': 1});
});
@ -29,15 +32,15 @@ var RDB = require('./../redis.js'),
for (var cid in modified) {
var category = modified[cid];
for (var key in category) {
RDB.set('cid:' + cid + ':' + key, category[key]);
RDB.hset('category:' + cid, key, category[key]);
if (key == 'name') {
// reset slugs if name is updated
var slug = cid + '/' + utils.slugify(category[key]);
RDB.set('cid:' + cid + ':slug', slug);
RDB.set('category:slug:' + slug + ':cid', cid);
RDB.hset('category:' + cid, 'slug', slug);
RDB.set('categoryslug:' + slug + ':cid', cid);
RDB.smembers('categories:' + cid + ':tid', function(err, tids) {
var pipe = RDB.multi();

@ -9,14 +9,13 @@ var RDB = require('./redis.js'),
Categories.getCategoryById = function(category_id, current_user, callback) {
RDB.smembers('categories:' + category_id + ':tid', function(err, tids) {
RDB.multi()
.get('cid:' + category_id + ':name')
.smembers('cid:' + category_id + ':active_users')
.get('cid:' + category_id + ':slug')
.exec(function(err, replies) {
var category_name = replies[0],
active_users = replies[1],
category_slug = replies[2];
Categories.getCategoryData(category_id, function(categoryData) {
var category_name = categoryData.name;
category_slug = categoryData.slug;
RDB.smembers('cid:' + category_id + ':active_users', function(err, active_users) {
if (category_name === null) {
callback(false);
@ -57,7 +56,7 @@ var RDB = require('./redis.js'),
}
function getActiveUsers(next) {
user.getMultipleUserFields(active_users, ['username','userslug'], function(users) {
user.getMultipleUserFields(active_users, ['username', 'userslug'], function(users) {
var activeUserData = [];
for(var uid in users) {
activeUserData.push(users[uid]);
@ -85,6 +84,7 @@ var RDB = require('./redis.js'),
});
}
});
});
});
}
@ -115,111 +115,83 @@ var RDB = require('./redis.js'),
// not the permanent location for this function
Categories.getTopicsByTids = function(tids, current_user, callback, category_id /*temporary*/) {
var title = [],
uid = [],
timestamp = [],
lastposttime = [],
slug = [],
postcount = [],
locked = [],
deleted = [],
pinned = [];
for (var i=0, ii=tids.length; i<ii; i++) {
title.push('tid:' + tids[i] + ':title');
uid.push('tid:' + tids[i] + ':uid');
timestamp.push('tid:' + tids[i] + ':timestamp');
lastposttime.push('tid:' + tids[i] + ':lastposttime');
slug.push('tid:' + tids[i] + ':slug');
postcount.push('tid:' + tids[i] + ':postcount');
locked.push('tid:' + tids[i] + ':locked');
deleted.push('tid:' + tids[i] + ':deleted');
pinned.push('tid:' + tids[i] + ':pinned');
var retrieved_topics = [];
var topicCountToLoad = tids.length;
function getTopicInfo(topicData, callback) {
function getUserName(next) {
user.getUserField(topicData.uid, 'username', function(username) {
next(null, username);
});
}
function hasReadTopic(next) {
topics.hasReadTopics([topicData.tid], current_user, function(hasRead) {
next(null, hasRead);
});
}
function getTeaserInfo(next) {
topics.getTeaser(topicData.tid, function(teaser) {
next(null, teaser);
});
}
// temporary. I don't think this call should belong here
function getPrivileges(next) {
Categories.privileges(category_id, current_user, function(user_privs) {
next(null, user_privs);
});
}
async.parallel([getUserName, hasReadTopic, getTeaserInfo, getPrivileges], function(err, results) {
var username = results[0],
hasReadTopic = results[1],
teaserInfo = results[2],
privileges = results[3];
callback({
username: username,
hasread: hasReadTopic,
teaserInfo: teaserInfo,
privileges: privileges
});
});
}
RDB.multi()
.mget(title)
.mget(uid)
.mget(timestamp)
.mget(lastposttime)
.mget(slug)
.mget(postcount)
.mget(locked)
.mget(deleted)
.mget(pinned)
.exec(function(err, replies) {
var retrieved_topics = [];
title = replies[0];
uid = replies[1];
timestamp = replies[2];
lastposttime = replies[3];
slug = replies[4];
postcount = replies[5];
locked = replies[6];
deleted = replies[7];
pinned = replies[8];
function getUserNames(next) {
user.get_usernames_by_uids(uid, function(userNames) {
next(null, userNames);
});
}
for(var i=0; i<tids.length; ++i) {
function hasReadTopics(next) {
topics.hasReadTopics(tids, current_user, function(hasRead) {
next(null, hasRead);
});
}
topics.getTopicData(tids[i], function(topicData) {
function getTeaserInfo(next) {
topics.get_teasers(tids, function(teasers) {
next(null, teasers);
});
}
getTopicInfo(topicData, function(topicInfo) {
// temporary. I don't think this call should belong here
function getPrivileges(next) {
Categories.privileges(category_id, current_user, function(user_privs) {
next(null, user_privs);
});
}
topicData['pin-icon'] = topicData.pinned === '1' ? 'icon-pushpin' : 'none';
topicData['lock-icon'] = topicData.locked === '1' ? 'icon-lock' : 'none';
topicData['deleted-class'] = topicData.deleted === '1' ? 'deleted' : '';
async.parallel([getUserNames, hasReadTopics, getTeaserInfo, getPrivileges], function(err, results) {
var usernames = results[0],
hasReadTopics = results[1],
teaserInfo = results[2],
privileges = results[3];
for (var i=0, ii=tids.length; i<ii; i++) {
if (!deleted[i] || (deleted[i] && privileges.view_deleted) || uid[i] === current_user) {
retrieved_topics.push({
'tid' : tids[i],
'title' : title[i],
'uid' : uid[i],
'username': usernames[i],
'timestamp' : timestamp[i],
'lastposttime' : lastposttime[i],
'relativeTime': utils.relativeTime(timestamp[i]),
'slug' : slug[i],
'post_count' : postcount[i],
'lock-icon': locked[i] === '1' ? 'icon-lock' : 'none',
'deleted': deleted[i],
'deleted-class': deleted[i] ? 'deleted' : '',
'pinned': parseInt(pinned[i] || 0), // For sorting purposes
'pin-icon': pinned[i] === '1' ? 'icon-pushpin' : 'none',
'badgeclass' : (hasReadTopics[i] && current_user !=0) ? '' : 'badge-important',
'teaser_text': teaserInfo[i].text,
'teaser_username': teaserInfo[i].username,
'teaser_timestamp': utils.relativeTime(teaserInfo[i].timestamp)
});
}
}
topicData.relativeTime = utils.relativeTime(topicData.timestamp);
topicData.username = topicInfo.username;
topicData.badgeclass = (topicInfo.hasread && current_user != 0) ? '' : 'badge-important';
topicData.teaser_text = topicInfo.teaserInfo.text,
topicData.teaser_username = topicInfo.teaserInfo.username;
topicData.teaser_timestamp = utils.relativeTime(topicInfo.teaserInfo.timestamp);
callback(retrieved_topics);
if (!topicData.deleted || (topicData.deleted && topicInfo.privileges.view_deleted) || topicData.uid === current_user)
retrieved_topics.push(topicData);
else
--topicCountToLoad;
if(retrieved_topics.length === topicCountToLoad)
callback(retrieved_topics);
});
});
}
}
Categories.getAllCategories = function(callback, current_user) {
@ -300,8 +272,17 @@ var RDB = require('./redis.js'),
});
}
Categories.hasReadCategory = function(cid, uid, callback) {
RDB.sismember('cid:' + cid + ':read_by_uid', uid, function(err, hasRead) {
RDB.handle(err);
callback(hasRead);
});
}
Categories.getRecentReplies = function(cid, callback) {
RDB.zrevrange('categories:recent_posts:cid:' + cid, 0, 4, function(err, pids) {
if (pids.length == 0) {
callback(false);
return;
@ -312,69 +293,40 @@ var RDB = require('./redis.js'),
});
}
Categories.getCategoryData = function(cid, callback) {
RDB.hgetall('category:' + cid, function(err, data) {
if(err === null)
callback(data);
else
console.log(err);
});
}
Categories.getCategories = function(cids, callback, current_user) {
if (cids.length === 0) {
callback({'categories' : []});
return;
}
var name = [],
description = [],
icon = [],
blockclass = [],
slug = [],
topic_count = [],
has_read = {};
for (var i=0, ii=cids.length; i<ii; i++) {
name.push('cid:' + cids[i] + ':name');
description.push('cid:' + cids[i] + ':description');
icon.push('cid:' + cids[i] + ':icon');
blockclass.push('cid:' + cids[i] + ':blockclass');
slug.push('cid:' + cids[i] + ':slug');
topic_count.push('cid:' + cids[i] + ':topiccount');
}
RDB.multi()
.mget(name)
.mget(description)
.mget(icon)
.mget(blockclass)
.mget(slug)
.mget(topic_count)
.exec(function(err, replies) {
name = replies[0];
description = replies[1];
icon = replies[2];
blockclass = replies[3];
slug = replies[4];
topic_count = replies[5];
var categories = [];
for(var i=0; i<cids.length; ++i) {
Categories.getCategoryData(cids[i], function(categoryData) {
function generateCategories() {
var categories = [];
for (var i=0, ii=cids.length; i<ii; i++) {
categories.push({
'name' : name[i],
'cid' : cids[i],
'slug' : slug[i],
'description' : description[i],
'blockclass' : blockclass[i],
'icon' : icon[i],
'badgeclass' : (!topic_count[i] || (has_read[i] && current_user !=0)) ? '' : 'badge-important',
'topic_count' : topic_count[i] || 0
});
}
if(!categoryData)
return;
callback({'categories': categories});
}
Categories.hasReadCategory(categoryData.cid, current_user, function(hasRead) {
categoryData['badgeclass'] = (parseInt(categoryData.topic_count,10) === 0 || (hasRead && current_user != 0)) ? '' : 'badge-important';
Categories.hasReadCategories(cids, current_user, function(read_data) {
has_read = read_data;
generateCategories();
});
categories.push(categoryData);
if(categories.length === cids.length)
callback({'categories': categories});
}) ;
});
}
};
}(exports));

@ -1,4 +1,5 @@
var RDB = require('./redis.js'),
posts = require('./posts.js'),
user = require('./user.js');
(function(Favourites) {
@ -20,15 +21,15 @@ var RDB = require('./redis.js'),
return;
}
RDB.get('pid:' + pid + ':uid', function(err, uid_of_poster) {
RDB.handle(err);
posts.getPostField(pid, 'uid', function(uid_of_poster) {
Favourites.hasFavourited(pid, uid, function(hasFavourited) {
if (hasFavourited == false) {
RDB.sadd('pid:' + pid + ':users_favourited', uid);
RDB.incr('pid:' + pid + ':rep');
RDB.hincrby('post:' + pid, 'reputation', 1);
if (uid !== uid_of_poster) user.incrementUserFieldBy(uid_of_poster, 'reputation', 1);
if (uid !== uid_of_poster)
user.incrementUserFieldBy(uid_of_poster, 'reputation', 1);
if (room_id) {
io.sockets.in(room_id).emit('event:rep_up', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
@ -54,16 +55,16 @@ var RDB = require('./redis.js'),
return;
}
RDB.get('pid:' + pid + ':uid', function(err, uid_of_poster) {
RDB.handle(err);
posts.getPostField(pid, 'uid', function(uid_of_poster) {
Favourites.hasFavourited(pid, uid, function(hasFavourited) {
if (hasFavourited == true) {
RDB.srem('pid:' + pid + ':users_favourited', uid);
RDB.decr('pid:' + pid + ':rep');
RDB.hincrby('post:' + pid, 'reputation', -1);
if (uid !== uid_of_poster) user.incrementUserFieldBy(uid_of_poster, 'reputation', -1);
if (uid !== uid_of_poster)
user.incrementUserFieldBy(uid_of_poster, 'reputation', -1);
if (room_id) {
io.sockets.in(room_id).emit('event:rep_down', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
@ -91,9 +92,10 @@ var RDB = require('./redis.js'),
data[post_id] = hasFavourited;
loaded ++;
if (loaded == pids.length) callback(data);
if (loaded === pids.length)
callback(data);
});
}(pids[i]))
}(pids[i]));
}
}

@ -29,24 +29,8 @@
Feed.updateTopic = function(tid, cid) {
var cache_time_in_seconds = 60;
function getTopicData(next) {
topics.getTopicById(tid, 0, function(topicData) {
next(null, topicData);
});
}
function getPostsData(next) {
posts.getPostsByTid(tid, 0, -20, -1, function(postsData) {
next(null, postsData);
});
}
async.parallel([getTopicData, getPostsData], function(err, results) {
var topicData = results[0],
postsData = results[1].postData,
userData = results[1].userData,
location = '/topic/' + topicData.slug,
topics.getTopicWithPosts(tid, 0, function(topicData) {
var location = '/topic/' + topicData.slug,
xml_url = '/topic/' + tid + '.rss';
var post = topicData.main_posts[0];
@ -55,18 +39,20 @@
var feed = createFeed(topicData.topic_name, '', location, xml_url, post.username, urn);
var title;
for (var i = 0, ii = postsData.pid.length; i < ii; i++) {
urn = 'urn:' + cid + ':' + tid + ':' + postsData.pid[i];
title = 'Reply to ' + topicData.topic_name + ' on ' + (new Date(parseInt(postsData.timestamp[i], 10)).toUTCString());
var topic_posts = topicData.main_posts.concat(topicData.posts);
for (var i = 0, ii = topic_posts.length; i < ii; i++) {
urn = 'urn:' + cid + ':' + tid + ':' + topic_posts[i].pid;
title = 'Reply to ' + topicData.topic_name + ' on ' + (new Date(parseInt(topic_posts[i].timestamp, 10)).toUTCString());
feed.addNewItem(
title,
location,
postsData.timestamp[i],
postsData.content[i],
topic_posts[i].timestamp,
topic_posts[i].content,
{
'urn' : urn,
'username' : userData[postsData.uid[i]].username
'username' : topic_posts[i].username
}
);
}

@ -23,24 +23,22 @@ marked.setOptions({
//todo: break early if one condition is true
function getThreadPrivileges(next) {
posts.get_tid_by_pid(pid, function(tid) {
threadTools.privileges(tid, uid, function(privileges) {
next(null, privileges);
});
posts.getPostField(pid, 'tid', function(tid) {
threadTools.privileges(tid, uid, function(privileges) {
next(null, privileges);
});
});
}
function isOwnPost(next) {
RDB.get('pid:' + pid + ':uid', function(err, author) {
if (author && parseInt(author) > 0) {
next(null, author === uid);
}
});
posts.getPostField(pid, 'uid', function(author) {
if (author && parseInt(author) > 0) {
next(null, author === uid);
}
});
}
function hasEnoughRep(next) {
// DRY fail in threadTools.
user.getUserField(uid, 'reputation', function(reputation) {
next(null, reputation >= global.config['privileges:manage_content']);
});
@ -55,23 +53,25 @@ marked.setOptions({
}
PostTools.edit = function(uid, pid, title, content) {
var success = function() {
RDB.set('pid:' + pid + ':content', content);
RDB.set('pid:' + pid + ':edited', Date.now());
RDB.set('pid:' + pid + ':editor', uid);
posts.get_tid_by_pid(pid, function(tid) {
PostTools.isMain(pid, tid, function(isMainPost) {
if (isMainPost) RDB.set('tid:' + tid + ':title', title);
io.sockets.in('topic_' + tid).emit('event:post_edited', {
pid: pid,
title: title,
content: marked(content || '')
});
posts.setPostField(pid, 'content', content);
posts.setPostField(pid, 'edited', Date.now());
posts.setPostField(pid, 'editor', uid);
posts.getPostField(pid, 'tid', function(tid) {
PostTools.isMain(pid, tid, function(isMainPost) {
if (isMainPost)
topics.setTopicField(tid, 'title', title);
io.sockets.in('topic_' + tid).emit('event:post_edited', {
pid: pid,
title: title,
content: marked(content || '')
});
});
};
});
};
PostTools.privileges(pid, uid, function(privileges) {
if (privileges.editable) {
@ -82,14 +82,14 @@ marked.setOptions({
PostTools.delete = function(uid, pid) {
var success = function() {
RDB.set('pid:' + pid + ':deleted', 1);
posts.setPostField(pid, 'deleted', 1);
posts.get_tid_by_pid(pid, function(tid) {
io.sockets.in('topic_' + tid).emit('event:post_deleted', {
pid: pid
});
posts.getPostField(pid, 'tid', function(tid) {
io.sockets.in('topic_' + tid).emit('event:post_deleted', {
pid: pid
});
};
});
};
PostTools.privileges(pid, uid, function(privileges) {
if (privileges.editable) {
@ -100,14 +100,14 @@ marked.setOptions({
PostTools.restore = function(uid, pid) {
var success = function() {
RDB.del('pid:' + pid + ':deleted');
posts.setPostField(pid, 'deleted', 0);
posts.get_tid_by_pid(pid, function(tid) {
io.sockets.in('topic_' + tid).emit('event:post_restored', {
pid: pid
});
posts.getPostField(pid, 'tid', function(tid) {
io.sockets.in('topic_' + tid).emit('event:post_restored', {
pid: pid
});
};
});
};
PostTools.privileges(pid, uid, function(privileges) {
if (privileges.editable) {
@ -116,65 +116,5 @@ marked.setOptions({
});
}
PostTools.constructPostObject = function(rawPosts, tid, current_user, privileges, callback) {
var postObj = [];
async.waterfall([
function(next) {
if (!privileges) {
threadTools.privileges(tid, current_user, function(privs) {
privileges = privs;
next();
});
} else {
next();
}
},
function(next) {
var postData = rawPosts.postData,
userData = rawPosts.userData,
voteData = rawPosts.voteData;
if (!postData) {
return next(null, []);
}
for (var i=0, ii= postData.pid.length; i<ii; i++) {
var uid = postData.uid[i],
pid = postData.pid[i];
// ############ to be moved into posts.getPostsByTid ############
if (postData.deleted[i] === null || (postData.deleted[i] === '1' && privileges.view_deleted) || current_user === uid) {
var post_obj = {
'pid' : pid,
'uid' : uid,
'content' : marked(postData.content[i] || ''),
'post_rep' : postData.reputation[i] || 0,
'timestamp' : postData.timestamp[i],
'relativeTime': utils.relativeTime(postData.timestamp[i]),
'username' : userData[uid].username || 'anonymous',
'userslug' : userData[uid].userslug || '',
'user_rep' : userData[uid].reputation || 0,
'gravatar' : userData[uid].picture || 'http://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e',
'signature' : marked(userData[uid].signature || ''),
'fav_star_class' : voteData[pid] ? 'icon-star' : 'icon-star-empty',
'display_moderator_tools': (uid == current_user || privileges.editable) ? 'show' : 'none',
'edited-class': postData.editor[i] !== null ? '' : 'none',
'editor': postData.editor[i] !== null ? userData[postData.editor[i]].username : '',
'relativeEditTime': postData.editTime !== null ? utils.relativeTime(postData.editTime[i]) : '',
'deleted': postData.deleted[i] || '0'
};
postObj.push(post_obj);
}
// ########## end to be moved into posts.getPostsByTid ############
}
next(null);
}
], function(err) {
callback(postObj);
});
}
}(exports));

@ -14,13 +14,14 @@ marked.setOptions({
});
(function(Posts) {
Posts.getPostsByTid = function(tid, current_user, start, end, callback) {
Posts.getPostsByTid = function(tid, start, end, callback) {
RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
RDB.handle(err);
topics.markAsRead(tid, current_user);
if (pids.length) {
Posts.getPostsByPids(pids, current_user, function(posts) {
Posts.getPostsByPids(pids, function(posts) {
callback(posts);
});
} else {
@ -30,130 +31,135 @@ marked.setOptions({
}
});
}
Posts.addUserInfoToPost = function(post, callback) {
user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'picture', 'signature'], function(userData) {
// todo, getPostsByPids has duplicated stuff, have that call this fn - after userinfo calls are pulled out.
Posts.getPostSummaryByPids = function(pids, callback) {
var content = [], uid = [], timestamp = [];
for (var i=0, ii=pids.length; i<ii; i++) {
content.push('pid:' + pids[i] + ':content');
uid.push('pid:' + pids[i] + ':uid');
timestamp.push('pid:' + pids[i] + ':timestamp');
}
post.username = userData.username || 'anonymous';
post.userslug = userData.userslug || '';
post.user_rep = userData.reputation || 0;
post.picture = userData.picture || 'http://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e';
post.signature = marked(userData.signature || '');
RDB.multi()
.mget(content)
.mget(uid)
.mget(timestamp)
.exec(function(err, replies) {
post_data = {
pids: pids,
content: replies[0],
uid: replies[1],
timestamp: replies[2]
}
callback();
});
}
// below, to be deprecated
user.getMultipleUserFields(post_data.uid, ['username','reputation','picture'], function(user_details) {
callback({
users: user_details,
posts: post_data
Posts.getPostSummaryByPids = function(pids, callback) {
var returnData = [];
var loaded = 0;
for(var i=0, ii=pids.length; i<ii; ++i) {
(function(index, pid) {
Posts.getPostFields(pids[i], ['pid', 'content', 'uid', 'timestamp'], function(postData) {
Posts.addUserInfoToPost(postData, function() {
returnData[index] = postData;
++loaded;
if(loaded === pids.length) {
callback(returnData);
}
});
});
// above, to be deprecated
});
}(i, pids[i]));
}
};
Posts.getPostsByPids = function(pids, current_user, callback) {
var content = [], uid = [], timestamp = [], post_rep = [], editor = [], editTime = [], deleted = [], tid = [];
for (var i=0, ii=pids.length; i<ii; i++) {
content.push('pid:' + pids[i] + ':content');
uid.push('pid:' + pids[i] + ':uid');
timestamp.push('pid:' + pids[i] + ':timestamp');
post_rep.push('pid:' + pids[i] + ':rep');
editor.push('pid:' + pids[i] + ':editor');
editTime.push('pid:' + pids[i] + ':edited');
deleted.push('pid:' + pids[i] + ':deleted');
tid.push('pid:' + pids[i] + ':tid');
}
Posts.getPostData = function(pid, callback) {
RDB.hgetall('post:' + pid, function(err, data) {
if(err === null)
callback(data);
else
console.log(err);
});
}
Posts.getPostFields = function(uid, fields, callback) {
RDB.hmget('post:' + uid, fields, function(err, data) {
if(err === null) {
var returnData = {};
for(var i=0, ii=fields.length; i<ii; ++i) {
returnData[fields[i]] = data[i];
}
function getFavouritesData(next) {
favourites.getFavouritesByPostIDs(pids, current_user, function(fav_data) {
next(null, fav_data);
}); // to be moved
}
callback(returnData);
}
else
console.log(err);
});
}
function getPostData(next) {
RDB.multi()
.mget(content)
.mget(uid)
.mget(timestamp)
.mget(post_rep)
.mget(editor)
.mget(editTime)
.mget(deleted)
.mget(tid)
.exec(function(err, replies) {
post_data = {
pid: pids,
content: replies[0],
uid: replies[1],
timestamp: replies[2],
reputation: replies[3],
editor: replies[4],
editTime: replies[5],
deleted: replies[6],
tid: replies[7]
};
Posts.getPostsByPids = function(pids, callback) {
var posts = [],
loaded = 0;
// below, to be deprecated
// Add any editors to the user_data object
for(var x = 0, numPosts = post_data.editor.length; x < numPosts; x++) {
if (post_data.editor[x] !== null && post_data.uid.indexOf(post_data.editor[x]) === -1) {
post_data.uid.push(post_data.editor[x]);
}
for(var i=0, ii=pids.length; i<ii; ++i) {
(function(index, pid) {
Posts.getPostData(pid, function(postData) {
if(postData) {
postData.relativeTime = utils.relativeTime(postData.timestamp);
postData.post_rep = postData.reputation;
postData['edited-class'] = postData.editor !== '' ? '' : 'none';
postData['relativeEditTime'] = postData.edited !== '0' ? utils.relativeTime(postData.edited) : '';
posts[index] = postData;
}
user.getMultipleUserFields(post_data.uid, ['username', 'userslug', 'reputation', 'picture', 'signature'], function(user_details) {
next(null, {
users: user_details,
posts: post_data
});
});
// above, to be deprecated
++loaded;
if(loaded === pids.length)
callback(posts);
});
}(i, pids[i]));
}
}
async.parallel([getFavouritesData, getPostData], function(err, results) {
callback({
'voteData' : results[0], // to be moved
'userData' : results[1].users, // to be moved
'postData' : results[1].posts
});
Posts.getPostField = function(pid, field, callback) {
RDB.hget('post:' + pid, field, function(err, data) {
if(err === null)
callback(data);
else
console.log(err);
});
}
Posts.get_tid_by_pid = function(pid, callback) {
RDB.get('pid:' + pid + ':tid', function(err, tid) {
if (tid && parseInt(tid) > 0) {
callback(tid);
} else {
callback(false);
Posts.setPostField = function(pid, field, value) {
RDB.hset('post:' + pid, field, value);
}
Posts.getPostFields = function(pid, fields, callback) {
RDB.hmget('post:' + pid, fields, function(err, data) {
if(err === null) {
var returnData = {};
for(var i=0, ii=fields.length; i<ii; ++i) {
returnData[fields[i]] = data[i];
}
callback(returnData);
}
});
else
console.log(err);
});
}
Posts.get_cid_by_pid = function(pid, callback) {
Posts.get_tid_by_pid(pid, function(tid) {
if (tid) topics.get_cid_by_tid(tid, function(cid) {
if (cid) {
callback(cid);
} else {
callback(false);
}
});
})
Posts.getPostField(pid, 'tid', function(tid) {
if (tid) {
topics.getTopicField(tid, 'cid', function(cid) {
if (cid) {
callback(cid);
} else {
callback(false);
}
});
}
});
}
Posts.reply = function(socket, tid, uid, content) {
@ -169,6 +175,7 @@ marked.setOptions({
user.getUserField(uid, 'lastposttime', function(lastposttime) {
if(Date.now() - lastposttime < config.post_delay) {
socket.emit('event:alert', {
title: 'Too many posts!',
@ -183,7 +190,7 @@ marked.setOptions({
if (pid > 0) {
RDB.rpush('tid:' + tid + ':posts', pid);
RDB.del('tid:' + tid + ':read_by_uid'); // let everybody know there is an unread post
RDB.del('tid:' + tid + ':read_by_uid');
Posts.get_cid_by_pid(pid, function(cid) {
RDB.del('cid:' + cid + ':read_by_uid', function(err, data) {
@ -193,7 +200,12 @@ marked.setOptions({
RDB.zadd('categories:recent_posts:cid:' + cid, Date.now(), pid);
});
Posts.getTopicPostStats(socket);
// Send notifications to users who are following this topic
threadTools.notify_followers(tid, uid);
socket.emit('event:alert', {
title: 'Reply Successful',
message: 'You have successfully replied. Click here to view your reply.',
@ -201,38 +213,29 @@ marked.setOptions({
timeout: 2000
});
Posts.getTopicPostStats(socket);
// Send notifications to users who are following this topic
threadTools.notify_followers(tid, uid);
user.getUserFields(uid, ['username','reputation','picture','signature'], function(data) {
var timestamp = Date.now();
var socketData = {
'posts' : [
{
'pid' : pid,
'content' : marked(content || ''),
'uid' : uid,
'username' : data.username || 'anonymous',
'user_rep' : data.reputation || 0,
'post_rep' : 0,
'gravatar' : data.picture,
'signature' : marked(data.signature || ''),
'timestamp' : timestamp,
'relativeTime': utils.relativeTime(timestamp),
'fav_star_class' :'icon-star-empty',
'edited-class': 'none',
'editor': '',
}
]
};
var timestamp = Date.now();
var socketData = {
'posts' : [
{
'pid' : pid,
'content' : marked(content || ''),
'uid' : uid,
'post_rep' : 0,
'timestamp' : timestamp,
'relativeTime': utils.relativeTime(timestamp),
'fav_star_class' :'icon-star-empty',
'edited-class': 'none',
'editor': '',
}
]
};
posts.addUserInfoToPost(socketData['posts'][0], function() {
io.sockets.in('topic_' + tid).emit('event:new_post', socketData);
io.sockets.in('recent_posts').emit('event:new_post', socketData);
});
});
} else {
socket.emit('event:alert', {
title: 'Reply Unsuccessful',
@ -246,57 +249,53 @@ marked.setOptions({
};
Posts.create = function(uid, tid, content, callback) {
if (uid === null) return;
if (uid === null) {
callback(-1);
return;
}
RDB.get('tid:' + tid + ':locked', function(err, locked) {
RDB.handle(err);
topics.isLocked(tid, function(locked) {
if (!locked || locked === '0') {
RDB.incr('global:next_post_id', function(err, pid) {
RDB.handle(err);
var timestamp = Date.now();
// Posts Info
RDB.set('pid:' + pid + ':content', content);
RDB.set('pid:' + pid + ':uid', uid);
RDB.set('pid:' + pid + ':timestamp', timestamp);
RDB.set('pid:' + pid + ':rep', 0);
RDB.set('pid:' + pid + ':tid', tid);
RDB.incr('tid:' + tid + ':postcount');
RDB.zadd(schema.topics().recent, timestamp, tid);
RDB.set('tid:' + tid + ':lastposttime', timestamp);
RDB.incr('totalpostcount');
RDB.hmset('post:' + pid, {
'pid': pid,
'uid': uid,
'tid': tid,
'content': content,
'timestamp': timestamp,
'reputation': 0,
'editor': '',
'edited': 0,
'deleted': 0
});
user.getUserFields(uid, ['username'], function(data) { // todo parallel
//add active users to this category
RDB.get('tid:' + tid + ':cid', function(err, cid) {
RDB.handle(err);
topics.increasePostCount(tid);
topics.setTopicField(tid, 'lastposttime', timestamp);
topics.addToRecent(tid, timestamp);
RDB.incr('totalpostcount');
topics.getTopicField(tid, 'cid', function(cid) {
RDB.handle(err);
feed.updateTopic(tid, cid);
feed.updateTopic(tid, cid);
// this is a bit of a naive implementation, defn something to look at post-MVP
RDB.scard('cid:' + cid + ':active_users', function(amount) {
if (amount > 10) {
RDB.spop('cid:' + cid + ':active_users');
}
// this is a bit of a naive implementation, defn something to look at post-MVP
RDB.scard('cid:' + cid + ':active_users', function(amount) {
if (amount > 10) {
RDB.spop('cid:' + cid + ':active_users');
}
//RDB.sadd('cid:' + cid + ':active_users', data.username);
RDB.sadd('cid:' + cid + ':active_users', uid);
});
RDB.sadd('cid:' + cid + ':active_users', uid);
});
});
// User Details - move this out later
RDB.lpush('uid:' + uid + ':posts', pid);
user.incrementUserFieldBy(uid, 'postcount', 1);
user.setUserField(uid, 'lastposttime', timestamp);
user.sendPostNotificationToFollowers(uid, tid, pid);
user.onNewPostMade(uid, tid, pid, timestamp);
if (callback)
callback(pid);
@ -306,40 +305,19 @@ marked.setOptions({
}
});
}
Posts.getRawContent = function(pid, callback) {
RDB.get('pid:' + pid + ':content', function(err, raw) {
callback(raw);
});
}
Posts.getPostsByUid = function(uid, callback) {
Posts.getPostsByUid = function(uid, start, end, callback) {
RDB.lrange('uid:' + uid + ':posts', 0, 10, function(err, pids) {
if(err === null) {
if(pids && pids.length) {
Posts.getPostsByPids(pids, uid, function(posts) {
var returnData = [];
var len = posts.postData.pid.length;
for (var i=0; i < len; ++i) {
returnData.push({
pid: posts.postData.pid[i],
content: posts.postData.content[i],
timestamp: utils.relativeTime(posts.postData.timestamp[i]),
tid: posts.postData.tid[i]
});
};
callback(returnData);
});
}
else
callback([]);
user.getPostIds(uid, start, end, function(pids) {
if(pids && pids.length) {
Posts.getPostsByPids(pids, function(posts) {
callback(posts);
});
}
else
callback([]);
});
}

@ -255,10 +255,10 @@ var user = require('./../user.js'),
if (!req.params.section && !req.params.userslug) {
user.getUserList(function(data) {
data = data.sort(function(a, b) {
return b.joindate - a.joindate;
});
res.json({search_display: 'none', users:data});
data = data.sort(function(a, b) {
return b.joindate - a.joindate;
});
res.json({search_display: 'none', users:data});
});
}
else if(String(req.params.section).toLowerCase() === 'following') {
@ -292,7 +292,7 @@ var user = require('./../user.js'),
user.isFollowing(callerUID, userData.theirid, function(isFollowing) {
posts.getPostsByUid(userData.theirid, function(posts) {
posts.getPostsByUid(userData.theirid, 0, 9, function(posts) {
userData.posts = posts;
userData.isFollowing = isFollowing;

@ -13,19 +13,6 @@
Schema.topics = function(tid) {
return {
/* strings */
title: 'tid:' + tid + ':title',
locked: 'tid:' + tid + ':locked',
category_name: 'tid:' + tid + ':category_name',
category_slug: 'tid:' + tid + ':category_slug',
deleted: 'tid:' + tid + ':deleted',
pinned: 'tid:' + tid + ':pinned',
uid: 'tid:' + tid + ':uid',
timestamp: 'tid:' + tid + ':timestamp',
slug: 'tid:' + tid + ':slug',
postcount: 'tid:' + tid + ':postcount',
cid: 'tid:' + tid + ':cid',
/* sets */
tid: 'topics:tid',
read_by_uid: 'tid:' + tid + ':read_by_uid',

@ -11,7 +11,7 @@ var RDB = require('./redis.js'),
//todo: break early if one condition is true
function getCategoryPrivileges(next) {
topics.get_cid_by_tid(tid, function(cid) {
topics.getTopicField(tid, 'cid', function(cid) {
categories.privileges(cid, uid, function(privileges) {
next(null, privileges);
});
@ -19,8 +19,6 @@ var RDB = require('./redis.js'),
}
function hasEnoughRep(next) {
// DRY fail in postTools
user.getUserField(uid, 'reputation', function(reputation) {
next(null, reputation >= global.config['privileges:manage_topic']);
});
@ -38,8 +36,7 @@ var RDB = require('./redis.js'),
ThreadTools.lock = function(tid, uid, socket) {
ThreadTools.privileges(tid, uid, function(privileges) {
if (privileges.editable) {
// Mark thread as locked
RDB.set('tid:' + tid + ':locked', 1);
topics.setTopicField(tid, 'locked', 1);
if (socket) {
io.sockets.in('topic_' + tid).emit('event:topic_locked', {
@ -59,8 +56,7 @@ var RDB = require('./redis.js'),
ThreadTools.unlock = function(tid, uid, socket) {
ThreadTools.privileges(tid, uid, function(privileges) {
if (privileges.editable) {
// Mark thread as unlocked
RDB.del('tid:' + tid + ':locked');
topics.setTopicField(tid, 'locked', 0);
if (socket) {
io.sockets.in('topic_' + tid).emit('event:topic_unlocked', {
@ -80,8 +76,8 @@ var RDB = require('./redis.js'),
ThreadTools.delete = function(tid, uid, socket) {
ThreadTools.privileges(tid, uid, function(privileges) {
if (privileges.editable) {
// Mark thread as deleted
RDB.set('tid:' + tid + ':deleted', 1);
topics.setTopicField(tid, 'deleted', 1);
ThreadTools.lock(tid, uid);
if (socket) {
@ -102,8 +98,8 @@ var RDB = require('./redis.js'),
ThreadTools.restore = function(tid, uid, socket) {
ThreadTools.privileges(tid, uid, function(privileges) {
if (privileges.editable) {
// Mark thread as restored
RDB.del('tid:' + tid + ':deleted');
topics.setTopicField(tid, 'deleted', 0);
ThreadTools.unlock(tid, uid);
if (socket) {
@ -124,8 +120,8 @@ var RDB = require('./redis.js'),
ThreadTools.pin = function(tid, uid, socket) {
ThreadTools.privileges(tid, uid, function(privileges) {
if (privileges.editable) {
// Mark thread as pinned
RDB.set('tid:' + tid + ':pinned', 1);
topics.setTopicField(tid, 'pinned', 1);
if (socket) {
io.sockets.in('topic_' + tid).emit('event:topic_pinned', {
@ -145,8 +141,8 @@ var RDB = require('./redis.js'),
ThreadTools.unpin = function(tid, uid, socket) {
ThreadTools.privileges(tid, uid, function(privileges) {
if (privileges.editable) {
// Mark thread as unpinned
RDB.del('tid:' + tid + ':pinned');
topics.setTopicField(tid, 'pinned', 0);
if (socket) {
io.sockets.in('topic_' + tid).emit('event:topic_unpinned', {
@ -164,15 +160,17 @@ var RDB = require('./redis.js'),
}
ThreadTools.move = function(tid, cid, socket) {
RDB.get('tid:' + tid + ':cid', function(err, oldCid) {
RDB.handle(err);
topics.getTopicField(tid, 'cid', function(oldCid) {
RDB.smove('categories:' + oldCid + ':tid', 'categories:' + cid + ':tid', tid, function(err, result) {
if (!err && result === 1) {
RDB.set('tid:' + tid + ':cid', cid);
topics.setTopicField(tid, 'cid', cid);
categories.getCategories([cid], function(data) {
RDB.set('tid:' + tid + ':category_name', data.categories[0].name);
RDB.set('tid:' + 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);
});
socket.emit('api:topic.move', {
@ -233,11 +231,16 @@ var RDB = require('./redis.js'),
ThreadTools.notify_followers = function(tid, exceptUid) {
async.parallel([
function(next) {
topics.get_topic(tid, 0, function(threadData) {
notifications.create(threadData.teaser_username + ' has posted a reply to: "' + threadData.title + '"', null, '/topic/' + tid, 'topic:' + tid, function(nid) {
next(null, nid);
topics.getTopicField(tid, 'title', function(title) {
topics.getTeaser(tid, function(teaser) {
notifications.create(teaser.username + ' has posted a reply to: "' + title + '"', null, '/topic/' + tid, 'topic:' + tid, function(nid) {
next(null, nid);
});
});
});
},
function(next) {
ThreadTools.get_followers(tid, function(err, followers) {

@ -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_teaser], 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.getTopicDataWithUsername(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.getTopicField(tid, 'cid', function(cid) {
categories.isTopicsRead(cid, uid, function(read) {
if(read) {
@ -243,13 +245,13 @@ marked.setOptions({
});
}
Topics.get_teasers = function(tids, callback) {
Topics.getTeasers = 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_teaser(tid, function(teaser_info) {
Topics.getTeaser(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_teaser = function(tid, callback) {
Topics.getTeaser = 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('topicslug:' + 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.getTopicForCategoryView(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));

@ -11,7 +11,7 @@ var utils = require('./../public/src/utils.js'),
(function(User) {
User.getUserField = function(uid, field, callback) {
RDB.hget('user:'+uid, field, function(err, data) {
RDB.hget('user:' + uid, field, function(err, data) {
if(err === null)
callback(data);
else
@ -20,7 +20,7 @@ var utils = require('./../public/src/utils.js'),
}
User.getUserFields = function(uid, fields, callback) {
RDB.hmget('user:'+uid, fields, function(err, data) {
RDB.hmget('user:' + uid, fields, function(err, data) {
if(err === null) {
var returnData = {};
@ -55,13 +55,13 @@ var utils = require('./../public/src/utils.js'),
loaded ++;
if (loaded == uuids.length) callback(data);
});
}(uuids[i]))
}(uuids[i]));
}
}
User.getUserData = function(uid, callback) {
RDB.hgetall('user:'+uid, function(err, data) {
RDB.hgetall('user:' + uid, function(err, data) {
if(err === null) {
if(data) {
if(data['password'])
@ -107,11 +107,11 @@ var utils = require('./../public/src/utils.js'),
}
User.setUserField = function(uid, field, value) {
RDB.hset('user:'+uid, field, value);
RDB.hset('user:' + uid, field, value);
}
User.incrementUserFieldBy = function(uid, field, value) {
RDB.hincrby('user:'+uid, field, value);
RDB.hincrby('user:' + uid, field, value);
}
User.getUserList = function(callback) {
@ -129,7 +129,7 @@ var utils = require('./../public/src/utils.js'),
User.getUserData(uid, function(userData) {
data.push(userData);
if(data.length == userkeys.length)
if(data.length === userkeys.length)
callback(data);
});
}
@ -221,13 +221,13 @@ var utils = require('./../public/src/utils.js'),
RDB.lpush('userlist', username);
io.sockets.emit('user.latest', {userslug: userslug, username: username});
callback(null, uid);
if (password) {
User.hashPassword(password, function(hash) {
RDB.hset('user:'+uid, 'password', hash);
User.setUserField(uid, 'password', hash);
});
}
callback(null, uid);
});
});
};
@ -276,6 +276,38 @@ var utils = require('./../public/src/utils.js'),
});
}
User.onNewPostMade = function(uid, tid, pid, timestamp) {
User.addPostIdToUser(uid, pid)
User.incrementUserFieldBy(uid, 'postcount', 1);
User.setUserField(uid, 'lastposttime', timestamp);
User.sendPostNotificationToFollowers(uid, tid, pid);
}
User.addPostIdToUser = function(uid, pid) {
RDB.lpush('uid:' + uid + ':posts', pid);
}
User.addTopicIdToUser = function(uid, tid) {
RDB.lpush('uid:' + uid + ':topics', tid);
}
User.getPostIds = function(uid, start, end, callback) {
RDB.lrange('uid:' + uid + ':posts', start, end, function(err, pids) {
if(err === null) {
if(pids && pids.length)
callback(pids);
else
callback([]);
}
else {
console.log(err);
callback([]);
}
});
}
User.sendConfirmationEmail = function (email) {
if (global.config['email:host'] && global.config['email:port'] && global.config['email:from']) {
var confirm_code = utils.generateUUID(),
@ -397,7 +429,7 @@ var utils = require('./../public/src/utils.js'),
User.getUserField(uid, 'username', function(username) {
RDB.smembers('followers:'+uid, function(err, followers) {
topics.getSlug(tid, function(slug) {
topics.getTopicField(tid, 'slug', function(slug) {
var message = username + ' made a new post';

@ -134,7 +134,7 @@ var express = require('express'),
var topic_url = tid + (req.params.slug ? '/' + req.params.slug : '');
topics.getTopicById(tid, ((req.user) ? req.user.uid : 0), function(topic) {
topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), function(topic) {
res.send(
app.build_header(res) +
'\n\t<noscript>\n' + templates['noscript/header'] + templates['noscript/topic'].parse(topic) + '\n\t</noscript>' +
@ -241,7 +241,7 @@ var express = require('express'),
res.json(data);
break;
case 'topic' :
topics.getTopicById(req.params.id, uid, function(data) {
topics.getTopicWithPosts(req.params.id, uid, function(data) {
res.json(data);
});
break;
@ -294,8 +294,30 @@ var express = require('express'),
app.get('/api/:method/:id/:section?', api_method);
app.get('/api/:method/:id*', api_method);
app.get('/tid/:tid', function(req, res) {
topics.getTopicData(req.params.tid, function(data){
if(data)
res.send(data);
else
res.send("Topic doesn't exist!");
});
});
app.get('/pid/:pid', function(req, res) {
posts.getPostData(req.params.pid, function(data){
if(data)
res.send(data);
else
res.send("Post doesn't exist!");
});
});
app.all('/test', function(req, res) {
res.send();
categories.getCategoryById(1,1, function(data) {
res.send(data);
},1);
});
//START TODO: MOVE TO GRAPH.JS

@ -247,7 +247,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
});
socket.on('api:posts.getRawPost', function(data) {
posts.getRawContent(data.pid, function(raw) {
posts.getPostField(data.pid, 'content', function(raw) {
socket.emit('api:posts.getRawPost', { post: raw });
});
});
@ -332,10 +332,16 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.on('api:composer.push', function(data) {
if (uid > 0) {
if (parseInt(data.tid) > 0) {
topics.get_topic(data.tid, uid, function(topicData) {
topicData.tid = data.tid;
if (data.body) topicData.body = data.body;
socket.emit('api:composer.push', topicData);
topics.getTopicData(data.tid, function(topicData) {
if (data.body)
topicData.body = data.body;
socket.emit('api:composer.push', {
tid: data.tid,
title: topicData.title,
body: topicData.body
});
});
} else if (parseInt(data.cid) > 0) {
user.getUserField(uid, 'username', function(username) {
@ -349,7 +355,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
} else if (parseInt(data.pid) > 0) {
async.parallel([
function(next) {
posts.getRawContent(data.pid, function(raw) {
posts.getPostField(data.pid, 'content', function(raw) {
next(null, raw);
});
},
@ -374,7 +380,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
});
socket.on('api:composer.editCheck', function(pid) {
posts.get_tid_by_pid(pid, function(tid) {
posts.getPostField(pid, 'tid', function(tid) {
postTools.isMain(pid, tid, function(isMain) {
socket.emit('api:composer.editCheck', {
titleEditable: isMain
@ -413,14 +419,10 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
var start = data.after,
end = start + 10;
posts.getPostsByTid(data.tid, uid, start, end, function(posts){
if (!posts.error) {
postTools.constructPostObject(posts, data.tid, uid, null, function(postObj) {
io.sockets.in('topic_' + data.tid).emit('event:new_post', {
posts: postObj
});
});
}
topics.getTopicPosts(data.tid, start, end, uid, function(posts) {
io.sockets.in('topic_' + data.tid).emit('event:new_post', {
posts: posts
});
});
});

Loading…
Cancel
Save