Merge remote-tracking branch 'origin/master' into email-revamp

This commit is contained in:
Julian Lam
2014-01-04 11:19:23 -05:00
20 changed files with 429 additions and 121 deletions

View File

@@ -138,9 +138,7 @@ var db = require('./database.js'),
db.getSortedSetRevRange('categories:' + cid + ':tid', start, stop, callback);
};
Categories.getActiveUsers = function(cid, callback) {
db.getSetMembers('cid:' + cid + ':active_users', callback);
};
Categories.getAllCategories = function(current_user, callback) {
db.getListRange('categories:cid', 0, -1, function(err, cids) {
@@ -217,6 +215,10 @@ var db = require('./database.js'),
};
Categories.getRecentReplies = function(cid, count, callback) {
if(count === 0) {
return callback(null, []);
}
db.getSortedSetRevRange('categories:recent_posts:cid:' + cid, 0, count - 1, function(err, pids) {
if (err) {
@@ -264,25 +266,7 @@ var db = require('./database.js'),
});
};
Categories.moveActiveUsers = function(tid, oldCid, cid, callback) {
function updateUser(uid) {
Categories.addActiveUser(cid, uid);
Categories.isUserActiveIn(oldCid, uid, function(err, active) {
if (!err && !active) {
Categories.removeActiveUser(oldCid, uid);
}
});
}
topics.getUids(tid, function(err, uids) {
if (!err && uids) {
for (var i = 0; i < uids.length; ++i) {
updateUser(uids[i]);
}
}
});
};
Categories.getCategoryData = function(cid, callback) {
db.exists('category:' + cid, function(err, exists) {
@@ -391,14 +375,38 @@ var db = require('./database.js'),
});
};
Categories.addActiveUser = function(cid, uid) {
Categories.addActiveUser = function(cid, uid, timestamp) {
if(parseInt(uid, 10)) {
db.setAdd('cid:' + cid + ':active_users', uid);
db.sortedSetAdd('cid:' + cid + ':active_users', timestamp, uid);
}
};
Categories.removeActiveUser = function(cid, uid) {
db.setRemove('cid:' + cid + ':active_users', uid);
db.sortedSetRemove('cid:' + cid + ':active_users', uid);
};
Categories.getActiveUsers = function(cid, callback) {
db.getSortedSetRevRange('cid:' + cid + ':active_users', 0, 15, callback);
};
Categories.moveActiveUsers = function(tid, oldCid, cid, callback) {
function updateUser(uid) {
Categories.addActiveUser(cid, uid);
Categories.isUserActiveIn(oldCid, uid, function(err, active) {
if (!err && !active) {
Categories.removeActiveUser(oldCid, uid);
}
});
}
topics.getUids(tid, function(err, uids) {
if (!err && uids) {
for (var i = 0; i < uids.length; ++i) {
updateUser(uids[i]);
}
}
});
};
Categories.onNewPostMade = function(uid, tid, pid, timestamp) {
@@ -412,13 +420,7 @@ var db = require('./database.js'),
db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid);
}
db.setCount('cid:' + cid + ':active_users', function(err, amount) {
if (amount > 15) {
db.setRemoveRandom('cid:' + cid + ':active_users');
}
Categories.addActiveUser(cid, uid);
});
Categories.addActiveUser(cid, uid, timestamp);
});
}

View File

@@ -738,6 +738,21 @@
});
}
module.listRemoveAll = function(key, value, callback) {
db.collection('objects').update({_key: key }, { $pull: { array: value } }, function(err, result) {
if(err) {
if(callback) {
return callback(err);
}
return;
}
if(callback) {
callback(null, result);
}
});
}
module.getListRange = function(key, start, stop, callback) {
if(stop === -1) {

View File

@@ -402,6 +402,10 @@
redisClient.rpop(key, callback);
}
module.listRemoveAll = function(key, value, callback) {
redisClient.lrem(key, 0, value, callback);
}
module.getListRange = function(key, start, stop, callback) {
redisClient.lrange(key, start, stop, callback);
}

View File

@@ -134,6 +134,11 @@ var async = require('async'),
return next(new Error('unknown database : ' + config.database));
}
var allQuestions = install.redisQuestions.concat(install.mongoQuestions);
for(var x=0;x<allQuestions.length;x++) {
delete config[allQuestions[x].name];
}
config.bcrypt_rounds = 12;
config.upload_path = '/public/uploads';
config.use_port = config.use_port.slice(0, 1) === 'y';
@@ -258,6 +263,21 @@ var async = require('async'),
}, function (err) {
meta.configs.init(next);
});
if (install.values) {
if (install.values['social:twitter:key'] && install.values['social:twitter:secret']) {
meta.configs.setOnEmpty('social:twitter:key', install.values['social:twitter:key']);
meta.configs.setOnEmpty('social:twitter:secret', install.values['social:twitter:secret']);
}
if (install.values['social:google:id'] && install.values['social:google:secret']) {
meta.configs.setOnEmpty('social:google:id', install.values['social:google:id']);
meta.configs.setOnEmpty('social:google:secret', install.values['social:google:secret']);
}
if (install.values['social:facebook:key'] && install.values['social:facebook:secret']) {
meta.configs.setOnEmpty('social:facebook:app_id', install.values['social:facebook:app_id']);
meta.configs.setOnEmpty('social:facebook:secret', install.values['social:facebook:secret']);
}
}
},
function (next) {
// Check if an administrator needs to be created

View File

@@ -137,7 +137,7 @@ var winston = require('winston'),
events.logPostDelete(uid, pid);
posts.getPostFields(pid, ['tid', 'uid'], function(err, postData) {
db.incrObjectFieldBy('topic:' + postData.tid, 'postcount', -1);
topics.decreasePostCount(postData.tid);
user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) {
db.sortedSetAdd('users:postcount', postcount, postData.uid);
@@ -193,7 +193,7 @@ var winston = require('winston'),
events.logPostRestore(uid, pid);
posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) {
db.incrObjectFieldBy('topic:' + postData.tid, 'postcount', 1);
topics.increasePostCount(postData.tid);
user.incrementUserFieldBy(postData.uid, 'postcount', 1);

View File

@@ -28,63 +28,70 @@ var db = require('./database'),
return callback(new Error('invalid-user'), null);
}
topics.isLocked(tid, function(err, locked) {
if(err) {
return callback(err, null);
} else if(locked) {
return callback(new Error('topic-locked'), null);
}
db.incrObjectField('global', 'nextPid', function(err, pid) {
if(err) {
return callback(err, null);
async.waterfall([
function(next) {
topics.isLocked(tid, next);
},
function(locked, next) {
if(locked) {
return next(new Error('topic-locked'));
}
db.incrObjectField('global', 'nextPid', next);
},
function(pid, next) {
plugins.fireHook('filter:post.save', content, function(err, newContent) {
next(err, pid, newContent)
});
},
function(pid, newContent, next) {
var timestamp = Date.now(),
postData = {
'pid': pid,
'uid': uid,
'tid': tid,
'content': newContent,
'timestamp': timestamp,
'reputation': 0,
'editor': '',
'edited': 0,
'deleted': 0
};
db.setObject('post:' + pid, postData, function(err) {
if(err) {
return callback(err, null);
return next(err);
}
var timestamp = Date.now(),
postData = {
'pid': pid,
'uid': uid,
'tid': tid,
'content': newContent,
'timestamp': timestamp,
'reputation': 0,
'editor': '',
'edited': 0,
'deleted': 0
};
db.setObject('post:' + pid, postData);
db.incrObjectField('global', 'postCount');
topics.onNewPostMade(tid, pid, timestamp);
categories.onNewPostMade(uid, tid, pid, timestamp);
user.onNewPostMade(uid, tid, pid, timestamp);
plugins.fireHook('filter:post.get', postData, function(err, newPostData) {
if(err) {
return callback(err, null);
}
postTools.parse(newPostData.content, function(err, content) {
if(err) {
return callback(err, null);
}
newPostData.content = content;
plugins.fireHook('action:post.save', newPostData);
db.searchIndex('post', content, pid);
callback(null, newPostData);
});
});
next(null, postData);
});
});
},
function(postData, next) {
plugins.fireHook('filter:post.get', postData, next);
},
function(postData, next) {
postTools.parse(postData.content, function(err, content) {
if(err) {
return next(err, null);
}
postData.content = content;
plugins.fireHook('action:post.save', postData);
db.searchIndex('post', content, postData.pid);
next(null, postData);
});
}
], function(err, postData) {
callback(err, postData);
});
};

View File

@@ -55,9 +55,9 @@ var path = require('path'),
});
function iterator(category, callback) {
categories.getRecentReplies(category.cid, 2, function (err, posts) {
categories.getRecentReplies(category.cid, parseInt(category.numRecentReplies, 10), function (err, posts) {
category.posts = posts;
category.post_count = posts.length > 2 ? 2 : posts.length;
category.post_count = posts.length > 2 ? 2 : posts.length; // this was a hack to make metro work back in the day, post_count should just = length
callback(null);
});
}

View File

@@ -23,6 +23,50 @@ var async = require('async'),
(function(Topics) {
Topics.create = function(uid, title, cid, callback) {
db.incrObjectField('global', 'nextTid', function(err, tid) {
if(err) {
return callback(err);
}
db.setAdd('topics:tid', tid);
var slug = tid + '/' + utils.slugify(title),
timestamp = Date.now();
db.setObject('topic:' + tid, {
'tid': tid,
'uid': uid,
'cid': cid,
'title': title,
'slug': slug,
'timestamp': timestamp,
'lastposttime': 0,
'postcount': 0,
'viewcount': 0,
'locked': 0,
'deleted': 0,
'pinned': 0
}, function(err) {
if(err) {
return callback(err);
}
db.searchIndex('topic', title, tid);
user.addTopicIdToUser(uid, tid);
// in future it may be possible to add topics to several categories, so leaving the door open here.
db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid);
db.incrObjectField('category:' + cid, 'topic_count');
db.incrObjectField('global', 'topicCount');
feed.updateCategory(cid);
callback(null, tid);
});
});
};
Topics.post = function(uid, title, content, cid, callback) {
categoryTools.privileges(cid, uid, function(err, privileges) {
@@ -61,41 +105,11 @@ var async = require('async'),
return callback(new Error('too-many-posts'), null);
}
db.incrObjectField('global', 'nextTid', function(err, tid) {
Topics.create(uid, title, cid, function(err, tid) {
if(err) {
return callback(err);
}
db.setAdd('topics:tid', tid);
var slug = tid + '/' + utils.slugify(title);
var timestamp = Date.now();
db.setObject('topic:' + tid, {
'tid': tid,
'uid': uid,
'cid': cid,
'title': title,
'slug': slug,
'timestamp': timestamp,
'lastposttime': 0,
'postcount': 0,
'viewcount': 0,
'locked': 0,
'deleted': 0,
'pinned': 0
});
db.searchIndex('topic', title, tid);
user.addTopicIdToUser(uid, tid);
// in future it may be possible to add topics to several categories, so leaving the door open here.
db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid);
db.incrObjectField('category:' + cid, 'topic_count');
db.incrObjectField('global', 'topicCount');
feed.updateCategory(cid);
Topics.reply(tid, uid, content, function(err, postData) {
if(err) {
return callback(err, null);
@@ -184,6 +198,40 @@ var async = require('async'),
});
}
Topics.createTopicFromPost = function(pid, callback) {
posts.getPostData(pid, function(err, postData) {
if(err) {
return callback(err);
}
posts.getCidByPid(pid, function(err, cid) {
if(err) {
return callback(err);
}
// TODO : title should be given by client
var title = postData.content.substr(0, 20);
Topics.create(postData.uid, title, cid, function(err, tid) {
if(err) {
return callback(err);
}
Topics.removePostFromTopic(postData.tid, postData.pid);
Topics.decreasePostCount(postData.tid);
posts.setPostField(pid, 'tid', tid);
Topics.onNewPostMade(tid, postData.pid, postData.timestamp);
Topics.getTopicData(tid, function(err, topicData) {
callback(err, topicData);
});
});
});
});
}
Topics.getTopicData = function(tid, callback) {
db.getObject('topic:' + tid, function(err, data) {
if(err) {
@@ -916,6 +964,10 @@ var async = require('async'),
db.incrObjectField('topic:' + tid, 'postcount', callback);
}
Topics.decreasePostCount = function(tid, callback) {
db.decrObjectField('topic:' + tid, 'postcount', callback);
}
Topics.increaseViewCount = function(tid, callback) {
db.incrObjectField('topic:' + tid, 'viewcount', callback);
}
@@ -944,6 +996,10 @@ var async = require('async'),
db.listAppend('tid:' + tid + ':posts', pid);
}
Topics.removePostFromTopic = function(tid, pid) {
db.listRemoveAll('tid:' + tid + ':posts', pid);
}
Topics.getPids = function(tid, callback) {
db.getListRange('tid:' + tid + ':posts', 0, -1, callback);
}

View File

@@ -14,7 +14,7 @@ var db = require('./database'),
Upgrade.check = function(callback) {
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
var latestSchema = new Date(2014, 0, 1).getTime();
var latestSchema = new Date(2014, 0, 5).getTime();
db.get('schemaDate', function(err, value) {
if (parseInt(value, 10) >= latestSchema) {
@@ -65,8 +65,16 @@ Upgrade.upgrade = function(callback) {
async.each(uids, function(uid, next) {
User.getUserField(uid, 'username', function(err, username) {
newUserSlug = Utils.slugify(username);
User.setUserField(uid, 'userslug', newUserSlug, next);
if(err) {
return next(err);
}
if(username) {
newUserSlug = Utils.slugify(username);
User.setUserField(uid, 'userslug', newUserSlug, next);
} else {
winston.warn('uid '+ uid + ' doesn\'t have a valid username (' + username + '), skipping');
next(null);
}
});
}, function(err) {
next(err);
@@ -108,6 +116,97 @@ Upgrade.upgrade = function(callback) {
winston.info('[2013/12/31] maximumTitleLength skipped');
next();
}
},
function(next) {
// Custom classes for each category, adding link field for each category
thisSchemaDate = new Date(2014, 0, 3).getTime();
if (schemaDate < thisSchemaDate) {
updatesMade = true;
db.getListRange('categories:cid', 0, -1, function(err, cids) {
if(err) {
return next(err);
}
for (var cid in cids) {
db.setObjectField('category:' + cids[cid], 'link', '');
db.setObjectField('category:' + cids[cid], 'class', 'col-md-3 col-xs-6');
}
winston.info('[2014/1/3] Added categories.class, categories.link fields');
next();
});
} else {
winston.info('[2014/1/3] categories.class, categories.link fields skipped');
next();
}
},
function(next) {
// Custom classes for each category, adding link field for each category
thisSchemaDate = new Date(2014, 0, 4).getTime();
if (schemaDate < thisSchemaDate) {
updatesMade = true;
db.getListRange('categories:cid', 0, -1, function(err, cids) {
if(err) {
return next(err);
}
for (var cid in cids) {
db.setObjectField('category:' + cids[cid], 'numRecentReplies', '2');
}
winston.info('[2014/1/4] Added categories.numRecentReplies fields');
next();
});
} else {
winston.info('[2014/1/4] categories.numRecentReplies fields skipped');
next();
}
},
function(next) {
thisSchemaDate = new Date(2014, 0, 5).getTime();
if (schemaDate < thisSchemaDate) {
updatesMade = true;
db.getListRange('categories:cid', 0, -1, function(err, cids) {
if(err) {
return next(err);
}
var timestamp = Date.now();
function upgradeCategory(cid, next) {
db.getSetMembers('cid:' + cid + ':active_users', function(err, uids) {
if(err) {
return next(err);
}
db.delete('cid:' + cid + ':active_users', function(err) {
if(err) {
return next(err);
}
for(var i=0; i<uids.length; ++i) {
db.sortedSetAdd('cid:' + cid + ':active_users', timestamp, uids[i]);
}
next();
});
});
}
async.each(cids, upgradeCategory, function(err) {
if(err) {
return next(err)
}
winston.info('[2014/1/5] Upgraded categories active users');
next();
});
});
} else {
winston.info('[2014/1/5] categories active users skipped');
next();
}
}
// Add new schema updates here
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 17!!!

View File

@@ -622,6 +622,12 @@ websockets.init = function(io) {
});
});
socket.on('api:topic.createTopicFromPost', function(data, callback) {
topics.createTopicFromPost(data.pid, function(err, data) {
callback(err?{message:err.message}:null, data);
});
});
socket.on('api:topic.move', function(data) {
threadTools.move(data.tid, data.cid, socket);
});