mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-26 17:29:53 +01:00
Merge remote-tracking branch 'origin' into category-whitelisting
This commit is contained in:
@@ -4,10 +4,6 @@ var RDB = require('./../redis.js'),
|
||||
|
||||
(function(CategoriesAdmin) {
|
||||
|
||||
CategoriesAdmin.create = function(data, callback) {
|
||||
categories.create(data, callback);
|
||||
};
|
||||
|
||||
CategoriesAdmin.update = function(modified, socket) {
|
||||
var updated = [];
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ var RDB = require('./redis.js'),
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
icon: data.icon,
|
||||
blockclass: data.blockclass,
|
||||
bgColor: data.bgColor,
|
||||
color: data.color,
|
||||
slug: slug,
|
||||
topic_count: 0,
|
||||
disabled: 0,
|
||||
@@ -140,10 +141,16 @@ var RDB = require('./redis.js'),
|
||||
RDB.smembers('cid:' + cid + ':active_users', callback);
|
||||
};
|
||||
|
||||
Categories.getAllCategories = function(callback, current_user) {
|
||||
Categories.getAllCategories = function(current_user, callback) {
|
||||
RDB.lrange('categories:cid', 0, -1, function(err, cids) {
|
||||
RDB.handle(err);
|
||||
Categories.getCategories(cids, callback, current_user);
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
if(cids && cids.length === 0) {
|
||||
return callback(null, {categories : []});
|
||||
}
|
||||
|
||||
Categories.getCategories(cids, current_user, callback);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -252,26 +259,28 @@ var RDB = require('./redis.js'),
|
||||
|
||||
Categories.moveRecentReplies = function(tid, oldCid, cid, callback) {
|
||||
function movePost(pid, callback) {
|
||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
||||
posts.getPostField(pid, 'timestamp', function(err, timestamp) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
RDB.zrem('categories:recent_posts:cid:' + oldCid, pid);
|
||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
topics.getPids(tid, function(err, pids) {
|
||||
if (!err) {
|
||||
async.each(pids, movePost, function(err) {
|
||||
if (!err) {
|
||||
callback(null, 1);
|
||||
} else {
|
||||
winston.err(err);
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
winston.err(err);
|
||||
callback(err, null);
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
async.each(pids, movePost, function(err) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
callback(null, 1);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -321,24 +330,20 @@ var RDB = require('./redis.js'),
|
||||
RDB.hincrby('category:' + cid, field, value);
|
||||
};
|
||||
|
||||
Categories.getCategories = function(cids, callback, current_user) {
|
||||
Categories.getCategories = function(cids, uid, callback) {
|
||||
if (!cids || !Array.isArray(cids) || cids.length === 0) {
|
||||
callback({
|
||||
'categories': []
|
||||
});
|
||||
return;
|
||||
return callback(new Error('invalid-cids'), null);
|
||||
}
|
||||
|
||||
function getCategory(cid, callback) {
|
||||
Categories.getCategoryData(cid, function(err, categoryData) {
|
||||
if (err) {
|
||||
winston.warn('Attempted to retrieve cid ' + cid + ', but nothing was returned!');
|
||||
callback(null, null);
|
||||
return;
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
Categories.hasReadCategory(cid, current_user, function(hasRead) {
|
||||
categoryData.badgeclass = (parseInt(categoryData.topic_count, 10) === 0 || (hasRead && current_user !== 0)) ? '' : 'badge-important';
|
||||
Categories.hasReadCategory(cid, uid, function(hasRead) {
|
||||
categoryData.badgeclass = (parseInt(categoryData.topic_count, 10) === 0 || (hasRead && uid !== 0)) ? '' : 'badge-important';
|
||||
|
||||
callback(null, categoryData);
|
||||
});
|
||||
@@ -348,8 +353,7 @@ var RDB = require('./redis.js'),
|
||||
async.map(cids, getCategory, function(err, categories) {
|
||||
if (err) {
|
||||
winston.err(err);
|
||||
callback(null);
|
||||
return;
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
categories = categories.filter(function(category) {
|
||||
@@ -358,7 +362,7 @@ var RDB = require('./redis.js'),
|
||||
return parseInt(a.order, 10) - parseInt(b.order, 10);
|
||||
});
|
||||
|
||||
callback({
|
||||
callback(null, {
|
||||
'categories': categories
|
||||
});
|
||||
});
|
||||
@@ -372,19 +376,6 @@ var RDB = require('./redis.js'),
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
function getPostCategory(pid, callback) {
|
||||
posts.getPostField(pid, 'tid', function(tid) {
|
||||
|
||||
topics.getTopicField(tid, 'cid', function(err, postCid) {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
return callback(null, postCid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var index = 0,
|
||||
active = false;
|
||||
|
||||
@@ -393,7 +384,7 @@ var RDB = require('./redis.js'),
|
||||
return active === false && index < pids.length;
|
||||
},
|
||||
function(callback) {
|
||||
getPostCategory(pids[index], function(err, postCid) {
|
||||
posts.getCidByPid(pids[index], function(err, postCid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -411,7 +402,6 @@ var RDB = require('./redis.js'),
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
|
||||
callback(null, active);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
var RDB = require('./redis.js'),
|
||||
posts = require('./posts.js'),
|
||||
user = require('./user.js'),
|
||||
translator = require('./../public/src/translator.js');
|
||||
var RDB = require('./redis'),
|
||||
posts = require('./posts'),
|
||||
user = require('./user'),
|
||||
websockets = require('./websockets')
|
||||
translator = require('./../public/src/translator');
|
||||
|
||||
(function (Favourites) {
|
||||
"use strict";
|
||||
@@ -21,7 +22,7 @@ var RDB = require('./redis.js'),
|
||||
return;
|
||||
}
|
||||
|
||||
posts.getPostFields(pid, ['uid', 'timestamp'], function (postData) {
|
||||
posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) {
|
||||
|
||||
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
||||
if (hasFavourited === 0) {
|
||||
@@ -37,7 +38,7 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
if (room_id) {
|
||||
io.sockets. in (room_id).emit('event:rep_up', {
|
||||
websockets.in(room_id).emit('event:rep_up', {
|
||||
uid: uid !== postData.uid ? postData.uid : 0,
|
||||
pid: pid
|
||||
});
|
||||
@@ -57,7 +58,7 @@ var RDB = require('./redis.js'),
|
||||
return;
|
||||
}
|
||||
|
||||
posts.getPostField(pid, 'uid', function (uid_of_poster) {
|
||||
posts.getPostField(pid, 'uid', function (err, uid_of_poster) {
|
||||
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
||||
if (hasFavourited === 1) {
|
||||
RDB.srem('pid:' + pid + ':users_favourited', uid);
|
||||
@@ -72,7 +73,7 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
if (room_id) {
|
||||
io.sockets. in (room_id).emit('event:rep_down', {
|
||||
websockets.in(room_id).emit('event:rep_down', {
|
||||
uid: uid !== uid_of_poster ? uid_of_poster : 0,
|
||||
pid: pid
|
||||
});
|
||||
|
||||
117
src/feed.js
117
src/feed.js
@@ -2,12 +2,14 @@
|
||||
var RDB = require('./redis.js'),
|
||||
posts = require('./posts.js'),
|
||||
topics = require('./topics.js'),
|
||||
categories = require('./categories'),
|
||||
|
||||
fs = require('fs'),
|
||||
rss = require('rss'),
|
||||
winston = require('winston'),
|
||||
path = require('path'),
|
||||
nconf = require('nconf'),
|
||||
categories = require('./categories');
|
||||
async = require('async');
|
||||
|
||||
Feed.defaults = {
|
||||
ttl: 60,
|
||||
@@ -26,43 +28,43 @@
|
||||
}
|
||||
|
||||
Feed.updateTopic = function (tid, callback) {
|
||||
if (process.env.NODE_ENV === 'development') winston.info('[rss] Updating RSS feeds for topic ' + tid);
|
||||
|
||||
topics.getTopicWithPosts(tid, 0, 0, -1, function (err, topicData) {
|
||||
if (err) return callback(new Error('topic-invalid'));
|
||||
|
||||
var feed = new rss({
|
||||
title: topicData.topic_name,
|
||||
description: topicData.main_posts[0].content,
|
||||
feed_url: Feed.defaults.baseUrl + '/topics/' + tid + '.rss',
|
||||
site_url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||
image_url: topicData.main_posts[0].picture,
|
||||
author: topicData.main_posts[0].username,
|
||||
ttl: Feed.defaults.ttl
|
||||
}),
|
||||
topic_posts = topicData.main_posts.concat(topicData.posts),
|
||||
title, postData, dateStamp;
|
||||
title: topicData.topic_name,
|
||||
description: topicData.posts[0].content,
|
||||
feed_url: Feed.defaults.baseUrl + '/topics/' + tid + '.rss',
|
||||
site_url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||
image_url: topicData.posts[0].picture,
|
||||
author: topicData.posts[0].username,
|
||||
ttl: Feed.defaults.ttl
|
||||
}),
|
||||
topic_posts = topicData.posts.concat(topicData.posts),
|
||||
dateStamp;
|
||||
|
||||
// Add pubDate if topic contains posts
|
||||
if (topicData.main_posts.length > 0) feed.pubDate = new Date(parseInt(topicData.main_posts[0].timestamp, 10)).toUTCString();
|
||||
if (topicData.posts.length > 0) feed.pubDate = new Date(parseInt(topicData.posts[0].timestamp, 10)).toUTCString();
|
||||
|
||||
for (var i = 0, ii = topic_posts.length; i < ii; i++) {
|
||||
if (topic_posts[i].deleted === '0') {
|
||||
postData = topic_posts[i];
|
||||
async.each(topic_posts, function(postData, next) {
|
||||
if (postData.deleted === '0') {
|
||||
dateStamp = new Date(parseInt(postData.edited === '0' ? postData.timestamp : postData.edited, 10)).toUTCString();
|
||||
title = 'Reply to ' + topicData.topic_name + ' on ' + dateStamp;
|
||||
|
||||
feed.item({
|
||||
title: title,
|
||||
title: 'Reply to ' + topicData.topic_name + ' on ' + dateStamp,
|
||||
description: postData.content,
|
||||
url: nconf.get('url') + 'topic/' + topicData.slug + '#' + postData.pid,
|
||||
author: postData.username,
|
||||
date: dateStamp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Feed.saveFeed('feeds/topics/' + tid + '.rss', feed, function (err) {
|
||||
next();
|
||||
}, function() {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[rss] Re-generated RSS Feed for tid ' + tid + '.');
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
@@ -70,40 +72,75 @@
|
||||
};
|
||||
|
||||
Feed.updateCategory = function (cid, callback) {
|
||||
if (process.env.NODE_ENV === 'development') winston.info('[rss] Updating RSS feeds for category ' + cid);
|
||||
categories.getCategoryById(cid, 0, function (err, categoryData) {
|
||||
if (err) return callback(new Error('category-invalid'));
|
||||
|
||||
var feed = new rss({
|
||||
title: categoryData.category_name,
|
||||
description: categoryData.category_description,
|
||||
feed_url: Feed.defaults.baseUrl + '/categories/' + cid + '.rss',
|
||||
site_url: nconf.get('url') + 'category/' + categoryData.category_id,
|
||||
ttl: Feed.defaults.ttl
|
||||
}),
|
||||
topics = categoryData.topics,
|
||||
title, topicData, dateStamp;
|
||||
title: categoryData.category_name,
|
||||
description: categoryData.category_description,
|
||||
feed_url: Feed.defaults.baseUrl + '/categories/' + cid + '.rss',
|
||||
site_url: nconf.get('url') + 'category/' + categoryData.category_id,
|
||||
ttl: Feed.defaults.ttl
|
||||
});
|
||||
|
||||
// Add pubDate if category has topics
|
||||
if (categoryData.topics.length > 0) feed.pubDate = new Date(parseInt(categoryData.topics[0].lastposttime, 10)).toUTCString();
|
||||
|
||||
for (var i = 0, ii = topics.length; i < ii; i++) {
|
||||
topicData = topics[i];
|
||||
dateStamp = new Date(parseInt(topicData.lastposttime, 10)).toUTCString();
|
||||
title = topics[i].title;
|
||||
|
||||
async.eachSeries(categoryData.topics, function(topicData, next) {
|
||||
feed.item({
|
||||
title: title,
|
||||
title: topicData.title,
|
||||
url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||
author: topicData.username,
|
||||
date: dateStamp
|
||||
date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString()
|
||||
});
|
||||
}
|
||||
|
||||
Feed.saveFeed('feeds/categories/' + cid + '.rss', feed, function (err) {
|
||||
if (callback) callback();
|
||||
next();
|
||||
}, function() {
|
||||
Feed.saveFeed('feeds/categories/' + cid + '.rss', feed, function (err) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[rss] Re-generated RSS Feed for cid ' + cid + '.');
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Feed.updateRecent = function(callback) {
|
||||
console.log('entered');
|
||||
if (process.env.NODE_ENV === 'development') winston.info('[rss] Updating Recent Posts RSS feed');
|
||||
topics.getLatestTopics(0, 0, 19, undefined, function (err, recentData) {
|
||||
var feed = new rss({
|
||||
title: 'Recently Active Topics',
|
||||
description: 'A list of topics that have been active within the past 24 hours',
|
||||
feed_url: Feed.defaults.baseUrl + '/recent.rss',
|
||||
site_url: nconf.get('url') + 'recent',
|
||||
ttl: Feed.defaults.ttl
|
||||
});
|
||||
|
||||
// Add pubDate if recent topics list contains topics
|
||||
if (recentData.topics.length > 0) {
|
||||
feed.pubDate = new Date(parseInt(recentData.topics[0].lastposttime, 10)).toUTCString();
|
||||
}
|
||||
|
||||
async.eachSeries(recentData.topics, function(topicData, next) {
|
||||
feed.item({
|
||||
title: topicData.title,
|
||||
url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||
author: topicData.username,
|
||||
date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString()
|
||||
});
|
||||
next();
|
||||
}, function() {
|
||||
Feed.saveFeed('feeds/recent.rss', feed, function (err) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[rss] Re-generated "recent posts" RSS Feed.');
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}(exports));
|
||||
@@ -56,6 +56,8 @@
|
||||
results.base.count = results.users.length;
|
||||
results.base.members = results.users;
|
||||
|
||||
results.base.deletable = (results.base.gid !== '1');
|
||||
|
||||
callback(err, results.base);
|
||||
});
|
||||
},
|
||||
@@ -112,7 +114,9 @@
|
||||
});
|
||||
},
|
||||
destroy: function (gid, callback) {
|
||||
RDB.hset('gid:' + gid, 'deleted', '1', callback);
|
||||
if (gid !== 1) {
|
||||
RDB.hset('gid:' + gid, 'deleted', '1', callback);
|
||||
}
|
||||
},
|
||||
join: function (gid, uid, callback) {
|
||||
RDB.sadd('gid:' + gid + ':members', uid, callback);
|
||||
|
||||
29
src/imgur.js
29
src/imgur.js
@@ -1,12 +1,11 @@
|
||||
var request = require('request');
|
||||
var request = require('request'),
|
||||
winston = require('winston');
|
||||
|
||||
|
||||
(function (imgur) {
|
||||
"use strict";
|
||||
|
||||
var clientID = '';
|
||||
|
||||
imgur.upload = function (image, type, callback) {
|
||||
imgur.upload = function (clientID, image, type, callback) {
|
||||
var options = {
|
||||
url: 'https://api.imgur.com/3/upload.json',
|
||||
headers: {
|
||||
@@ -15,21 +14,27 @@ var request = require('request');
|
||||
};
|
||||
|
||||
var post = request.post(options, function (err, req, body) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
try {
|
||||
callback(err, JSON.parse(body));
|
||||
} catch (e) {
|
||||
callback(err, body);
|
||||
var response = JSON.parse(body);
|
||||
if(response.success) {
|
||||
callback(null, response.data);
|
||||
} else {
|
||||
callback(new Error(response.data.error.message), null);
|
||||
}
|
||||
} catch(e) {
|
||||
winston.error('Unable to parse Imgur json response. [' + body +']');
|
||||
callback(e, null);
|
||||
}
|
||||
});
|
||||
|
||||
var upload = post.form({
|
||||
post.form({
|
||||
type: type,
|
||||
image: image
|
||||
});
|
||||
};
|
||||
|
||||
imgur.setClientID = function (id) {
|
||||
clientID = id;
|
||||
};
|
||||
|
||||
}(exports));
|
||||
@@ -63,13 +63,14 @@ var async = require('async'),
|
||||
}
|
||||
|
||||
if (setupVal && setupVal instanceof Object) {
|
||||
if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:email']) {
|
||||
if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:password:confirm'] && setupVal['admin:email']) {
|
||||
install.values = setupVal;
|
||||
next();
|
||||
} else {
|
||||
winston.error('Required values are missing for automated setup:');
|
||||
if (!setupVal['admin:username']) winston.error(' admin:username');
|
||||
if (!setupVal['admin:password']) winston.error(' admin:password');
|
||||
if (!setupVal['admin:password:confirm']) winston.error(' admin:password:confirm');
|
||||
if (!setupVal['admin:email']) winston.error(' admin:email');
|
||||
process.exit();
|
||||
}
|
||||
@@ -135,8 +136,11 @@ var async = require('async'),
|
||||
winston.info('Populating database with default configs, if not already set...');
|
||||
var meta = require('./meta'),
|
||||
defaults = [{
|
||||
field: 'title',
|
||||
value: 'NodeBB'
|
||||
}, {
|
||||
field: 'postDelay',
|
||||
value: 10000
|
||||
value: 10
|
||||
}, {
|
||||
field: 'minimumPostLength',
|
||||
value: 8
|
||||
@@ -200,12 +204,9 @@ var async = require('async'),
|
||||
},
|
||||
function (next) {
|
||||
// Categories
|
||||
var Categories = require('./categories'),
|
||||
admin = {
|
||||
categories: require('./admin/categories')
|
||||
};
|
||||
var Categories = require('./categories');
|
||||
|
||||
Categories.getAllCategories(function (data) {
|
||||
Categories.getAllCategories(0, function (err, data) {
|
||||
if (data.categories.length === 0) {
|
||||
winston.warn('No categories found, populating instance with default categories');
|
||||
|
||||
@@ -213,7 +214,7 @@ var async = require('async'),
|
||||
default_categories = JSON.parse(default_categories);
|
||||
|
||||
async.eachSeries(default_categories, function (category, next) {
|
||||
admin.categories.create(category, next);
|
||||
Categories.create(category, next);
|
||||
}, function (err) {
|
||||
if (!err) {
|
||||
next();
|
||||
@@ -249,6 +250,11 @@ var async = require('async'),
|
||||
}
|
||||
});
|
||||
}, next);
|
||||
},
|
||||
function (next) {
|
||||
// Upgrading schema
|
||||
var Upgrade = require('./upgrade');
|
||||
Upgrade.upgrade(next);
|
||||
}
|
||||
], function (err) {
|
||||
if (err) {
|
||||
@@ -274,18 +280,32 @@ var async = require('async'),
|
||||
description: 'Administrator email address',
|
||||
pattern: /.+@.+/,
|
||||
required: true
|
||||
}, {
|
||||
}],
|
||||
passwordQuestions = [{
|
||||
name: 'password',
|
||||
description: 'Password',
|
||||
required: true,
|
||||
hidden: true,
|
||||
type: 'string'
|
||||
}, {
|
||||
name: 'password:confirm',
|
||||
description: 'Confirm Password',
|
||||
required: true,
|
||||
hidden: true,
|
||||
type: 'string'
|
||||
}],
|
||||
success = function(err, results) {
|
||||
if (!results) {
|
||||
return callback(new Error('aborted'));
|
||||
}
|
||||
|
||||
// Check if the passwords match
|
||||
if (results['password:confirm'] !== results.password) {
|
||||
winston.warn("Passwords did not match, please try again");
|
||||
// Re-prompt password questions.
|
||||
return retryPassword(results);
|
||||
}
|
||||
|
||||
nconf.set('bcrypt_rounds', 12);
|
||||
User.create(results.username, results.password, results.email, function (err, uid) {
|
||||
if (err) {
|
||||
@@ -303,14 +323,33 @@ var async = require('async'),
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
retryPassword = function (originalResults) {
|
||||
// Ask only the password questions
|
||||
prompt.get(passwordQuestions, function (err, results) {
|
||||
if (!results) {
|
||||
return callback(new Error('aborted'));
|
||||
}
|
||||
|
||||
// Update the original data with newly collected password
|
||||
originalResults.password = results.password;
|
||||
originalResults['password:confirm'] = results['password:confirm'];
|
||||
|
||||
// Send back to success to handle
|
||||
success(err, originalResults);
|
||||
});
|
||||
};
|
||||
|
||||
// Add the password questions
|
||||
questions = questions.concat(passwordQuestions);
|
||||
|
||||
if (!install.values) prompt.get(questions, success);
|
||||
else {
|
||||
var results = {
|
||||
username: install.values['admin:username'],
|
||||
email: install.values['admin:email'],
|
||||
password: install.values['admin:password']
|
||||
password: install.values['admin:password'],
|
||||
'password:confirm': install.values['admin:password:confirm']
|
||||
};
|
||||
|
||||
success(null, results);
|
||||
|
||||
77
src/meta.js
77
src/meta.js
@@ -1,5 +1,6 @@
|
||||
var utils = require('./../public/src/utils.js'),
|
||||
RDB = require('./redis.js'),
|
||||
plugins = require('./plugins'),
|
||||
async = require('async'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
@@ -102,7 +103,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
var themeData = {
|
||||
'theme:type': data.type,
|
||||
'theme:id': data.id,
|
||||
'theme:staticDir': ''
|
||||
'theme:staticDir': '',
|
||||
'theme:templates': '',
|
||||
'theme:src': ''
|
||||
};
|
||||
|
||||
switch(data.type) {
|
||||
@@ -205,44 +208,46 @@ var utils = require('./../public/src/utils.js'),
|
||||
],
|
||||
minFile: path.join(__dirname, '..', 'public/src/nodebb.min.js'),
|
||||
get: function (callback) {
|
||||
var mtime,
|
||||
jsPaths = this.scripts.map(function (jsPath) {
|
||||
return path.join(__dirname, '..', '/public', jsPath);
|
||||
});
|
||||
plugins.fireHook('filter:scripts.get', this.scripts, function(err, scripts) {
|
||||
var mtime,
|
||||
jsPaths = scripts.map(function (jsPath) {
|
||||
return path.join(__dirname, '..', '/public', jsPath);
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
async.parallel({
|
||||
mtime: function (next) {
|
||||
async.map(jsPaths, fs.stat, function (err, stats) {
|
||||
async.reduce(stats, 0, function (memo, item, callback) {
|
||||
mtime = +new Date(item.mtime);
|
||||
callback(null, mtime > memo ? mtime : memo);
|
||||
}, next);
|
||||
});
|
||||
},
|
||||
minFile: function (next) {
|
||||
if (!fs.existsSync(Meta.js.minFile)) {
|
||||
if (process.env.NODE_ENV === 'development') winston.warn('No minified client-side library found');
|
||||
return next(null, 0);
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
async.parallel({
|
||||
mtime: function (next) {
|
||||
async.map(jsPaths, fs.stat, function (err, stats) {
|
||||
async.reduce(stats, 0, function (memo, item, callback) {
|
||||
mtime = +new Date(item.mtime);
|
||||
callback(null, mtime > memo ? mtime : memo);
|
||||
}, next);
|
||||
});
|
||||
},
|
||||
minFile: function (next) {
|
||||
if (!fs.existsSync(Meta.js.minFile)) {
|
||||
if (process.env.NODE_ENV === 'development') winston.warn('No minified client-side library found');
|
||||
return next(null, 0);
|
||||
}
|
||||
|
||||
fs.stat(Meta.js.minFile, function (err, stat) {
|
||||
next(err, +new Date(stat.mtime));
|
||||
});
|
||||
}
|
||||
|
||||
fs.stat(Meta.js.minFile, function (err, stat) {
|
||||
next(err, +new Date(stat.mtime));
|
||||
});
|
||||
}
|
||||
}, function (err, results) {
|
||||
if (results.minFile > results.mtime) {
|
||||
if (process.env.NODE_ENV === 'development') winston.info('No changes to client-side libraries -- skipping minification');
|
||||
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
||||
} else {
|
||||
Meta.js.minify(function () {
|
||||
}, function (err, results) {
|
||||
if (results.minFile > results.mtime) {
|
||||
if (process.env.NODE_ENV === 'development') winston.info('No changes to client-side libraries -- skipping minification');
|
||||
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(null, this.scripts);
|
||||
}
|
||||
} else {
|
||||
Meta.js.minify(function () {
|
||||
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(null, scripts);
|
||||
}
|
||||
});
|
||||
},
|
||||
minify: function (callback) {
|
||||
var uglifyjs = require('uglify-js'),
|
||||
|
||||
@@ -1,146 +1,158 @@
|
||||
var RDB = require('./redis.js'),
|
||||
var RDB = require('./redis'),
|
||||
async = require('async'),
|
||||
utils = require('../public/src/utils.js'),
|
||||
utils = require('../public/src/utils'),
|
||||
winston = require('winston'),
|
||||
cron = require('cron').CronJob,
|
||||
|
||||
notifications = {
|
||||
init: function() {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.init] Registering jobs.');
|
||||
}
|
||||
websockets = require('./websockets');
|
||||
|
||||
new cron('0 0 * * *', notifications.prune, null, true);
|
||||
},
|
||||
get: function(nid, uid, callback) {
|
||||
RDB.multi()
|
||||
.hmget('notifications:' + nid, 'text', 'score', 'path', 'datetime', 'uniqueId')
|
||||
.zrank('uid:' + uid + ':notifications:read', nid)
|
||||
.exists('notifications:' + nid)
|
||||
.exec(function(err, results) {
|
||||
var notification = results[0],
|
||||
readIdx = results[1];
|
||||
|
||||
if (!results[2]) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
callback({
|
||||
nid: nid,
|
||||
text: notification[0],
|
||||
score: notification[1],
|
||||
path: notification[2],
|
||||
datetime: notification[3],
|
||||
uniqueId: notification[4],
|
||||
read: readIdx !== null ? true : false
|
||||
});
|
||||
});
|
||||
},
|
||||
create: function(text, path, uniqueId, callback) {
|
||||
/**
|
||||
* uniqueId is used solely to override stale nids.
|
||||
* If a new nid is pushed to a user and an existing nid in the user's
|
||||
* (un)read list contains the same uniqueId, it will be removed, and
|
||||
* the new one put in its place.
|
||||
*/
|
||||
RDB.incr('notifications:next_nid', function(err, nid) {
|
||||
RDB.sadd('notifications', nid);
|
||||
RDB.hmset('notifications:' + nid, {
|
||||
text: text || '',
|
||||
path: path || null,
|
||||
datetime: Date.now(),
|
||||
uniqueId: uniqueId || utils.generateUUID()
|
||||
}, function(err, status) {
|
||||
if (!err) {
|
||||
callback(nid);
|
||||
}
|
||||
(function(Notifications) {
|
||||
|
||||
Notifications.init = function() {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.init] Registering jobs.');
|
||||
}
|
||||
new cron('0 0 * * *', Notifications.prune, null, true);
|
||||
};
|
||||
|
||||
Notifications.get = function(nid, uid, callback) {
|
||||
RDB.multi()
|
||||
.hmget('notifications:' + nid, 'text', 'score', 'path', 'datetime', 'uniqueId')
|
||||
.zrank('uid:' + uid + ':notifications:read', nid)
|
||||
.exists('notifications:' + nid)
|
||||
.exec(function(err, results) {
|
||||
var notification = results[0],
|
||||
readIdx = results[1];
|
||||
|
||||
if (!results[2]) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
callback({
|
||||
nid: nid,
|
||||
text: notification[0],
|
||||
score: notification[1],
|
||||
path: notification[2],
|
||||
datetime: notification[3],
|
||||
uniqueId: notification[4],
|
||||
read: readIdx !== null ? true : false
|
||||
});
|
||||
});
|
||||
},
|
||||
destroy: function(nid) {
|
||||
var multi = RDB.multi();
|
||||
|
||||
multi.del('notifications:' + nid);
|
||||
multi.srem('notifications', nid);
|
||||
|
||||
multi.exec(function(err) {
|
||||
if (err) {
|
||||
winston.error('Problem deleting expired notifications. Stack follows.');
|
||||
winston.error(err.stack);
|
||||
}
|
||||
});
|
||||
},
|
||||
push: function(nid, uids, callback) {
|
||||
if (!Array.isArray(uids)) uids = [uids];
|
||||
|
||||
var numUids = uids.length,
|
||||
x;
|
||||
|
||||
notifications.get(nid, null, function(notif_data) {
|
||||
for (x = 0; x < numUids; x++) {
|
||||
if (parseInt(uids[x], 10) > 0) {
|
||||
(function(uid) {
|
||||
notifications.remove_by_uniqueId(notif_data.uniqueId, uid, function() {
|
||||
RDB.zadd('uid:' + uid + ':notifications:unread', notif_data.datetime, nid);
|
||||
global.io.sockets.in('uid_' + uid).emit('event:new_notification');
|
||||
if (callback) {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
})(uids[x]);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
remove_by_uniqueId: function(uniqueId, uid, callback) {
|
||||
async.parallel([
|
||||
function(next) {
|
||||
RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
|
||||
if (nids && nids.length > 0) {
|
||||
async.each(nids, function(nid, next) {
|
||||
notifications.get(nid, uid, function(nid_info) {
|
||||
if (nid_info.uniqueId === uniqueId) {
|
||||
RDB.zrem('uid:' + uid + ':notifications:unread', nid);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
RDB.zrange('uid:' + uid + ':notifications:read', 0, -1, function(err, nids) {
|
||||
if (nids && nids.length > 0) {
|
||||
async.each(nids, function(nid, next) {
|
||||
notifications.get(nid, uid, function(nid_info) {
|
||||
if (nid_info && nid_info.uniqueId === uniqueId) {
|
||||
RDB.zrem('uid:' + uid + ':notifications:read', nid);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
};
|
||||
|
||||
Notifications.create = function(text, path, uniqueId, callback) {
|
||||
/**
|
||||
* uniqueId is used solely to override stale nids.
|
||||
* If a new nid is pushed to a user and an existing nid in the user's
|
||||
* (un)read list contains the same uniqueId, it will be removed, and
|
||||
* the new one put in its place.
|
||||
*/
|
||||
RDB.incr('notifications:next_nid', function(err, nid) {
|
||||
RDB.sadd('notifications', nid);
|
||||
RDB.hmset('notifications:' + nid, {
|
||||
text: text || '',
|
||||
path: path || null,
|
||||
datetime: Date.now(),
|
||||
uniqueId: uniqueId || utils.generateUUID()
|
||||
}, function(err, status) {
|
||||
if (!err) {
|
||||
callback(true);
|
||||
callback(nid);
|
||||
}
|
||||
});
|
||||
},
|
||||
mark_read: function(nid, uid, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
function destroy(nid) {
|
||||
var multi = RDB.multi();
|
||||
|
||||
multi.del('notifications:' + nid);
|
||||
multi.srem('notifications', nid);
|
||||
|
||||
multi.exec(function(err) {
|
||||
if (err) {
|
||||
winston.error('Problem deleting expired notifications. Stack follows.');
|
||||
winston.error(err.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Notifications.push = function(nid, uids, callback) {
|
||||
if (!Array.isArray(uids)) uids = [uids];
|
||||
|
||||
var numUids = uids.length,
|
||||
x;
|
||||
|
||||
Notifications.get(nid, null, function(notif_data) {
|
||||
for (x = 0; x < numUids; x++) {
|
||||
if (parseInt(uids[x], 10) > 0) {
|
||||
(function(uid) {
|
||||
remove_by_uniqueId(notif_data.uniqueId, uid, function() {
|
||||
RDB.zadd('uid:' + uid + ':notifications:unread', notif_data.datetime, nid);
|
||||
|
||||
websockets.in('uid_' + uid).emit('event:new_notification');
|
||||
|
||||
if (callback) {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
})(uids[x]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function remove_by_uniqueId(uniqueId, uid, callback) {
|
||||
async.parallel([
|
||||
function(next) {
|
||||
RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
|
||||
if (nids && nids.length > 0) {
|
||||
async.each(nids, function(nid, next) {
|
||||
Notifications.get(nid, uid, function(nid_info) {
|
||||
if (nid_info.uniqueId === uniqueId) {
|
||||
RDB.zrem('uid:' + uid + ':notifications:unread', nid);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
RDB.zrange('uid:' + uid + ':notifications:read', 0, -1, function(err, nids) {
|
||||
if (nids && nids.length > 0) {
|
||||
async.each(nids, function(nid, next) {
|
||||
Notifications.get(nid, uid, function(nid_info) {
|
||||
if (nid_info && nid_info.uniqueId === uniqueId) {
|
||||
RDB.zrem('uid:' + uid + ':notifications:read', nid);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (!err) {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Notifications.mark_read = function(nid, uid, callback) {
|
||||
if (parseInt(uid) > 0) {
|
||||
notifications.get(nid, uid, function(notif_data) {
|
||||
Notifications.get(nid, uid, function(notif_data) {
|
||||
RDB.zrem('uid:' + uid + ':notifications:unread', nid);
|
||||
RDB.zadd('uid:' + uid + ':notifications:read', notif_data.datetime, nid);
|
||||
if (callback) {
|
||||
@@ -148,121 +160,115 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
mark_read_multiple: function(nids, uid, callback) {
|
||||
if (!Array.isArray(nids) && parseInt(nids, 10) > 0) {
|
||||
nids = [nids];
|
||||
}
|
||||
|
||||
Notifications.mark_read_multiple = function(nids, uid, callback) {
|
||||
if (!Array.isArray(nids) && parseInt(nids, 10) > 0) {
|
||||
nids = [nids];
|
||||
}
|
||||
|
||||
async.each(nids, function(nid, next) {
|
||||
Notifications.mark_read(nid, uid, function(err) {
|
||||
if (!err) {
|
||||
next(null);
|
||||
}
|
||||
});
|
||||
}, function(err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Notifications.mark_all_read = function(uid, callback) {
|
||||
RDB.zrange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.each(nids, function(nid, next) {
|
||||
notifications.mark_read(nid, uid, function(err) {
|
||||
if (!err) {
|
||||
next(null);
|
||||
}
|
||||
});
|
||||
}, function(err) {
|
||||
if (callback) {
|
||||
if (nids.length > 0) {
|
||||
Notifications.mark_read_multiple(nids, uid, function(err) {
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
},
|
||||
mark_all_read: function(uid, callback) {
|
||||
RDB.zrange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Notifications.prune = function(cutoff) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.prune] Removing expired notifications from the database.');
|
||||
}
|
||||
|
||||
if (nids.length > 0) {
|
||||
notifications.mark_read_multiple(nids, uid, function(err) {
|
||||
callback(err);
|
||||
var today = new Date(),
|
||||
numPruned = 0;
|
||||
|
||||
if (!cutoff) {
|
||||
cutoff = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
|
||||
}
|
||||
|
||||
var cutoffTime = cutoff.getTime();
|
||||
|
||||
async.parallel({
|
||||
"inboxes": function(next) {
|
||||
RDB.keys('uid:*:notifications:unread', next);
|
||||
},
|
||||
"nids": function(next) {
|
||||
RDB.smembers('notifications', function(err, nids) {
|
||||
async.filter(nids, function(nid, next) {
|
||||
RDB.hget('notifications:' + nid, 'datetime', function(err, datetime) {
|
||||
if (parseInt(datetime, 10) < cutoffTime) {
|
||||
next(true);
|
||||
} else {
|
||||
next(false);
|
||||
}
|
||||
});
|
||||
}, function(expiredNids) {
|
||||
next(null, expiredNids);
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
prune: function(cutoff) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.prune] Removing expired notifications from the database.');
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (!err) {
|
||||
var numInboxes = results.inboxes.length,
|
||||
x;
|
||||
|
||||
var today = new Date(),
|
||||
numPruned = 0;
|
||||
async.eachSeries(results.nids, function(nid, next) {
|
||||
var multi = RDB.multi();
|
||||
|
||||
if (!cutoff) {
|
||||
cutoff = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
|
||||
}
|
||||
for(x=0;x<numInboxes;x++) {
|
||||
multi.zscore(results.inboxes[x], nid);
|
||||
}
|
||||
|
||||
var cutoffTime = cutoff.getTime();
|
||||
|
||||
async.parallel({
|
||||
"inboxes": function(next) {
|
||||
RDB.keys('uid:*:notifications:unread', next);
|
||||
},
|
||||
"nids": function(next) {
|
||||
RDB.smembers('notifications', function(err, nids) {
|
||||
async.filter(nids, function(nid, next) {
|
||||
RDB.hget('notifications:' + nid, 'datetime', function(err, datetime) {
|
||||
if (parseInt(datetime, 10) < cutoffTime) {
|
||||
next(true);
|
||||
} else {
|
||||
next(false);
|
||||
multi.exec(function(err, results) {
|
||||
// If the notification is not present in any inbox, delete it altogether
|
||||
var expired = results.every(function(present) {
|
||||
if (present === null) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}, function(expiredNids) {
|
||||
next(null, expiredNids);
|
||||
});
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (!err) {
|
||||
var numInboxes = results.inboxes.length,
|
||||
x;
|
||||
|
||||
async.eachSeries(results.nids, function(nid, next) {
|
||||
var multi = RDB.multi();
|
||||
|
||||
for(x=0;x<numInboxes;x++) {
|
||||
multi.zscore(results.inboxes[x], nid);
|
||||
if (expired) {
|
||||
destroy(nid);
|
||||
numPruned++;
|
||||
}
|
||||
|
||||
multi.exec(function(err, results) {
|
||||
// If the notification is not present in any inbox, delete it altogether
|
||||
var expired = results.every(function(present) {
|
||||
if (present === null) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (expired) {
|
||||
notifications.destroy(nid);
|
||||
numPruned++;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.');
|
||||
}
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
}, function(err) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.error('[notifications.prune] Ran into trouble pruning expired notifications. Stack trace to follow.');
|
||||
winston.error(err.stack);
|
||||
winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.error('[notifications.prune] Ran into trouble pruning expired notifications. Stack trace to follow.');
|
||||
winston.error(err.stack);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
}(exports));
|
||||
|
||||
module.exports = {
|
||||
init: notifications.init,
|
||||
get: notifications.get,
|
||||
create: notifications.create,
|
||||
push: notifications.push,
|
||||
mark_read: notifications.mark_read_multiple,
|
||||
mark_all_read: notifications.mark_all_read,
|
||||
prune: notifications.prune
|
||||
};
|
||||
|
||||
@@ -284,14 +284,14 @@ var fs = require('fs'),
|
||||
winston.warn("Plugin: " + file + " is corrupted or invalid. Please check plugin.json for errors.")
|
||||
return next(err, null);
|
||||
}
|
||||
|
||||
|
||||
_self.isActive(config.id, function(err, active) {
|
||||
if (err) next(new Error('no-active-state'));
|
||||
|
||||
delete config.library;
|
||||
delete config.hooks;
|
||||
config.active = active;
|
||||
config.activeText = '<i class="icon-off"></i> ' + (active ? 'Dea' : 'A') + 'ctivate';
|
||||
config.activeText = '<i class="fa fa-power-off"></i> ' + (active ? 'Dea' : 'A') + 'ctivate';
|
||||
next(null, config);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
var RDB = require('./redis.js'),
|
||||
posts = require('./posts.js'),
|
||||
var RDB = require('./redis'),
|
||||
posts = require('./posts'),
|
||||
topics = require('./topics'),
|
||||
threadTools = require('./threadTools.js'),
|
||||
user = require('./user.js'),
|
||||
threadTools = require('./threadTools'),
|
||||
user = require('./user'),
|
||||
websockets = require('./websockets'),
|
||||
async = require('async'),
|
||||
nconf = require('nconf'),
|
||||
validator = require('validator'),
|
||||
@@ -13,7 +14,7 @@ var RDB = require('./redis.js'),
|
||||
postSearch = reds.createSearch('nodebbpostsearch'),
|
||||
topicSearch = reds.createSearch('nodebbtopicsearch'),
|
||||
winston = require('winston'),
|
||||
meta = require('./meta.js'),
|
||||
meta = require('./meta'),
|
||||
Feed = require('./feed');
|
||||
|
||||
(function(PostTools) {
|
||||
@@ -34,7 +35,7 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
function getThreadPrivileges(next) {
|
||||
posts.getPostField(pid, 'tid', function(tid) {
|
||||
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||
threadTools.privileges(tid, uid, function(privileges) {
|
||||
next(null, privileges);
|
||||
});
|
||||
@@ -42,7 +43,7 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
function isOwnPost(next) {
|
||||
posts.getPostField(pid, 'uid', function(author) {
|
||||
posts.getPostField(pid, 'uid', function(err, author) {
|
||||
next(null, parseInt(author, 10) === parseInt(uid, 10));
|
||||
});
|
||||
}
|
||||
@@ -87,7 +88,7 @@ var RDB = require('./redis.js'),
|
||||
|
||||
async.parallel([
|
||||
function(next) {
|
||||
posts.getPostField(pid, 'tid', function(tid) {
|
||||
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||
PostTools.isMain(pid, tid, function(isMainPost) {
|
||||
if (isMainPost) {
|
||||
topics.setTopicField(tid, 'title', title);
|
||||
@@ -107,7 +108,7 @@ var RDB = require('./redis.js'),
|
||||
PostTools.parse(content, next);
|
||||
}
|
||||
], function(err, results) {
|
||||
io.sockets.in('topic_' + results[0].tid).emit('event:post_edited', {
|
||||
websockets.in('topic_' + results[0].tid).emit('event:post_edited', {
|
||||
pid: pid,
|
||||
title: validator.sanitize(title).escape(),
|
||||
isMainPost: results[0].isMainPost,
|
||||
@@ -132,41 +133,47 @@ var RDB = require('./redis.js'),
|
||||
RDB.decr('totalpostcount');
|
||||
postSearch.remove(pid);
|
||||
|
||||
posts.getPostFields(pid, ['tid', 'uid'], function(postData) {
|
||||
posts.getPostFields(pid, ['tid', 'uid'], function(err, postData) {
|
||||
RDB.hincrby('topic:' + postData.tid, 'postcount', -1);
|
||||
|
||||
user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) {
|
||||
RDB.zadd('users:postcount', postcount, postData.uid);
|
||||
});
|
||||
|
||||
io.sockets. in ('topic_' + postData.tid).emit('event:post_deleted', {
|
||||
pid: pid
|
||||
});
|
||||
|
||||
// Delete the thread if it is the last undeleted post
|
||||
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
|
||||
if (err && err.message === 'no-undeleted-pids-found') {
|
||||
threadTools.delete(postData.tid, -1, function(err) {
|
||||
if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
|
||||
threadTools.delete(postData.tid, function(err) {
|
||||
if (err) {
|
||||
winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
||||
posts.getPostField(pid, 'timestamp', function(err, timestamp) {
|
||||
topics.updateTimestamp(postData.tid, timestamp);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Feed.updateTopic(postData.tid);
|
||||
Feed.updateRecent();
|
||||
|
||||
callback();
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
PostTools.privileges(pid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
success();
|
||||
posts.getPostField(pid, 'deleted', function(err, deleted) {
|
||||
if(deleted === '1') {
|
||||
return callback(new Error('Post already deleted!'));
|
||||
}
|
||||
|
||||
PostTools.privileges(pid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
success();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
PostTools.restore = function(uid, pid, callback) {
|
||||
@@ -174,17 +181,13 @@ var RDB = require('./redis.js'),
|
||||
posts.setPostField(pid, 'deleted', 0);
|
||||
RDB.incr('totalpostcount');
|
||||
|
||||
posts.getPostFields(pid, ['tid', 'uid', 'content'], function(postData) {
|
||||
posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) {
|
||||
RDB.hincrby('topic:' + postData.tid, 'postcount', 1);
|
||||
|
||||
user.incrementUserFieldBy(postData.uid, 'postcount', 1);
|
||||
|
||||
io.sockets. in ('topic_' + postData.tid).emit('event:post_restored', {
|
||||
pid: pid
|
||||
});
|
||||
|
||||
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
|
||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
||||
posts.getPostField(pid, 'timestamp', function(err, timestamp) {
|
||||
topics.updateTimestamp(postData.tid, timestamp);
|
||||
});
|
||||
});
|
||||
@@ -197,6 +200,7 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
|
||||
Feed.updateTopic(postData.tid);
|
||||
Feed.updateRecent();
|
||||
|
||||
postSearch.index(postData.content, pid);
|
||||
|
||||
@@ -204,10 +208,16 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
};
|
||||
|
||||
PostTools.privileges(pid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
success();
|
||||
posts.getPostField(pid, 'deleted', function(err, deleted) {
|
||||
if(deleted === '0') {
|
||||
return callback(new Error('Post already restored'));
|
||||
}
|
||||
|
||||
PostTools.privileges(pid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
success();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
437
src/posts.js
437
src/posts.js
@@ -2,6 +2,7 @@ var RDB = require('./redis.js'),
|
||||
utils = require('./../public/src/utils.js'),
|
||||
user = require('./user.js'),
|
||||
topics = require('./topics.js'),
|
||||
categories = require('./categories.js'),
|
||||
favourites = require('./favourites.js'),
|
||||
threadTools = require('./threadTools.js'),
|
||||
postTools = require('./postTools'),
|
||||
@@ -13,11 +14,175 @@ var RDB = require('./redis.js'),
|
||||
postSearch = reds.createSearch('nodebbpostsearch'),
|
||||
nconf = require('nconf'),
|
||||
meta = require('./meta.js'),
|
||||
validator = require('validator'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(Posts) {
|
||||
var customUserInfo = {};
|
||||
|
||||
Posts.create = function(uid, tid, content, callback) {
|
||||
if (uid === null) {
|
||||
callback(new Error('invalid-user'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
topics.isLocked(tid, function(err, locked) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
} else if(locked) {
|
||||
callback(new Error('topic-locked'), null);
|
||||
}
|
||||
|
||||
RDB.incr('global:next_post_id', function(err, pid) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:post.save', content, function(err, newContent) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
content = newContent;
|
||||
|
||||
var timestamp = Date.now(),
|
||||
postData = {
|
||||
'pid': pid,
|
||||
'uid': uid,
|
||||
'tid': tid,
|
||||
'content': content,
|
||||
'timestamp': timestamp,
|
||||
'reputation': 0,
|
||||
'editor': '',
|
||||
'edited': 0,
|
||||
'deleted': 0,
|
||||
'fav_button_class': '',
|
||||
'fav_star_class': 'fa-star-o',
|
||||
'show_banned': 'hide',
|
||||
'relativeTime': new Date(timestamp).toISOString(),
|
||||
'post_rep': '0',
|
||||
'edited-class': 'none',
|
||||
'relativeEditTime': ''
|
||||
};
|
||||
|
||||
RDB.hmset('post:' + pid, postData);
|
||||
|
||||
topics.addPostToTopic(tid, pid);
|
||||
topics.increasePostCount(tid);
|
||||
topics.updateTimestamp(tid, timestamp);
|
||||
|
||||
RDB.incr('totalpostcount');
|
||||
|
||||
topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) {
|
||||
|
||||
RDB.handle(err);
|
||||
|
||||
var cid = topicData.cid;
|
||||
|
||||
feed.updateTopic(tid);
|
||||
feed.updateRecent();
|
||||
|
||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||
|
||||
if(topicData.pinned === '0') {
|
||||
RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
|
||||
}
|
||||
|
||||
RDB.scard('cid:' + cid + ':active_users', function(err, amount) {
|
||||
if (amount > 16) {
|
||||
RDB.spop('cid:' + cid + ':active_users');
|
||||
}
|
||||
|
||||
categories.addActiveUser(cid, uid);
|
||||
});
|
||||
});
|
||||
|
||||
user.onNewPostMade(uid, tid, pid, timestamp);
|
||||
|
||||
plugins.fireHook('filter:post.get', postData, function(err, newPostData) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
postData = newPostData;
|
||||
|
||||
postTools.parse(postData.content, function(err, content) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
postData.content = content;
|
||||
|
||||
plugins.fireHook('action:post.save', postData);
|
||||
|
||||
postSearch.index(content, pid);
|
||||
|
||||
callback(null, postData);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Posts.reply = function(tid, uid, content, callback) {
|
||||
if(content) {
|
||||
content = content.trim();
|
||||
}
|
||||
|
||||
if (!content || content.length < meta.config.minimumPostLength) {
|
||||
callback(new Error('content-too-short'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
Posts.create(uid, tid, content, function(err, postData) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
} else if(!postData) {
|
||||
callback(new Error('reply-error'), null);
|
||||
}
|
||||
|
||||
async.parallel([
|
||||
function(next) {
|
||||
topics.markUnRead(tid, function(err) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
topics.markAsRead(tid, uid);
|
||||
next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
Posts.getCidByPid(postData.pid, function(err, cid) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
RDB.del('cid:' + cid + ':read_by_uid');
|
||||
next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
threadTools.notifyFollowers(tid, uid);
|
||||
next();
|
||||
},
|
||||
function(next) {
|
||||
Posts.addUserInfoToPost(postData, function(err) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
], function(err, results) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
callback(null, postData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getPostsByTid = function(tid, start, end, callback) {
|
||||
RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
|
||||
RDB.handle(err);
|
||||
@@ -86,7 +251,7 @@ var RDB = require('./redis.js'),
|
||||
function getPostSummary(pid, callback) {
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) {
|
||||
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(err, postData) {
|
||||
if (postData.deleted === '1') return callback(null);
|
||||
else {
|
||||
postData.relativeTime = new Date(parseInt(postData.timestamp || 0, 10)).toISOString();
|
||||
@@ -100,12 +265,15 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
},
|
||||
function(postData, next) {
|
||||
topics.getTopicFields(postData.tid, ['slug', 'deleted'], function(err, topicData) {
|
||||
topics.getTopicFields(postData.tid, ['title', 'cid', 'slug', 'deleted'], function(err, topicData) {
|
||||
if (err) return callback(err);
|
||||
else if (topicData.deleted === '1') return callback(null);
|
||||
|
||||
postData.topicSlug = topicData.slug;
|
||||
next(null, postData);
|
||||
categories.getCategoryField(topicData.cid, 'name', function(err, categoryData) {
|
||||
postData.category_name = categoryData;
|
||||
postData.title = validator.sanitize(topicData.title).escape();
|
||||
postData.topicSlug = topicData.slug;
|
||||
next(null, postData);
|
||||
})
|
||||
});
|
||||
},
|
||||
function(postData, next) {
|
||||
@@ -131,51 +299,48 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: this function is never called except from some debug route. clean up?
|
||||
Posts.getPostData = function(pid, callback) {
|
||||
RDB.hgetall('post:' + pid, function(err, data) {
|
||||
if (err === null) {
|
||||
plugins.fireHook('filter:post.get', data, function(err, newData) {
|
||||
if (!err) callback(newData);
|
||||
else callback(data);
|
||||
});
|
||||
} else {
|
||||
winston.error(err);
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:post.get', data, function(err, newData) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
callback(null, newData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getPostFields = function(pid, fields, callback) {
|
||||
RDB.hmgetObject('post:' + pid, fields, function(err, data) {
|
||||
if (err === null) {
|
||||
// TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this:
|
||||
data = data || {};
|
||||
data.pid = pid;
|
||||
data.fields = fields;
|
||||
|
||||
plugins.fireHook('filter:post.getFields', data, function(err, data) {
|
||||
callback(data);
|
||||
});
|
||||
} else {
|
||||
console.log(err);
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
// TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this:
|
||||
data = data || {};
|
||||
data.pid = pid;
|
||||
data.fields = fields;
|
||||
|
||||
plugins.fireHook('filter:post.getFields', data, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
callback(null, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getPostField = function(pid, field, callback) {
|
||||
RDB.hget('post:' + pid, field, function(err, data) {
|
||||
if (err === null) {
|
||||
// TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this:
|
||||
data = data || {};
|
||||
data.pid = pid;
|
||||
data.field = field;
|
||||
|
||||
plugins.fireHook('filter:post.getField', data, function(err, data) {
|
||||
callback(data);
|
||||
});
|
||||
} else {
|
||||
console.log(err);
|
||||
Posts.getPostFields(pid, [field], function(err, data) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
callback(null, data[field]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,8 +357,8 @@ var RDB = require('./redis.js'),
|
||||
var posts = [],
|
||||
multi = RDB.multi();
|
||||
|
||||
for(var x=0,numPids=pids.length;x<numPids;x++) {
|
||||
multi.hgetall("post:"+pids[x]);
|
||||
for(var x=0, numPids=pids.length; x<numPids; x++) {
|
||||
multi.hgetall("post:" + pids[x]);
|
||||
}
|
||||
|
||||
multi.exec(function (err, replies) {
|
||||
@@ -204,12 +369,11 @@ var RDB = require('./redis.js'),
|
||||
postData['edited-class'] = postData.editor !== '' ? '' : 'none';
|
||||
try {
|
||||
postData.relativeTime = new Date(parseInt(postData.timestamp,10)).toISOString();
|
||||
postData['relativeEditTime'] = postData.edited !== '0' ? (new Date(parseInt(postData.edited,10)).toISOString()) : '';
|
||||
postData.relativeEditTime = postData.edited !== '0' ? (new Date(parseInt(postData.edited,10)).toISOString()) : '';
|
||||
} catch(e) {
|
||||
winston.err('invalid time value');
|
||||
}
|
||||
|
||||
|
||||
if (postData.uploadedImages) {
|
||||
try {
|
||||
postData.uploadedImages = JSON.parse(postData.uploadedImages);
|
||||
@@ -238,17 +402,23 @@ var RDB = require('./redis.js'),
|
||||
})
|
||||
}
|
||||
|
||||
Posts.get_cid_by_pid = function(pid, callback) {
|
||||
Posts.getPostField(pid, 'tid', function(tid) {
|
||||
if (tid) {
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
if (cid) {
|
||||
callback(cid);
|
||||
} else {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
Posts.getCidByPid = function(pid, callback) {
|
||||
Posts.getPostField(pid, 'tid', function(err, tid) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
if (cid) {
|
||||
callback(null, cid);
|
||||
} else {
|
||||
callback(new Error('invalid-category-id'), null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -265,163 +435,25 @@ var RDB = require('./redis.js'),
|
||||
Posts.emitTooManyPostsAlert = function(socket) {
|
||||
socket.emit('event:alert', {
|
||||
title: 'Too many posts!',
|
||||
message: 'You can only post every ' + meta.config.postDelay / 1000 + ' seconds.',
|
||||
message: 'You can only post every ' + meta.config.postDelay + ' seconds.',
|
||||
type: 'danger',
|
||||
timeout: 2000
|
||||
});
|
||||
}
|
||||
|
||||
Posts.reply = function(tid, uid, content, callback) {
|
||||
if(content) {
|
||||
content = content.trim();
|
||||
}
|
||||
|
||||
if (!content || content.length < meta.config.minimumPostLength) {
|
||||
callback(new Error('content-too-short'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
Posts.create(uid, tid, content, function(postData) {
|
||||
if (postData) {
|
||||
|
||||
topics.markUnRead(tid);
|
||||
|
||||
Posts.get_cid_by_pid(postData.pid, function(cid) {
|
||||
RDB.del('cid:' + cid + ':read_by_uid', function(err, data) {
|
||||
topics.markAsRead(tid, uid);
|
||||
});
|
||||
});
|
||||
|
||||
threadTools.notifyFollowers(tid, uid);
|
||||
|
||||
Posts.addUserInfoToPost(postData, function() {
|
||||
var socketData = {
|
||||
posts: [postData]
|
||||
};
|
||||
|
||||
io.sockets.in('topic_' + tid).emit('event:new_post', socketData);
|
||||
io.sockets.in('recent_posts').emit('event:new_post', socketData);
|
||||
io.sockets.in('user/' + uid).emit('event:new_post', socketData);
|
||||
});
|
||||
|
||||
callback(null, 'Reply successful');
|
||||
} else {
|
||||
callback(new Error('reply-error'), null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Posts.create = function(uid, tid, content, callback) {
|
||||
if (uid === null) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
topics.isLocked(tid, function(locked) {
|
||||
if (!locked || locked === '0') {
|
||||
RDB.incr('global:next_post_id', function(err, pid) {
|
||||
RDB.handle(err);
|
||||
|
||||
plugins.fireHook('filter:post.save', content, function(err, newContent) {
|
||||
if (!err) content = newContent;
|
||||
|
||||
var timestamp = Date.now(),
|
||||
postData = {
|
||||
'pid': pid,
|
||||
'uid': uid,
|
||||
'tid': tid,
|
||||
'content': content,
|
||||
'timestamp': timestamp,
|
||||
'reputation': 0,
|
||||
'editor': '',
|
||||
'edited': 0,
|
||||
'deleted': 0,
|
||||
'fav_button_class': '',
|
||||
'fav_star_class': 'icon-star-empty',
|
||||
'show_banned': 'hide',
|
||||
'relativeTime': new Date(timestamp).toISOString(),
|
||||
'post_rep': '0',
|
||||
'edited-class': 'none',
|
||||
'relativeEditTime': ''
|
||||
};
|
||||
|
||||
RDB.hmset('post:' + pid, postData);
|
||||
|
||||
topics.addPostToTopic(tid, pid);
|
||||
topics.increasePostCount(tid);
|
||||
topics.updateTimestamp(tid, timestamp);
|
||||
|
||||
RDB.incr('totalpostcount');
|
||||
|
||||
topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) {
|
||||
|
||||
RDB.handle(err);
|
||||
|
||||
var cid = topicData.cid;
|
||||
|
||||
feed.updateTopic(tid);
|
||||
|
||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||
|
||||
if(topicData.pinned === '0')
|
||||
RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
|
||||
|
||||
RDB.scard('cid:' + cid + ':active_users', function(err, amount) {
|
||||
if (amount > 10) {
|
||||
RDB.spop('cid:' + cid + ':active_users');
|
||||
}
|
||||
|
||||
categories.addActiveUser(cid, uid);
|
||||
});
|
||||
});
|
||||
|
||||
user.onNewPostMade(uid, tid, pid, timestamp);
|
||||
|
||||
async.parallel({
|
||||
content: function(next) {
|
||||
plugins.fireHook('filter:post.get', postData, function(err, newPostData) {
|
||||
if (!err) postData = newPostData;
|
||||
|
||||
postTools.parse(postData.content, function(err, content) {
|
||||
next(null, content);
|
||||
});
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
postData.content = results.content;
|
||||
callback(postData);
|
||||
});
|
||||
|
||||
plugins.fireHook('action:post.save', postData);
|
||||
|
||||
postSearch.index(content, pid);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Posts.uploadPostImage = function(image, callback) {
|
||||
var imgur = require('./imgur');
|
||||
imgur.setClientID(meta.config.imgurClientID);
|
||||
|
||||
if(!image)
|
||||
return callback('invalid image', null);
|
||||
|
||||
imgur.upload(image.data, 'base64', function(err, data) {
|
||||
require('./imgur').upload(meta.config.imgurClientID, image.data, 'base64', function(err, data) {
|
||||
if(err) {
|
||||
callback('Can\'t upload image!', null);
|
||||
callback(err.message, null);
|
||||
} else {
|
||||
if(data.success) {
|
||||
var img= {url:data.data.link, name:image.name};
|
||||
|
||||
callback(null, img);
|
||||
} else {
|
||||
winston.error('Can\'t upload image, did you set imgurClientID?');
|
||||
callback("upload error", null);
|
||||
}
|
||||
callback(null, {
|
||||
url: data.link,
|
||||
name: image.name
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -444,25 +476,12 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getTopicPostStats = function() {
|
||||
RDB.mget(['totaltopiccount', 'totalpostcount'], function(err, data) {
|
||||
if (err === null) {
|
||||
var stats = {
|
||||
topics: data[0] ? data[0] : 0,
|
||||
posts: data[1] ? data[1] : 0
|
||||
};
|
||||
|
||||
io.sockets.emit('post.stats', stats);
|
||||
} else
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
Posts.reIndexPids = function(pids, callback) {
|
||||
|
||||
function reIndex(pid, callback) {
|
||||
|
||||
Posts.getPostField(pid, 'content', function(content) {
|
||||
Posts.getPostField(pid, 'content', function(err, content) {
|
||||
postSearch.remove(pid, function() {
|
||||
|
||||
if (content && content.length) {
|
||||
|
||||
20
src/redis.js
20
src/redis.js
@@ -58,18 +58,18 @@
|
||||
*/
|
||||
RedisDB.hmgetObject = function(key, fields, callback) {
|
||||
RedisDB.hmget(key, 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(null, returnData);
|
||||
} else {
|
||||
console.log(err);
|
||||
callback(err, null);
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
var returnData = {};
|
||||
|
||||
for (var i = 0, ii = fields.length; i < ii; ++i) {
|
||||
returnData[fields[i]] = data[i];
|
||||
}
|
||||
|
||||
callback(null, returnData);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -19,21 +19,27 @@ var user = require('./../user.js'),
|
||||
});
|
||||
}
|
||||
|
||||
Admin.build_header = function (res, callback) {
|
||||
Admin.buildHeader = function (req, res, callback) {
|
||||
var custom_header = {
|
||||
'plugins': []
|
||||
};
|
||||
|
||||
plugins.fireHook('filter:admin.header.build', custom_header, function(err, custom_header) {
|
||||
callback(err, templates['admin/header'].parse({
|
||||
csrf: res.locals.csrf_token,
|
||||
relative_path: nconf.get('relative_path'),
|
||||
plugins: custom_header.plugins
|
||||
}));
|
||||
user.getUserFields(req.user.uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
||||
|
||||
plugins.fireHook('filter:admin.header.build', custom_header, function(err, custom_header) {
|
||||
callback(err, templates['admin/header'].parse({
|
||||
csrf: res.locals.csrf_token,
|
||||
relative_path: nconf.get('relative_path'),
|
||||
plugins: custom_header.plugins,
|
||||
userpicture: userData.picture,
|
||||
username: userData.username,
|
||||
userslug: userData.userslug
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Admin.create_routes = function (app) {
|
||||
Admin.createRoutes = function (app) {
|
||||
|
||||
(function () {
|
||||
var routes = [
|
||||
@@ -46,7 +52,7 @@ var user = require('./../user.js'),
|
||||
for (var i = 0, ii = routes.length; i < ii; i++) {
|
||||
(function (route) {
|
||||
app.get('/admin/' + route, Admin.isAdmin, function (req, res) {
|
||||
Admin.build_header(res, function(err, header) {
|
||||
Admin.buildHeader(req, res, function(err, header) {
|
||||
res.send(header + app.create_route('admin/' + route) + templates['admin/footer']);
|
||||
});
|
||||
});
|
||||
@@ -58,7 +64,7 @@ var user = require('./../user.js'),
|
||||
for (var i = 0, ii = unit_tests.length; i < ii; i++) {
|
||||
(function (route) {
|
||||
app.get('/admin/testing/' + route, Admin.isAdmin, function (req, res) {
|
||||
Admin.build_header(res, function(err, header) {
|
||||
Admin.buildHeader(req, res, function(err, header) {
|
||||
res.send(header + app.create_route('admin/testing/' + route) + templates['admin/footer']);
|
||||
});
|
||||
});
|
||||
@@ -69,13 +75,13 @@ var user = require('./../user.js'),
|
||||
|
||||
app.namespace('/admin', function () {
|
||||
app.get('/', Admin.isAdmin, function (req, res) {
|
||||
Admin.build_header(res, function(err, header) {
|
||||
Admin.buildHeader(req, res, function(err, header) {
|
||||
res.send(header + app.create_route('admin/index') + templates['admin/footer']);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/index', Admin.isAdmin, function (req, res) {
|
||||
Admin.build_header(res, function(err, header) {
|
||||
Admin.buildHeader(req, res, function(err, header) {
|
||||
res.send(header + app.create_route('admin/index') + templates['admin/footer']);
|
||||
});
|
||||
});
|
||||
@@ -105,7 +111,7 @@ var user = require('./../user.js'),
|
||||
}
|
||||
|
||||
var filename = 'site-logo' + extension;
|
||||
var uploadPath = path.join(process.cwd(), nconf.get('upload_path'), filename);
|
||||
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
|
||||
|
||||
winston.info('Attempting upload to: ' + uploadPath);
|
||||
|
||||
@@ -144,7 +150,7 @@ var user = require('./../user.js'),
|
||||
(function(route) {
|
||||
app[routes[route].method || 'get']('/admin' + routes[route].route, function(req, res) {
|
||||
routes[route].options(req, res, function(options) {
|
||||
Admin.build_header(res, function (err, header) {
|
||||
Admin.buildHeader(req, res, function (err, header) {
|
||||
res.send(header + options.content + templates['admin/footer']);
|
||||
});
|
||||
});
|
||||
@@ -158,7 +164,7 @@ var user = require('./../user.js'),
|
||||
app.namespace('/api/admin', function () {
|
||||
app.get('/index', function (req, res) {
|
||||
res.json({
|
||||
version: pkg.version
|
||||
version: pkg.version,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -214,13 +220,13 @@ var user = require('./../user.js'),
|
||||
});
|
||||
|
||||
app.get('/categories', function (req, res) {
|
||||
categories.getAllCategories(function (data) {
|
||||
categories.getAllCategories(0, function (err, data) {
|
||||
res.json(data);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/categories/active', function (req, res) {
|
||||
categories.getAllCategories(function (data) {
|
||||
categories.getAllCategories(0, function (err, data) {
|
||||
data.categories = data.categories.filter(function (category) {
|
||||
return (!category.disabled || category.disabled === "0");
|
||||
});
|
||||
@@ -229,7 +235,7 @@ var user = require('./../user.js'),
|
||||
});
|
||||
|
||||
app.get('/categories/disabled', function (req, res) {
|
||||
categories.getAllCategories(function (data) {
|
||||
categories.getAllCategories(0, function (err, data) {
|
||||
data.categories = data.categories.filter(function (category) {
|
||||
return category.disabled === "1";
|
||||
});
|
||||
@@ -238,9 +244,10 @@ var user = require('./../user.js'),
|
||||
});
|
||||
|
||||
app.get('/topics', function (req, res) {
|
||||
topics.getAllTopics(10, null, function (topics) {
|
||||
topics.getAllTopics(10, null, function (err, topics) {
|
||||
res.json({
|
||||
topics: topics
|
||||
topics: topics,
|
||||
notopics: topics.length === 0
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ var user = require('./../user.js'),
|
||||
|
||||
|
||||
(function (Api) {
|
||||
Api.create_routes = function (app) {
|
||||
Api.createRoutes = function (app) {
|
||||
app.namespace('/api', function () {
|
||||
app.get('/get_templates_listing', function (req, res) {
|
||||
utils.walk(path.join(__dirname, '../../', 'public/templates'), function (err, data) {
|
||||
@@ -32,13 +32,14 @@ var user = require('./../user.js'),
|
||||
config.maximumUsernameLength = meta.config.maximumUsernameLength;
|
||||
config.minimumPasswordLength = meta.config.minimumPasswordLength;
|
||||
config.useOutgoingLinksPage = meta.config.useOutgoingLinksPage;
|
||||
config.emailSetup = !!meta.config['email:from'];
|
||||
|
||||
res.json(200, config);
|
||||
});
|
||||
|
||||
app.get('/home', function (req, res) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
categories.getAllCategories(function (data) {
|
||||
categories.getAllCategories(uid, function (err, data) {
|
||||
data.categories = data.categories.filter(function (category) {
|
||||
return (!category.disabled || category.disabled === "0");
|
||||
});
|
||||
@@ -55,11 +56,10 @@ var user = require('./../user.js'),
|
||||
data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : ' none';
|
||||
data.motd_class += (meta.config.motd && meta.config.motd.length > 0 ? '' : ' default');
|
||||
|
||||
data.motd = require('marked')(meta.config.motd || "<div class=\"pull-right btn-group\"><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"icon-comment\"></i><span class='hidden-mobile'> Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"icon-github-alt\"></i><span class='hidden-mobile'> Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"icon-twitter\"></i><span class='hidden-mobile'> @dcplabs</span></a></div>\n\n# NodeBB <span>v" + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future.");
|
||||
data.motd = require('marked')(meta.config.motd || "<div class=\"pull-right btn-group\"><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-comment\"></i><span class='hidden-mobile'> Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-github\"></i><span class='hidden-mobile'> Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-twitter\"></i><span class='hidden-mobile'> @dcplabs</span></a></div>\n\n# NodeBB <span>v" + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future.");
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
}, uid);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/login', function (req, res) {
|
||||
@@ -152,8 +152,12 @@ var user = require('./../user.js'),
|
||||
|
||||
app.get('/recent/:term?', function (req, res) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
topics.getLatestTopics(uid, 0, 19, req.params.term, function (data) {
|
||||
res.json(data);
|
||||
topics.getLatestTopics(uid, 0, 19, req.params.term, function (err, data) {
|
||||
if (!err) {
|
||||
res.json(data);
|
||||
} else {
|
||||
res.send(500);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -204,7 +208,8 @@ var user = require('./../user.js'),
|
||||
|
||||
if (url) {
|
||||
res.json({
|
||||
url: url
|
||||
url: url,
|
||||
title: meta.config.title
|
||||
});
|
||||
} else {
|
||||
res.status(404);
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
return login_strategies;
|
||||
}
|
||||
|
||||
Auth.create_routes = function(app) {
|
||||
Auth.createRoutes = function(app) {
|
||||
app.post('/logout', function(req, res) {
|
||||
if (req.user && req.user.uid > 0) {
|
||||
winston.info('[Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')');
|
||||
@@ -178,11 +178,13 @@
|
||||
|
||||
app.post('/register', function(req, res) {
|
||||
user.create(req.body.username, req.body.password, req.body.email, function(err, uid) {
|
||||
|
||||
if (err === null && uid) {
|
||||
req.login({
|
||||
uid: uid
|
||||
}, function() {
|
||||
|
||||
require('./../websockets').emitUserCount();
|
||||
|
||||
if(req.body.referrer)
|
||||
res.redirect(req.body.referrer);
|
||||
else
|
||||
|
||||
@@ -1,20 +1,75 @@
|
||||
|
||||
var user = require('./../user'),
|
||||
categories = require('./../categories'),
|
||||
topics = require('./../topics'),
|
||||
posts = require('./../posts');
|
||||
|
||||
var DebugRoute = function(app) {
|
||||
app.namespace('/debug', function() {
|
||||
app.get('/prune', function(req, res) {
|
||||
var Notifications = require('../notifications');
|
||||
|
||||
Notifications.prune(new Date(), function() {
|
||||
console.log('done');
|
||||
});
|
||||
res.send();
|
||||
});
|
||||
app.namespace('/debug', function() {
|
||||
|
||||
app.get('/uuidtest', function(req, res) {
|
||||
var Utils = require('../../public/src/utils.js');
|
||||
app.get('/uid/:uid', function (req, res) {
|
||||
|
||||
res.send(Utils.generateUUID());
|
||||
if (!req.params.uid)
|
||||
return res.redirect('/404');
|
||||
|
||||
user.getUserData(req.params.uid, function (err, data) {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.json(404, {
|
||||
error: "User doesn't exist!"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
app.get('/cid/:cid', function (req, res) {
|
||||
categories.getCategoryData(req.params.cid, function (err, data) {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.send(404, "Category doesn't exist!");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/tid/:tid', function (req, res) {
|
||||
topics.getTopicData(req.params.tid, function (err, data) {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.send(404, "Topic doesn't exist!");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/pid/:pid', function (req, res) {
|
||||
posts.getPostData(req.params.pid, function (err, data) {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.send(404, "Post doesn't exist!");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/prune', function(req, res) {
|
||||
var Notifications = require('../notifications');
|
||||
|
||||
Notifications.prune(new Date(), function() {
|
||||
console.log('done');
|
||||
});
|
||||
res.send();
|
||||
});
|
||||
|
||||
app.get('/uuidtest', function(req, res) {
|
||||
var Utils = require('../../public/src/utils.js');
|
||||
|
||||
res.send(Utils.generateUUID());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = DebugRoute;
|
||||
@@ -1,9 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
var nconf = require('nconf'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
validator = require('validator'),
|
||||
Plugins = require('../plugins'),
|
||||
|
||||
PluginRoutes = function(app) {
|
||||
app.get('/plugins/fireHook', function(req, res) {
|
||||
// GET = filter
|
||||
Plugins.fireHook('filter:' + req.query.hook, req.query.args, function(err, returnData) {
|
||||
if (typeof returnData === 'object') {
|
||||
res.json(200, returnData);
|
||||
} else {
|
||||
res.send(200, validator.sanitize(returnData).escape());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/plugins/fireHook', function(req, res) {
|
||||
// PUT = action
|
||||
Plugins.fireHook('action:' + req.body.hook, req.body.args);
|
||||
res.send(200);
|
||||
});
|
||||
|
||||
// Static Assets
|
||||
app.get('/plugins/:id/*', function(req, res) {
|
||||
var relPath = req.url.replace('/plugins/' + req.params.id, '');
|
||||
@@ -15,7 +35,7 @@ var nconf = require('nconf'),
|
||||
} else {
|
||||
res.redirect('/404');
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
res.redirect('/404');
|
||||
}
|
||||
|
||||
@@ -12,23 +12,7 @@ var user = require('./../user.js'),
|
||||
websockets = require('./../websockets.js');
|
||||
|
||||
(function (User) {
|
||||
User.create_routes = function (app) {
|
||||
|
||||
app.get('/uid/:uid', function (req, res) {
|
||||
|
||||
if (!req.params.uid)
|
||||
return res.redirect('/404');
|
||||
|
||||
user.getUserData(req.params.uid, function (err, data) {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.json(404, {
|
||||
error: "User doesn't exist!"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
User.createRoutes = function (app) {
|
||||
|
||||
app.namespace('/users', function () {
|
||||
app.get('', function (req, res) {
|
||||
@@ -175,7 +159,7 @@ var user = require('./../user.js'),
|
||||
return;
|
||||
}
|
||||
|
||||
var absolutePath = path.join(process.cwd(), nconf.get('upload_path'), path.basename(oldpicture));
|
||||
var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), path.basename(oldpicture));
|
||||
|
||||
fs.unlink(absolutePath, function (err) {
|
||||
if (err) {
|
||||
@@ -197,7 +181,7 @@ var user = require('./../user.js'),
|
||||
}
|
||||
|
||||
var filename = uid + '-profileimg' + extension;
|
||||
var uploadPath = path.join(process.cwd(), nconf.get('upload_path'), filename);
|
||||
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
|
||||
|
||||
winston.info('Attempting upload to: ' + uploadPath);
|
||||
|
||||
@@ -207,11 +191,6 @@ var user = require('./../user.js'),
|
||||
is.on('end', function () {
|
||||
fs.unlinkSync(tempPath);
|
||||
|
||||
var imageUrl = nconf.get('upload_url') + filename;
|
||||
|
||||
user.setUserField(uid, 'uploadedpicture', imageUrl);
|
||||
user.setUserField(uid, 'picture', imageUrl);
|
||||
|
||||
require('node-imagemagick').crop({
|
||||
srcPath: uploadPath,
|
||||
dstPath: uploadPath,
|
||||
@@ -220,8 +199,17 @@ var user = require('./../user.js'),
|
||||
}, function (err, stdout, stderr) {
|
||||
if (err) {
|
||||
winston.err(err);
|
||||
res.send({
|
||||
error: 'Invalid image file!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var imageUrl = nconf.get('upload_url') + filename;
|
||||
|
||||
user.setUserField(uid, 'uploadedpicture', imageUrl);
|
||||
user.setUserField(uid, 'picture', imageUrl);
|
||||
|
||||
res.json({
|
||||
path: imageUrl
|
||||
});
|
||||
@@ -555,6 +543,8 @@ var user = require('./../user.js'),
|
||||
else
|
||||
data.emailClass = "hide";
|
||||
|
||||
data.websiteName = data.website.replace('http://', '').replace('https://', '');
|
||||
|
||||
data.show_banned = data.banned === '1' ? '' : 'hide';
|
||||
|
||||
data.uid = uid;
|
||||
|
||||
@@ -27,7 +27,7 @@ var path = require('path'),
|
||||
async.parallel([
|
||||
function(next) {
|
||||
var categoryUrls = [];
|
||||
categories.getAllCategories(function(data) {
|
||||
categories.getAllCategories(0, function(err, data) {
|
||||
data.categories.forEach(function(category) {
|
||||
if (!category.disabled) {
|
||||
categoryUrls.push({
|
||||
@@ -43,7 +43,7 @@ var path = require('path'),
|
||||
},
|
||||
function(next) {
|
||||
var topicUrls = [];
|
||||
topics.getAllTopics(null, null, function(topics) {
|
||||
topics.getAllTopics(null, null, function(err, topics) {
|
||||
topics.forEach(function(topic) {
|
||||
if (topic.deleted !== '1') {
|
||||
topicUrls.push({
|
||||
|
||||
@@ -9,7 +9,8 @@ var RDB = require('./redis.js'),
|
||||
topicSearch = reds.createSearch('nodebbtopicsearch'),
|
||||
winston = require('winston'),
|
||||
meta = require('./meta'),
|
||||
nconf = require('nconf');
|
||||
nconf = require('nconf'),
|
||||
websockets = require('./websockets');
|
||||
|
||||
(function(ThreadTools) {
|
||||
|
||||
@@ -47,136 +48,119 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
}
|
||||
|
||||
ThreadTools.lock = function(tid, uid, socket) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
topics.setTopicField(tid, 'locked', 1);
|
||||
ThreadTools.lock = function(tid, socket) {
|
||||
topics.setTopicField(tid, 'locked', 1);
|
||||
|
||||
if (socket) {
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_locked', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
if (socket) {
|
||||
websockets.in('topic_' + tid).emit('event:topic_locked', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
socket.emit('api:topic.lock', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
if (socket) {
|
||||
socket.emit('api:topic.lock', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTools.unlock = function(tid, uid, socket) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
topics.setTopicField(tid, 'locked', 0);
|
||||
ThreadTools.unlock = function(tid, socket) {
|
||||
topics.setTopicField(tid, 'locked', 0);
|
||||
|
||||
if (socket) {
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_unlocked', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
if (socket) {
|
||||
websockets.in('topic_' + tid).emit('event:topic_unlocked', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
socket.emit('api:topic.unlock', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
if (socket) {
|
||||
socket.emit('api:topic.unlock', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTools.delete = function(tid, uid, callback) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable || uid === -1) {
|
||||
ThreadTools.delete = function(tid, callback) {
|
||||
topics.delete(tid);
|
||||
|
||||
topics.delete(tid);
|
||||
RDB.decr('totaltopiccount');
|
||||
|
||||
RDB.decr('totaltopiccount');
|
||||
ThreadTools.lock(tid);
|
||||
|
||||
ThreadTools.lock(tid, uid);
|
||||
topicSearch.remove(tid);
|
||||
|
||||
topicSearch.remove(tid);
|
||||
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_deleted', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
callback(null);
|
||||
} else callback(new Error('not-enough-privs'));
|
||||
websockets.in('topic_' + tid).emit('event:topic_deleted', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTools.restore = function(tid, uid, socket, callback) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
ThreadTools.restore = function(tid, socket, callback) {
|
||||
topics.restore(tid);
|
||||
RDB.incr('totaltopiccount');
|
||||
ThreadTools.unlock(tid);
|
||||
|
||||
topics.restore(tid);
|
||||
RDB.incr('totaltopiccount');
|
||||
ThreadTools.unlock(tid, uid);
|
||||
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_restored', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
topics.getTopicField(tid, 'title', function(err, title) {
|
||||
topicSearch.index(title, tid);
|
||||
});
|
||||
|
||||
if(callback)
|
||||
callback(null);
|
||||
}
|
||||
websockets.in('topic_' + tid).emit('event:topic_restored', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
topics.getTopicField(tid, 'title', function(err, title) {
|
||||
topicSearch.index(title, tid);
|
||||
});
|
||||
|
||||
if(callback) {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTools.pin = function(tid, uid, socket) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
|
||||
topics.setTopicField(tid, 'pinned', 1);
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
RDB.zadd('categories:' + cid + ':tid', Math.pow(2, 53), tid);
|
||||
});
|
||||
|
||||
if (socket) {
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_pinned', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
socket.emit('api:topic.pin', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
}
|
||||
ThreadTools.pin = function(tid, socket) {
|
||||
topics.setTopicField(tid, 'pinned', 1);
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
RDB.zadd('categories:' + cid + ':tid', Math.pow(2, 53), tid);
|
||||
});
|
||||
|
||||
if (socket) {
|
||||
websockets.in('topic_' + tid).emit('event:topic_pinned', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
if (socket) {
|
||||
socket.emit('api:topic.pin', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTools.unpin = function(tid, uid, socket) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
|
||||
topics.setTopicField(tid, 'pinned', 0);
|
||||
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
|
||||
RDB.zadd('categories:' + topicData.cid + ':tid', topicData.lastposttime, tid);
|
||||
});
|
||||
if (socket) {
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_unpinned', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
socket.emit('api:topic.unpin', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
}
|
||||
ThreadTools.unpin = function(tid, socket) {
|
||||
topics.setTopicField(tid, 'pinned', 0);
|
||||
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
|
||||
RDB.zadd('categories:' + topicData.cid + ':tid', topicData.lastposttime, tid);
|
||||
});
|
||||
if (socket) {
|
||||
websockets.in('topic_' + tid).emit('event:topic_unpinned', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
if (socket) {
|
||||
socket.emit('api:topic.unpin', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTools.move = function(tid, cid, socket) {
|
||||
@@ -213,7 +197,7 @@ var RDB = require('./redis.js'),
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
io.sockets. in ('topic_' + tid).emit('event:topic_moved', {
|
||||
websockets.in('topic_' + tid).emit('event:topic_moved', {
|
||||
tid: tid
|
||||
});
|
||||
} else {
|
||||
@@ -303,7 +287,7 @@ var RDB = require('./redis.js'),
|
||||
|
||||
pids.reverse();
|
||||
async.detectSeries(pids, function(pid, next) {
|
||||
posts.getPostField(pid, 'deleted', function(deleted) {
|
||||
posts.getPostField(pid, 'deleted', function(err, deleted) {
|
||||
if (deleted === '0') next(true);
|
||||
else next(false);
|
||||
});
|
||||
|
||||
350
src/topics.js
350
src/topics.js
@@ -6,7 +6,7 @@ var RDB = require('./redis.js'),
|
||||
posts = require('./posts.js'),
|
||||
threadTools = require('./threadTools.js'),
|
||||
postTools = require('./postTools'),
|
||||
Notifications = require('./notifications'),
|
||||
notifications = require('./notifications'),
|
||||
async = require('async'),
|
||||
feed = require('./feed.js'),
|
||||
favourites = require('./favourites.js'),
|
||||
@@ -18,30 +18,134 @@ var RDB = require('./redis.js'),
|
||||
|
||||
(function(Topics) {
|
||||
|
||||
Topics.getTopicData = function(tid, callback) {
|
||||
RDB.hgetall('topic:' + tid, function(err, data) {
|
||||
if (err === null) {
|
||||
if(data) {
|
||||
data.title = validator.sanitize(data.title).escape();
|
||||
if(data.timestamp) {
|
||||
data.relativeTime = new Date(parseInt(data.timestamp, 10)).toISOString();
|
||||
}
|
||||
Topics.post = function(uid, title, content, category_id, callback) {
|
||||
if (!category_id)
|
||||
throw new Error('Attempted to post without a category_id');
|
||||
|
||||
if (content)
|
||||
content = content.trim();
|
||||
if (title)
|
||||
title = title.trim();
|
||||
|
||||
if (!uid) {
|
||||
callback(new Error('not-logged-in'), null);
|
||||
return;
|
||||
} else if (!title || title.length < meta.config.minimumTitleLength) {
|
||||
callback(new Error('title-too-short'), null);
|
||||
return;
|
||||
} else if (!content || content.length < meta.config.miminumPostLength) {
|
||||
callback(new Error('content-too-short'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'lastposttime', function(err, lastposttime) {
|
||||
if (err) lastposttime = 0;
|
||||
if (Date.now() - lastposttime < meta.config.postDelay * 1000) {
|
||||
callback(new Error('too-many-posts'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
RDB.incr('next_topic_id', function(err, tid) {
|
||||
RDB.handle(err);
|
||||
|
||||
// Global Topics
|
||||
if (uid == null) uid = 0;
|
||||
if (uid !== null) {
|
||||
RDB.sadd('topics:tid', tid);
|
||||
} else {
|
||||
// need to add some unique key sent by client so we can update this with the real uid later
|
||||
RDB.lpush('topics:queued:tid', tid);
|
||||
}
|
||||
|
||||
callback(data);
|
||||
} else {
|
||||
console.log(err);
|
||||
var slug = tid + '/' + utils.slugify(title);
|
||||
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,
|
||||
'viewcount': 0,
|
||||
'locked': 0,
|
||||
'deleted': 0,
|
||||
'pinned': 0
|
||||
});
|
||||
|
||||
topicSearch.index(title, tid);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
|
||||
// in future it may be possible to add topics to several categories, so leaving the door open here.
|
||||
RDB.zadd('categories:' + category_id + ':tid', timestamp, tid);
|
||||
RDB.hincrby('category:' + category_id, 'topic_count', 1);
|
||||
RDB.incr('totaltopiccount');
|
||||
|
||||
feed.updateCategory(category_id);
|
||||
|
||||
posts.create(uid, tid, content, function(err, postData) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
} else if(!postData) {
|
||||
return callback(new Error('invalid-post'), null);
|
||||
}
|
||||
|
||||
// Auto-subscribe the post creator to the newly created topic
|
||||
threadTools.toggleFollow(tid, uid);
|
||||
|
||||
Topics.getTopicForCategoryView(tid, uid, function(topicData) {
|
||||
topicData.unreplied = 1;
|
||||
|
||||
callback(null, {
|
||||
topicData: topicData,
|
||||
postData: postData
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Topics.getTopicData = function(tid, callback) {
|
||||
RDB.hgetall('topic:' + tid, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
if(data) {
|
||||
data.title = validator.sanitize(data.title).escape();
|
||||
if(data.timestamp) {
|
||||
data.relativeTime = new Date(parseInt(data.timestamp, 10)).toISOString();
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
||||
Topics.getTopicDataWithUser = function(tid, callback) {
|
||||
Topics.getTopicData(tid, function(topic) {
|
||||
Topics.getTopicData(tid, function(err, topic) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
user.getUserFields(topic.uid, ['username', 'userslug', 'picture'] , function(err, userData) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
topic.username = userData.username;
|
||||
topic.userslug = userData.userslug
|
||||
topic.picture = userData.picture;
|
||||
callback(topic);
|
||||
callback(null, topic);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -85,7 +189,7 @@ var RDB = require('./redis.js'),
|
||||
|
||||
for (var i = 0; i < postData.length; ++i) {
|
||||
postData[i].fav_button_class = fav_data[postData[i].pid] ? 'btn-warning' : '';
|
||||
postData[i].fav_star_class = fav_data[postData[i].pid] ? 'icon-star' : 'icon-star-empty';
|
||||
postData[i].fav_star_class = fav_data[postData[i].pid] ? 'fa-star' : 'fa-star-o';
|
||||
postData[i]['display_moderator_tools'] = ((current_user != 0) && (postData[i].uid == current_user || privileges.editable)) ? 'show' : 'none';
|
||||
|
||||
postData[i].show_banned = postData[i].user_banned === '1' ? 'show' : 'hide';
|
||||
@@ -119,26 +223,24 @@ var RDB = require('./redis.js'),
|
||||
var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
|
||||
|
||||
RDB.zrevrangebyscore(args, function(err, tids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var latestTopics = {
|
||||
'category_name': 'Recent',
|
||||
'show_sidebar': 'hidden',
|
||||
'show_topic_button': 'hidden',
|
||||
'no_topics_message': 'hidden',
|
||||
'topic_row_size': 'col-md-12',
|
||||
'category_id': false,
|
||||
'topics': []
|
||||
};
|
||||
|
||||
if (!tids || !tids.length) {
|
||||
latestTopics.no_topics_message = 'show';
|
||||
callback(latestTopics);
|
||||
callback(err, latestTopics);
|
||||
return;
|
||||
}
|
||||
|
||||
Topics.getTopicsByTids(tids, current_user, function(topicData) {
|
||||
latestTopics.topics = topicData;
|
||||
callback(latestTopics);
|
||||
callback(err, latestTopics);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -331,17 +433,18 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
function loadTopic(tid, callback) {
|
||||
Topics.getTopicData(tid, function(topicData) {
|
||||
Topics.getTopicData(tid, function(err, topicData) {
|
||||
if (!topicData) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
getTopicInfo(topicData, function(topicInfo) {
|
||||
|
||||
topicData['pin-icon'] = topicData.pinned === '1' ? 'icon-pushpin' : 'none';
|
||||
topicData['lock-icon'] = topicData.locked === '1' ? 'icon-lock' : 'none';
|
||||
topicData['pin-icon'] = topicData.pinned === '1' ? 'fa-thumb-tack' : 'none';
|
||||
topicData['lock-icon'] = topicData.locked === '1' ? 'fa-lock' : 'none';
|
||||
topicData['deleted-class'] = topicData.deleted === '1' ? 'deleted' : '';
|
||||
|
||||
topicData.unreplied = topicData.postcount === '1';
|
||||
topicData.username = topicInfo.username;
|
||||
topicData.userslug = topicInfo.userslug;
|
||||
topicData.picture = topicInfo.picture;
|
||||
@@ -381,9 +484,7 @@ var RDB = require('./redis.js'),
|
||||
Topics.increaseViewCount(tid);
|
||||
|
||||
function getTopicData(next) {
|
||||
Topics.getTopicData(tid, function(topicData) {
|
||||
next(null, topicData);
|
||||
});
|
||||
Topics.getTopicData(tid, next);
|
||||
}
|
||||
|
||||
function getTopicPosts(next) {
|
||||
@@ -414,8 +515,6 @@ var RDB = require('./redis.js'),
|
||||
privileges = results[2],
|
||||
categoryData = results[3];
|
||||
|
||||
var main_posts = topicPosts.splice(0, 1);
|
||||
|
||||
callback(null, {
|
||||
'topic_name': topicData.title,
|
||||
'category_name': categoryData.name,
|
||||
@@ -426,10 +525,10 @@ var RDB = require('./redis.js'),
|
||||
'slug': topicData.slug,
|
||||
'postcount': topicData.postcount,
|
||||
'viewcount': topicData.viewcount,
|
||||
'unreplied': topicData.postcount > 1,
|
||||
'topic_id': tid,
|
||||
'expose_tools': privileges.editable ? 1 : 0,
|
||||
'posts': topicPosts,
|
||||
'main_posts': main_posts,
|
||||
'twitter-intent-url': 'https://twitter.com/intent/tweet?url=' + encodeURIComponent(nconf.get('url') + 'topic/' + topicData.slug) + '&text=' + encodeURIComponent(topicData.title),
|
||||
'facebook-share-url': 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(nconf.get('url') + 'topic/' + topicData.slug),
|
||||
'google-share-url': 'https://plus.google.com/share?url=' + encodeURIComponent(nconf.get('url') + 'topic/' + topicData.slug)
|
||||
@@ -442,9 +541,7 @@ var RDB = require('./redis.js'),
|
||||
Topics.getTopicForCategoryView = function(tid, uid, callback) {
|
||||
|
||||
function getTopicData(next) {
|
||||
Topics.getTopicDataWithUser(tid, function(topic) {
|
||||
next(null, topic);
|
||||
});
|
||||
Topics.getTopicDataWithUser(tid, next);
|
||||
}
|
||||
|
||||
function getReadStatus(next) {
|
||||
@@ -473,6 +570,9 @@ var RDB = require('./redis.js'),
|
||||
hasRead = results[1],
|
||||
teaser = results[2];
|
||||
|
||||
topicData['pin-icon'] = topicData.pinned === '1' ? 'fa-thumb-tack' : 'none';
|
||||
topicData['lock-icon'] = topicData.locked === '1' ? 'fa-lock' : 'none';
|
||||
|
||||
topicData.badgeclass = hasRead ? '' : 'badge-important';
|
||||
topicData.teaser_text = teaser.text || '';
|
||||
topicData.teaser_username = teaser.username || '';
|
||||
@@ -487,6 +587,10 @@ var RDB = require('./redis.js'),
|
||||
|
||||
Topics.getAllTopics = function(limit, after, callback) {
|
||||
RDB.smembers('topics:tid', function(err, tids) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
var topics = [],
|
||||
numTids, x;
|
||||
|
||||
@@ -517,12 +621,12 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
|
||||
async.each(tids, function(tid, next) {
|
||||
Topics.getTopicDataWithUser(tid, function(topicData) {
|
||||
Topics.getTopicDataWithUser(tid, function(err, topicData) {
|
||||
topics.push(topicData);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
callback(topics);
|
||||
callback(err, topics);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -546,15 +650,15 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
Topics.getTitleByPid = function(pid, callback) {
|
||||
posts.getPostField(pid, 'tid', function(tid) {
|
||||
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||
Topics.getTopicField(tid, 'title', function(err, title) {
|
||||
callback(title);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Topics.markUnRead = function(tid) {
|
||||
RDB.del('tid:' + tid + ':read_by_uid');
|
||||
Topics.markUnRead = function(tid, callback) {
|
||||
RDB.del('tid:' + tid + ':read_by_uid', callback);
|
||||
}
|
||||
|
||||
Topics.markAsRead = function(tid, uid) {
|
||||
@@ -571,11 +675,9 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
|
||||
user.notifications.getUnreadByUniqueId(uid, 'topic:' + tid, function(err, nids) {
|
||||
if (nids.length > 0) {
|
||||
async.each(nids, function(nid, next) {
|
||||
Notifications.mark_read(nid, uid, next);
|
||||
});
|
||||
}
|
||||
notifications.mark_read_multiple(nids, uid, function() {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -620,36 +722,44 @@ var RDB = require('./redis.js'),
|
||||
|
||||
Topics.getTeaser = function(tid, callback) {
|
||||
threadTools.getLatestUndeletedPid(tid, function(err, pid) {
|
||||
if (!err) {
|
||||
posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(postData) {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
||||
if (err)
|
||||
return callback(err, null);
|
||||
posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(err, postData) {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
} else if(!postData) {
|
||||
return callback(new Error('no-teaser-found'));
|
||||
}
|
||||
|
||||
var stripped = postData.content,
|
||||
timestamp = postData.timestamp,
|
||||
returnObj = {
|
||||
"pid": postData.pid,
|
||||
"username": userData.username,
|
||||
"userslug": userData.userslug,
|
||||
"picture": userData.picture,
|
||||
"timestamp": timestamp
|
||||
};
|
||||
user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
if (postData.content) {
|
||||
stripped = postData.content.replace(/>.+\n\n/, '');
|
||||
postTools.parse(stripped, function(err, stripped) {
|
||||
returnObj.text = utils.strip_tags(stripped);
|
||||
callback(null, returnObj);
|
||||
});
|
||||
} else {
|
||||
returnObj.text = '';
|
||||
var stripped = postData.content,
|
||||
timestamp = postData.timestamp,
|
||||
returnObj = {
|
||||
"pid": postData.pid,
|
||||
"username": userData.username,
|
||||
"userslug": userData.userslug,
|
||||
"picture": userData.picture,
|
||||
"timestamp": timestamp
|
||||
};
|
||||
|
||||
if (postData.content) {
|
||||
stripped = postData.content.replace(/>.+\n\n/, '');
|
||||
postTools.parse(stripped, function(err, stripped) {
|
||||
returnObj.text = utils.strip_tags(stripped);
|
||||
callback(null, returnObj);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
returnObj.text = '';
|
||||
callback(null, returnObj);
|
||||
}
|
||||
});
|
||||
} else callback(new Error('no-teaser-found'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -663,101 +773,6 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
}
|
||||
|
||||
Topics.post = function(uid, title, content, category_id, callback) {
|
||||
if (!category_id)
|
||||
throw new Error('Attempted to post without a category_id');
|
||||
|
||||
if (content)
|
||||
content = content.trim();
|
||||
if (title)
|
||||
title = title.trim();
|
||||
|
||||
if (uid === 0) {
|
||||
callback(new Error('not-logged-in'), null);
|
||||
return;
|
||||
} else if (!title || title.length < meta.config.minimumTitleLength) {
|
||||
callback(new Error('title-too-short'), null);
|
||||
return;
|
||||
} else if (!content || content.length < meta.config.miminumPostLength) {
|
||||
callback(new Error('content-too-short'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'lastposttime', function(err, lastposttime) {
|
||||
if (err) lastposttime = 0;
|
||||
if (Date.now() - lastposttime < meta.config.postDelay) {
|
||||
callback(new Error('too-many-posts'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
RDB.incr('next_topic_id', function(err, tid) {
|
||||
RDB.handle(err);
|
||||
|
||||
// Global Topics
|
||||
if (uid == null) uid = 0;
|
||||
if (uid !== null) {
|
||||
RDB.sadd('topics:tid', tid);
|
||||
} else {
|
||||
// need to add some unique key sent by client so we can update this with the real uid later
|
||||
RDB.lpush('topics:queued:tid', tid);
|
||||
}
|
||||
|
||||
var slug = tid + '/' + utils.slugify(title);
|
||||
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,
|
||||
'viewcount': 0,
|
||||
'locked': 0,
|
||||
'deleted': 0,
|
||||
'pinned': 0
|
||||
});
|
||||
|
||||
topicSearch.index(title, tid);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
|
||||
// in future it may be possible to add topics to several categories, so leaving the door open here.
|
||||
RDB.zadd('categories:' + category_id + ':tid', timestamp, tid);
|
||||
RDB.hincrby('category:' + category_id, 'topic_count', 1);
|
||||
RDB.incr('totaltopiccount');
|
||||
|
||||
feed.updateCategory(category_id);
|
||||
|
||||
posts.create(uid, tid, content, function(postData) {
|
||||
if (postData) {
|
||||
|
||||
// Auto-subscribe the post creator to the newly created topic
|
||||
threadTools.toggleFollow(tid, uid);
|
||||
|
||||
// Notify any users looking at the category that a new topic has arrived
|
||||
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);
|
||||
io.sockets.in('user/' + uid).emit('event:new_post', {
|
||||
posts: postData
|
||||
});
|
||||
});
|
||||
|
||||
callback(null, postData);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Topics.getTopicField = function(tid, field, callback) {
|
||||
RDB.hget('topic:' + tid, field, callback);
|
||||
}
|
||||
@@ -780,7 +795,10 @@ var RDB = require('./redis.js'),
|
||||
|
||||
Topics.isLocked = function(tid, callback) {
|
||||
Topics.getTopicField(tid, 'locked', function(err, locked) {
|
||||
callback(locked);
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
callback(null, locked === "1");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -802,7 +820,7 @@ var RDB = require('./redis.js'),
|
||||
Topics.getPids(tid, function(err, pids) {
|
||||
|
||||
function getUid(pid, next) {
|
||||
posts.getPostField(pid, 'uid', function(uid) {
|
||||
posts.getPostField(pid, 'uid', function(err, uid) {
|
||||
if (err)
|
||||
return next(err);
|
||||
uids[uid] = 1;
|
||||
|
||||
283
src/upgrade.js
283
src/upgrade.js
@@ -1,94 +1,235 @@
|
||||
"use strict";
|
||||
|
||||
var RDB = require('./redis.js'),
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
notifications = require('./notifications')
|
||||
Upgrade = {};
|
||||
notifications = require('./notifications'),
|
||||
categories = require('./categories'),
|
||||
Upgrade = {},
|
||||
|
||||
Upgrade.upgrade = function() {
|
||||
schemaDate, thisSchemaDate;
|
||||
|
||||
Upgrade.check = function(callback) {
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
|
||||
var latestSchema = new Date(2013, 10, 26).getTime();
|
||||
|
||||
RDB.get('schemaDate', function(err, value) {
|
||||
if (parseInt(value, 10) >= latestSchema) {
|
||||
callback(true);
|
||||
} else {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Upgrade.upgrade = function(callback) {
|
||||
winston.info('Beginning Redis database schema update');
|
||||
|
||||
async.series([
|
||||
function(next) {
|
||||
RDB.hget('notifications:1', 'score', function(err, score) {
|
||||
if (score) {
|
||||
async.series([
|
||||
function(next) {
|
||||
RDB.keys('uid:*:notifications:flag', function(err, keys) {
|
||||
if (keys.length > 0) {
|
||||
winston.info('[2013/10/03] Removing deprecated Notification Flags');
|
||||
async.each(keys, function(key, next) {
|
||||
RDB.del(key, next);
|
||||
}, next);
|
||||
} else {
|
||||
winston.info('[2013/10/03] No Notification Flags found. Good.');
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
winston.info('[2013/10/03] Updating Notifications');
|
||||
RDB.keys('uid:*:notifications:*', function(err, keys) {
|
||||
async.each(keys, function(key, next) {
|
||||
RDB.zrange(key, 0, -1, function(err, nids) {
|
||||
async.each(nids, function(nid, next) {
|
||||
notifications.get(nid, null, function(notif_data) {
|
||||
RDB.zadd(key, notif_data.datetime, nid, next);
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
RDB.keys('notifications:*', function(err, keys) {
|
||||
if (keys.length > 0) {
|
||||
winston.info('[2013/10/03] Removing Notification Scores');
|
||||
async.each(keys, function(key, next) {
|
||||
if (key === 'notifications:next_nid') return next();
|
||||
RDB.hdel(key, 'score', next);
|
||||
}, next);
|
||||
} else {
|
||||
winston.info('[2013/10/03] No Notification Scores found. Good.');
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
], next);
|
||||
} else {
|
||||
winston.info('[2013/10/03] Updates to Notifications skipped.');
|
||||
next();
|
||||
}
|
||||
RDB.get('schemaDate', function(err, value) {
|
||||
schemaDate = value;
|
||||
next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
RDB.exists('notifications', function(err, exists) {
|
||||
if (!exists) {
|
||||
RDB.keys('notifications:*', function(err, keys) {
|
||||
var multi = RDB.multi();
|
||||
|
||||
keys = keys.filter(function(key) {
|
||||
if (key === 'notifications:next_nid') return false;
|
||||
else return true;
|
||||
}).map(function(key) {
|
||||
return key.slice(14);
|
||||
thisSchemaDate = new Date(2013, 9, 3).getTime();
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
async.series([
|
||||
function(next) {
|
||||
RDB.keys('uid:*:notifications:flag', function(err, keys) {
|
||||
if (keys.length > 0) {
|
||||
winston.info('[2013/10/03] Removing deprecated Notification Flags');
|
||||
async.each(keys, function(key, next) {
|
||||
RDB.del(key, next);
|
||||
}, next);
|
||||
} else {
|
||||
winston.info('[2013/10/03] No Notification Flags found. Good.');
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
winston.info('[2013/10/03] Updating Notifications');
|
||||
RDB.keys('uid:*:notifications:*', function(err, keys) {
|
||||
async.each(keys, function(key, next) {
|
||||
RDB.zrange(key, 0, -1, function(err, nids) {
|
||||
async.each(nids, function(nid, next) {
|
||||
notifications.get(nid, null, function(notif_data) {
|
||||
if (notif_data) {
|
||||
RDB.zadd(key, notif_data.datetime, nid, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
RDB.keys('notifications:*', function(err, keys) {
|
||||
if (keys.length > 0) {
|
||||
winston.info('[2013/10/03] Removing Notification Scores');
|
||||
async.each(keys, function(key, next) {
|
||||
if (key === 'notifications:next_nid') {
|
||||
return next();
|
||||
}
|
||||
|
||||
winston.info('[2013/10/23] Adding existing notifications to set');
|
||||
RDB.sadd('notifications', keys, next);
|
||||
RDB.hdel(key, 'score', next);
|
||||
}, next);
|
||||
} else {
|
||||
winston.info('[2013/10/03] No Notification Scores found. Good.');
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
], next);
|
||||
} else {
|
||||
winston.info('[2013/10/03] Updates to Notifications skipped.');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2013, 9, 23).getTime();
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
RDB.keys('notifications:*', function(err, keys) {
|
||||
|
||||
keys = keys.filter(function(key) {
|
||||
if (key === 'notifications:next_nid') {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).map(function(key) {
|
||||
return key.slice(14);
|
||||
});
|
||||
} else {
|
||||
winston.info('[2013/10/23] Updates to Notifications skipped.');
|
||||
|
||||
winston.info('[2013/10/23] Adding existing notifications to set');
|
||||
|
||||
if(keys && Array.isArray(keys)) {
|
||||
async.each(keys, function(key, cb) {
|
||||
RDB.sadd('notifications', key, cb);
|
||||
}, next);
|
||||
} else next();
|
||||
|
||||
});
|
||||
} else {
|
||||
winston.info('[2013/10/23] Updates to Notifications skipped.');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2013, 10, 11).getTime();
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
RDB.hset('config', 'postDelay', 10, function(err, success) {
|
||||
winston.info('[2013/11/11] Updated postDelay to 10 seconds.');
|
||||
next();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
winston.info('[2013/11/11] Update to postDelay skipped.');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2013, 10, 22).getTime();
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
RDB.keys('category:*', function(err, categories) {
|
||||
async.each(categories, function(categoryStr, next) {
|
||||
var hex;
|
||||
RDB.hgetall(categoryStr, function(err, categoryObj) {
|
||||
switch(categoryObj.blockclass) {
|
||||
case 'category-purple':
|
||||
hex = '#ab1290';
|
||||
break;
|
||||
|
||||
case 'category-darkblue':
|
||||
hex = '#004c66';
|
||||
break;
|
||||
|
||||
case 'category-blue':
|
||||
hex = '#0059b2';
|
||||
break;
|
||||
|
||||
case 'category-darkgreen':
|
||||
hex = '#004000';
|
||||
break;
|
||||
|
||||
case 'category-orange':
|
||||
hex = '#ff7a4d';
|
||||
break;
|
||||
|
||||
default:
|
||||
hex = '#0059b2';
|
||||
break;
|
||||
}
|
||||
|
||||
RDB.hset(categoryStr, 'bgColor', hex, next);
|
||||
RDB.hdel(categoryStr, 'blockclass');
|
||||
});
|
||||
}, function() {
|
||||
winston.info('[2013/11/22] Updated Category colours.');
|
||||
next();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
winston.info('[2013/11/22] Update to Category colours skipped.');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2013, 10, 26).getTime();
|
||||
if (schemaDate < thisSchemaDate || 1) {
|
||||
categories.getAllCategories(0, function(err, categories) {
|
||||
|
||||
function updateIcon(category, next) {
|
||||
var icon = '';
|
||||
if(category.icon === 'icon-lightbulb') {
|
||||
icon = 'fa-lightbulb-o';
|
||||
} else if(category.icon === 'icon-plus-sign') {
|
||||
icon = 'fa-plus';
|
||||
} else if(category.icon === 'icon-screenshot') {
|
||||
icon = 'fa-crosshairs';
|
||||
} else {
|
||||
icon = category.icon.replace('icon-', 'fa-');
|
||||
}
|
||||
|
||||
RDB.hset('category:' + category.cid, 'icon', icon, next);
|
||||
}
|
||||
|
||||
async.each(categories.categories, updateIcon, function(err) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
winston.info('[2013/11/26] Updated Category icons.');
|
||||
next();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
winston.info('[2013/11/26] Update to Category icons skipped.');
|
||||
next();
|
||||
}
|
||||
}
|
||||
// Add new schema updates here
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 12!!!
|
||||
], function(err) {
|
||||
if (!err) {
|
||||
winston.info('Redis schema update complete!');
|
||||
process.exit();
|
||||
RDB.set('schemaDate', thisSchemaDate, function(err) {
|
||||
if (!err) {
|
||||
winston.info('[upgrade] Redis schema update complete!');
|
||||
if (callback) {
|
||||
callback(err);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
} else {
|
||||
winston.error('[upgrade] Could not update NodeBB schema date!');
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
winston.error('Errors were encountered while updating the NodeBB schema: ' + err.message);
|
||||
winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message);
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
67
src/user.js
67
src/user.js
@@ -1,16 +1,20 @@
|
||||
var utils = require('./../public/src/utils.js'),
|
||||
RDB = require('./redis.js'),
|
||||
emailjs = require('emailjs'),
|
||||
meta = require('./meta.js'),
|
||||
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
|
||||
bcrypt = require('bcrypt'),
|
||||
Groups = require('./groups'),
|
||||
notifications = require('./notifications.js'),
|
||||
topics = require('./topics.js'),
|
||||
var bcrypt = require('bcrypt'),
|
||||
async = require('async'),
|
||||
emailjs = require('emailjs'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
userSearch = require('reds').createSearch('nodebbusersearch'),
|
||||
winston = require('winston');
|
||||
check = require('validator').check,
|
||||
sanitize = require('validator').sanitize,
|
||||
|
||||
utils = require('./../public/src/utils'),
|
||||
RDB = require('./redis'),
|
||||
meta = require('./meta'),
|
||||
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
|
||||
Groups = require('./groups'),
|
||||
notifications = require('./notifications'),
|
||||
topics = require('./topics');
|
||||
|
||||
|
||||
(function(User) {
|
||||
'use strict';
|
||||
@@ -98,15 +102,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
User.sendConfirmationEmail(email);
|
||||
}
|
||||
|
||||
RDB.incr('usercount', function(err, count) {
|
||||
RDB.handle(err);
|
||||
|
||||
if (typeof io !== 'undefined') {
|
||||
io.sockets.emit('user.count', {
|
||||
count: count
|
||||
});
|
||||
}
|
||||
});
|
||||
RDB.incr('usercount');
|
||||
|
||||
RDB.zadd('users:joindate', timestamp, uid);
|
||||
RDB.zadd('users:postcount', 0, uid);
|
||||
@@ -114,13 +110,6 @@ var utils = require('./../public/src/utils.js'),
|
||||
|
||||
userSearch.index(username, uid);
|
||||
|
||||
if (typeof io !== 'undefined') {
|
||||
io.sockets.emit('user.latest', {
|
||||
userslug: userslug,
|
||||
username: username
|
||||
});
|
||||
}
|
||||
|
||||
if (password !== undefined) {
|
||||
User.hashPassword(password, function(err, hash) {
|
||||
User.setUserField(uid, 'password', hash);
|
||||
@@ -250,6 +239,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
|
||||
function updateField(field, next) {
|
||||
if (data[field] !== undefined && typeof data[field] === 'string') {
|
||||
data[field] = data[field].trim();
|
||||
data[field] = sanitize(data[field]).escape();
|
||||
|
||||
if (field === 'email') {
|
||||
var gravatarpicture = User.createGravatarURLFromEmail(data[field]);
|
||||
User.setUserField(uid, 'gravatarpicture', gravatarpicture);
|
||||
@@ -271,6 +263,10 @@ var utils = require('./../public/src/utils.js'),
|
||||
return;
|
||||
} else if (field === 'signature') {
|
||||
data[field] = utils.strip_tags(data[field]);
|
||||
} else if (field === 'website') {
|
||||
if(data[field].substr(0, 7) !== 'http://' && data[field].substr(0, 8) !== 'https://') {
|
||||
data[field] = 'http://' + data[field];
|
||||
}
|
||||
}
|
||||
|
||||
User.setUserField(uid, field, data[field]);
|
||||
@@ -642,21 +638,6 @@ var utils = require('./../public/src/utils.js'),
|
||||
});
|
||||
};
|
||||
|
||||
User.latest = function(socket) {
|
||||
RDB.zrevrange('users:joindate', 0, 0, function(err, uid) {
|
||||
RDB.handle(err);
|
||||
|
||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||
if (!err && userData) {
|
||||
socket.emit('user.latest', {
|
||||
userslug: userData.userslug,
|
||||
username: userData.username
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
User.getUidByUsername = function(username, callback) {
|
||||
RDB.hget('username:uid', username, callback);
|
||||
};
|
||||
@@ -1009,7 +990,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
next(null, notif_data);
|
||||
});
|
||||
}, function(err, notifs) {
|
||||
notifs = notifs.sort(function(a, b) {
|
||||
notifs = notifs.filter(function(notif) {
|
||||
return notif !== null;
|
||||
}).sort(function(a, b) {
|
||||
return parseInt(b.datetime, 10) - parseInt(a.datetime, 10);
|
||||
}).map(function(notif) {
|
||||
notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString();
|
||||
|
||||
338
src/webserver.js
338
src/webserver.js
@@ -1,45 +1,55 @@
|
||||
var express = require('express'),
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
|
||||
express = require('express'),
|
||||
express_namespace = require('express-namespace'),
|
||||
WebServer = express(),
|
||||
server = require('http').createServer(WebServer),
|
||||
RedisStore = require('connect-redis')(express),
|
||||
path = require('path'),
|
||||
RDB = require('./redis'),
|
||||
utils = require('../public/src/utils.js'),
|
||||
pkg = require('../package.json'),
|
||||
fs = require('fs'),
|
||||
|
||||
user = require('./user.js'),
|
||||
categories = require('./categories.js'),
|
||||
posts = require('./posts.js'),
|
||||
topics = require('./topics.js'),
|
||||
notifications = require('./notifications.js'),
|
||||
admin = require('./routes/admin.js'),
|
||||
userRoute = require('./routes/user.js'),
|
||||
apiRoute = require('./routes/api.js'),
|
||||
auth = require('./routes/authentication.js'),
|
||||
meta = require('./meta.js'),
|
||||
feed = require('./feed'),
|
||||
plugins = require('./plugins'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
validator = require('validator'),
|
||||
async = require('async'),
|
||||
logger = require('./logger.js');
|
||||
|
||||
pkg = require('../package.json'),
|
||||
|
||||
utils = require('../public/src/utils'),
|
||||
RDB = require('./redis'),
|
||||
user = require('./user'),
|
||||
categories = require('./categories'),
|
||||
posts = require('./posts'),
|
||||
topics = require('./topics'),
|
||||
notifications = require('./notifications'),
|
||||
admin = require('./routes/admin'),
|
||||
userRoute = require('./routes/user'),
|
||||
apiRoute = require('./routes/api'),
|
||||
auth = require('./routes/authentication'),
|
||||
meta = require('./meta'),
|
||||
feed = require('./feed'),
|
||||
plugins = require('./plugins'),
|
||||
logger = require('./logger');
|
||||
|
||||
(function (app) {
|
||||
"use strict";
|
||||
|
||||
var templates = null,
|
||||
clientScripts;
|
||||
|
||||
// Minify client-side libraries
|
||||
meta.js.get(function (err, scripts) {
|
||||
clientScripts = scripts.map(function (script) {
|
||||
return script = {
|
||||
script: script
|
||||
}
|
||||
|
||||
plugins.ready(function() {
|
||||
// Minify client-side libraries
|
||||
meta.js.get(function (err, scripts) {
|
||||
clientScripts = scripts.map(function (script) {
|
||||
script = {
|
||||
script: script
|
||||
};
|
||||
|
||||
return script;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
server.app = app;
|
||||
|
||||
/**
|
||||
@@ -66,13 +76,17 @@ var express = require('express'),
|
||||
content: meta.config.title || 'NodeBB'
|
||||
}, {
|
||||
property: 'keywords',
|
||||
content: meta.config['keywords'] || ''
|
||||
content: meta.config.keywords || ''
|
||||
}],
|
||||
defaultLinkTags = [{
|
||||
rel: 'apple-touch-icon',
|
||||
href: meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'
|
||||
}],
|
||||
metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])),
|
||||
linkTags = utils.buildLinkTags(options.linkTags || []),
|
||||
linkTags = utils.buildLinkTags(defaultLinkTags.concat(options.linkTags || [])),
|
||||
templateValues = {
|
||||
cssSrc: meta.config['theme:src'] || nconf.get('relative_path') + '/vendor/bootstrap/css/bootstrap.min.css',
|
||||
pluginCSS: plugins.cssFiles.map(function(file) { return { path: file } }),
|
||||
pluginCSS: plugins.cssFiles.map(function(file) { return { path: file }; }),
|
||||
title: meta.config.title || '',
|
||||
description: meta.config.description || '',
|
||||
'brand:logo': meta.config['brand:logo'] || '',
|
||||
@@ -88,8 +102,9 @@ var express = require('express'),
|
||||
|
||||
var uid = '0';
|
||||
|
||||
if(options.req.user && options.req.user.uid)
|
||||
if(options.req.user && options.req.user.uid) {
|
||||
uid = options.req.user.uid;
|
||||
}
|
||||
|
||||
user.isAdministrator(uid, function(isAdmin) {
|
||||
templateValues.adminDisplay = isAdmin ? 'show' : 'hide';
|
||||
@@ -97,7 +112,7 @@ var express = require('express'),
|
||||
translator.translate(templates.header.parse(templateValues), function(template) {
|
||||
callback(null, template);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -139,7 +154,7 @@ var express = require('express'),
|
||||
res.locals.csrf_token = req.session._csrf;
|
||||
|
||||
// Disable framing
|
||||
res.setHeader("X-Frame-Options", "DENY");
|
||||
res.setHeader('X-Frame-Options', 'DENY');
|
||||
|
||||
next();
|
||||
});
|
||||
@@ -165,14 +180,18 @@ var express = require('express'),
|
||||
|
||||
// Theme's static directory
|
||||
if (themeData[2]) {
|
||||
app.use('/css/assets', express.static(path.join(__dirname, '../node_modules', themeData[1], themeData[2])));
|
||||
app.use('/css/assets', express.static(path.join(__dirname, '../node_modules', themeData[1], themeData[2]), {
|
||||
maxAge: 5184000000
|
||||
}));
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('Static directory routed for theme: ' + themeData[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (themeData[3]) {
|
||||
app.use('/templates', express.static(path.join(__dirname, '../node_modules', themeData[1], themeData[3])));
|
||||
app.use('/templates', express.static(path.join(__dirname, '../node_modules', themeData[1], themeData[3]), {
|
||||
maxAge: 5184000000
|
||||
}));
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('Custom templates directory routed for theme: ' + themeData[1]);
|
||||
}
|
||||
@@ -232,7 +251,9 @@ var express = require('express'),
|
||||
app.use(app.router);
|
||||
|
||||
// Static directory /public
|
||||
app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public')));
|
||||
app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public'), {
|
||||
maxAge: 5184000000
|
||||
}));
|
||||
|
||||
// 404 catch-all
|
||||
app.use(function (req, res, next) {
|
||||
@@ -249,11 +270,17 @@ var express = require('express'),
|
||||
res.json(200, {});
|
||||
} else if (req.accepts('html')) {
|
||||
// respond with html page
|
||||
if (process.env.NODE_ENV === 'development') winston.warn('Route requested but not found: ' + req.url);
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.warn('Route requested but not found: ' + req.url);
|
||||
}
|
||||
|
||||
res.redirect(nconf.get('relative_path') + '/404');
|
||||
} else if (req.accepts('json')) {
|
||||
// respond with json
|
||||
if (process.env.NODE_ENV === 'development') winston.warn('Route requested but not found: ' + req.url);
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.warn('Route requested but not found: ' + req.url);
|
||||
}
|
||||
|
||||
res.json({
|
||||
error: 'Not found'
|
||||
});
|
||||
@@ -284,7 +311,9 @@ var express = require('express'),
|
||||
winston.error('Errors were encountered while attempting to initialise NodeBB.');
|
||||
process.exit();
|
||||
} else {
|
||||
if (process.env.NODE_ENV === 'development') winston.info('Middlewares loaded.');
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('Middlewares loaded.');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -293,54 +322,62 @@ var express = require('express'),
|
||||
templates = global.templates;
|
||||
|
||||
// translate all static templates served by webserver here. ex. footer, logout
|
||||
translator.translate(templates['footer'].toString(), function(parsedTemplate) {
|
||||
templates['footer'] = parsedTemplate;
|
||||
plugins.fireHook('filter:footer.build', '', function(err, appendHTML) {
|
||||
var footer = templates.footer.parse({
|
||||
footerHTML: appendHTML
|
||||
});
|
||||
|
||||
translator.translate(footer, function(parsedTemplate) {
|
||||
templates.footer = parsedTemplate;
|
||||
});
|
||||
});
|
||||
translator.translate(templates['logout'].toString(), function(parsedTemplate) {
|
||||
templates['logout'] = parsedTemplate;
|
||||
|
||||
plugins.fireHook('action:app.load');
|
||||
|
||||
translator.translate(templates.logout.toString(), function(parsedTemplate) {
|
||||
templates.logout = parsedTemplate;
|
||||
});
|
||||
|
||||
winston.info('NodeBB Ready');
|
||||
server.listen(nconf.get('PORT') || nconf.get('port'), nconf.get('bind_address'));
|
||||
}
|
||||
};
|
||||
|
||||
app.create_route = function (url, tpl) { // to remove
|
||||
return '<script>templates.ready(function(){ajaxify.go("' + url + '", null, "' + tpl + '");});</script>';
|
||||
return '<script>templates.ready(function(){ajaxify.go("' + url + '", null, "' + tpl + '", true);});</script>';
|
||||
};
|
||||
|
||||
app.namespace(nconf.get('relative_path'), function () {
|
||||
|
||||
auth.create_routes(app);
|
||||
admin.create_routes(app);
|
||||
userRoute.create_routes(app);
|
||||
apiRoute.create_routes(app);
|
||||
|
||||
auth.createRoutes(app);
|
||||
admin.createRoutes(app);
|
||||
userRoute.createRoutes(app);
|
||||
apiRoute.createRoutes(app);
|
||||
|
||||
// Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section)
|
||||
(function () {
|
||||
var routes = ['login', 'register', 'account', 'recent', 'unread', 'notifications', '403', '404'];
|
||||
var routes = ['login', 'register', 'account', 'recent', '403', '404'],
|
||||
loginRequired = ['unread', 'search', 'notifications'];
|
||||
|
||||
for (var i = 0, ii = routes.length; i < ii; i++) {
|
||||
(function (route) {
|
||||
async.each(routes.concat(loginRequired), function(route, next) {
|
||||
app.get('/' + route, function (req, res) {
|
||||
if ((route === 'login' || route === 'register') && (req.user && req.user.uid > 0)) {
|
||||
|
||||
app.get('/' + route, function (req, res) {
|
||||
if ((route === 'login' || route === 'register') && (req.user && req.user.uid > 0)) {
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
|
||||
res.redirect('/user/' + userslug);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send((isNaN(parseInt(route, 10)) ? 200 : parseInt(route, 10)), header + app.create_route(route) + templates['footer']);
|
||||
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
|
||||
res.redirect('/user/' + userslug);
|
||||
});
|
||||
return;
|
||||
} else if (loginRequired.indexOf(route) !== -1 && !req.user) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send((isNaN(parseInt(route, 10)) ? 200 : parseInt(route, 10)), header + app.create_route(route) + templates.footer);
|
||||
});
|
||||
}(routes[i]));
|
||||
}
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
@@ -366,26 +403,28 @@ var express = require('express'),
|
||||
}, next);
|
||||
},
|
||||
"categories": function (next) {
|
||||
categories.getAllCategories(function (returnData) {
|
||||
categories.getAllCategories(0, function (err, returnData) {
|
||||
returnData.categories = returnData.categories.filter(function (category) {
|
||||
if (category.disabled !== '1') return true;
|
||||
else return false;
|
||||
if (category.disabled !== '1') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
next(null, returnData);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
}, function (err, data) {
|
||||
res.send(
|
||||
data.header +
|
||||
'\n\t<noscript>\n' + templates['noscript/header'] + templates['noscript/home'].parse(data.categories) + '\n\t</noscript>' +
|
||||
app.create_route('') +
|
||||
templates['footer']
|
||||
templates.footer
|
||||
);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/topic/:topic_id/:slug?', function (req, res) {
|
||||
var tid = req.params.topic_id;
|
||||
|
||||
@@ -394,18 +433,26 @@ var express = require('express'),
|
||||
var rssPath = path.join(__dirname, '../', 'feeds/topics', tid + '.rss'),
|
||||
loadFeed = function () {
|
||||
fs.readFile(rssPath, function (err, data) {
|
||||
if (err) res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||
else res.type('xml').set('Content-Length', data.length).send(data);
|
||||
if (err) {
|
||||
res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||
} else {
|
||||
res.type('xml').set('Content-Length', data.length).send(data);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
if (!fs.existsSync(rssPath)) {
|
||||
feed.updateTopic(tid, function (err) {
|
||||
if (err) res.redirect('/404');
|
||||
else loadFeed();
|
||||
if (err) {
|
||||
res.redirect('/404');
|
||||
} else {
|
||||
loadFeed();
|
||||
}
|
||||
});
|
||||
} else loadFeed();
|
||||
} else {
|
||||
loadFeed();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -414,8 +461,9 @@ var express = require('express'),
|
||||
function (next) {
|
||||
topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), 0, -1, function (err, topicData) {
|
||||
if (topicData) {
|
||||
if (topicData.deleted === '1' && topicData.expose_tools === 0)
|
||||
if (topicData.deleted === '1' && topicData.expose_tools === 0) {
|
||||
return next(new Error('Topic deleted'), null);
|
||||
}
|
||||
}
|
||||
|
||||
next(err, topicData);
|
||||
@@ -428,7 +476,9 @@ var express = require('express'),
|
||||
|
||||
for (var x = 0, numPosts = topicData.posts.length; x < numPosts; x++) {
|
||||
timestamp = parseInt(topicData.posts[x].timestamp, 10);
|
||||
if (timestamp > lastMod) lastMod = timestamp;
|
||||
if (timestamp > lastMod) {
|
||||
lastMod = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
app.build_header({
|
||||
@@ -439,7 +489,7 @@ var express = require('express'),
|
||||
content: topicData.topic_name
|
||||
}, {
|
||||
name: "description",
|
||||
content: sanitize(topicData.main_posts[0].content.substr(0, 255)).escape().replace('\n', '')
|
||||
content: sanitize(topicData.posts[0].content.substr(0, 255)).escape().replace('\n', '')
|
||||
}, {
|
||||
property: 'og:title',
|
||||
content: topicData.topic_name + ' | ' + (meta.config.title || 'NodeBB')
|
||||
@@ -451,10 +501,10 @@ var express = require('express'),
|
||||
content: nconf.get('url') + 'topic/' + topicData.slug
|
||||
}, {
|
||||
property: 'og:image',
|
||||
content: topicData.main_posts[0].picture
|
||||
content: topicData.posts[0].picture
|
||||
}, {
|
||||
property: "article:published_time",
|
||||
content: new Date(parseInt(topicData.main_posts[0].timestamp, 10)).toISOString()
|
||||
content: new Date(parseInt(topicData.posts[0].timestamp, 10)).toISOString()
|
||||
}, {
|
||||
property: 'article:modified_time',
|
||||
content: new Date(lastMod).toISOString()
|
||||
@@ -481,14 +531,17 @@ var express = require('express'),
|
||||
});
|
||||
},
|
||||
], function (err, data) {
|
||||
if (err) return res.redirect('404');
|
||||
if (err) {
|
||||
return res.redirect('404');
|
||||
}
|
||||
|
||||
var topic_url = tid + (req.params.slug ? '/' + req.params.slug : '');
|
||||
|
||||
res.send(
|
||||
data.header +
|
||||
'\n\t<noscript>\n' + templates['noscript/header'] + templates['noscript/topic'].parse(data.topics) + '\n\t</noscript>' +
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("topic/' + topic_url + '");});</script>' +
|
||||
templates['footer']
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("topic/' + topic_url + '", undefined, undefined, true);});</script>' +
|
||||
templates.footer
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -501,18 +554,26 @@ var express = require('express'),
|
||||
var rssPath = path.join(__dirname, '../', 'feeds/categories', cid + '.rss'),
|
||||
loadFeed = function () {
|
||||
fs.readFile(rssPath, function (err, data) {
|
||||
if (err) res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||
else res.type('xml').set('Content-Length', data.length).send(data);
|
||||
if (err) {
|
||||
res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||
} else {
|
||||
res.type('xml').set('Content-Length', data.length).send(data);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
if (!fs.existsSync(rssPath)) {
|
||||
feed.updateCategory(cid, function (err) {
|
||||
if (err) res.redirect('/404');
|
||||
else loadFeed();
|
||||
if (err) {
|
||||
res.redirect('/404');
|
||||
} else {
|
||||
loadFeed();
|
||||
}
|
||||
});
|
||||
} else loadFeed();
|
||||
} else {
|
||||
loadFeed();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -522,9 +583,11 @@ var express = require('express'),
|
||||
categories.getCategoryById(cid, 0, function (err, categoryData) {
|
||||
|
||||
if (categoryData) {
|
||||
if (categoryData.disabled === '1')
|
||||
if (categoryData.disabled === '1') {
|
||||
return next(new Error('Category disabled'), null);
|
||||
}
|
||||
}
|
||||
|
||||
next(err, categoryData);
|
||||
});
|
||||
},
|
||||
@@ -561,14 +624,17 @@ var express = require('express'),
|
||||
});
|
||||
}
|
||||
], function (err, data) {
|
||||
if (err) return res.redirect('404');
|
||||
if (err) {
|
||||
return res.redirect('404');
|
||||
}
|
||||
|
||||
var category_url = cid + (req.params.slug ? '/' + req.params.slug : '');
|
||||
|
||||
res.send(
|
||||
data.header +
|
||||
'\n\t<noscript>\n' + templates['noscript/header'] + templates['noscript/category'].parse(data.categories) + '\n\t</noscript>' +
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("category/' + category_url + '");});</script>' +
|
||||
templates['footer']
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("category/' + category_url + '", undefined, undefined, true);});</script>' +
|
||||
templates.footer
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -578,7 +644,7 @@ var express = require('express'),
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send(header + '<script>templates.ready(function(){ajaxify.go("confirm/' + req.params.code + '");});</script>' + templates['footer']);
|
||||
res.send(header + '<script>templates.ready(function(){ajaxify.go("confirm/' + req.params.code + '", undefined, undefined, true);});</script>' + templates.footer);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -597,22 +663,30 @@ var express = require('express'),
|
||||
"Sitemap: " + nconf.get('url') + "sitemap.xml");
|
||||
});
|
||||
|
||||
app.get('/cid/:cid', function (req, res) {
|
||||
categories.getCategoryData(req.params.cid, function (err, data) {
|
||||
if (data)
|
||||
res.send(data);
|
||||
else
|
||||
res.send(404, "Category doesn't exist!");
|
||||
});
|
||||
});
|
||||
app.get('/recent.rss', function(req, res) {
|
||||
var rssPath = path.join(__dirname, '../', 'feeds/recent.rss'),
|
||||
loadFeed = function () {
|
||||
fs.readFile(rssPath, function (err, data) {
|
||||
if (err) {
|
||||
res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||
} else {
|
||||
res.type('xml').set('Content-Length', data.length).send(data);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/tid/:tid', function (req, res) {
|
||||
topics.getTopicData(req.params.tid, function (data) {
|
||||
if (data)
|
||||
res.send(data);
|
||||
else
|
||||
res.send(404, "Topic doesn't exist!");
|
||||
});
|
||||
};
|
||||
|
||||
if (!fs.existsSync(rssPath)) {
|
||||
feed.updateRecent(function (err) {
|
||||
if (err) {
|
||||
res.redirect('/404');
|
||||
} else {
|
||||
loadFeed();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
loadFeed();
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/recent/:term?', function (req, res) {
|
||||
@@ -621,22 +695,15 @@ var express = require('express'),
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send(header + app.create_route("recent/" + req.params.term, null, "recent") + templates['footer']);
|
||||
res.send(header + app.create_route('recent/' + req.params.term, null, 'recent') + templates.footer);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
app.get('/pid/:pid', function (req, res) {
|
||||
posts.getPostData(req.params.pid, function (data) {
|
||||
if (data)
|
||||
res.send(data);
|
||||
else
|
||||
res.send(404, "Post doesn't exist!");
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/outgoing', function (req, res) {
|
||||
if (!req.query.url) return res.redirect('/404');
|
||||
if (!req.query.url) {
|
||||
return res.redirect('/404');
|
||||
}
|
||||
|
||||
app.build_header({
|
||||
req: req,
|
||||
@@ -645,30 +712,21 @@ var express = require('express'),
|
||||
res.send(
|
||||
header +
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("outgoing?url=' + encodeURIComponent(req.query.url) + '", null, null, true);});</script>' +
|
||||
templates['footer']
|
||||
templates.footer
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/search', function (req, res) {
|
||||
if (!req.user)
|
||||
return res.redirect('/403');
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send(header + app.create_route("search", null, "search") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/search/:term', function (req, res) {
|
||||
if (!req.user)
|
||||
if (!req.user) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send(header + app.create_route("search/" + req.params.term, null, "search") + templates['footer']);
|
||||
res.send(header + app.create_route('search/' + req.params.term, null, 'search') + templates.footer);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -713,7 +771,7 @@ var express = require('express'),
|
||||
req: options.req,
|
||||
res: options.res
|
||||
}, function (err, header) {
|
||||
res.send(header + options.content + templates['footer']);
|
||||
res.send(header + options.content + templates.footer);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -728,4 +786,4 @@ var express = require('express'),
|
||||
}(WebServer));
|
||||
|
||||
|
||||
global.server = server;
|
||||
global.server = server;
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
|
||||
var cookie = require('cookie'),
|
||||
var cookie = require('cookie'),
|
||||
express = require('express'),
|
||||
user = require('./user.js'),
|
||||
Groups = require('./groups'),
|
||||
posts = require('./posts.js'),
|
||||
favourites = require('./favourites.js'),
|
||||
utils = require('../public/src/utils.js'),
|
||||
util = require('util'),
|
||||
topics = require('./topics.js'),
|
||||
categories = require('./categories.js'),
|
||||
notifications = require('./notifications.js'),
|
||||
threadTools = require('./threadTools.js'),
|
||||
postTools = require('./postTools.js'),
|
||||
meta = require('./meta.js'),
|
||||
async = require('async'),
|
||||
fs = require('fs'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
|
||||
RedisStoreLib = require('connect-redis')(express),
|
||||
RDB = require('./redis'),
|
||||
util = require('util'),
|
||||
logger = require('./logger.js'),
|
||||
fs = require('fs'),
|
||||
RedisStore = new RedisStoreLib({
|
||||
client: RDB,
|
||||
ttl: 60 * 60 * 24 * 14
|
||||
}),
|
||||
nconf = require('nconf'),
|
||||
|
||||
user = require('./user'),
|
||||
Groups = require('./groups'),
|
||||
posts = require('./posts'),
|
||||
favourites = require('./favourites'),
|
||||
utils = require('../public/src/utils'),
|
||||
topics = require('./topics'),
|
||||
categories = require('./categories'),
|
||||
notifications = require('./notifications'),
|
||||
threadTools = require('./threadTools'),
|
||||
postTools = require('./postTools'),
|
||||
meta = require('./meta'),
|
||||
logger = require('./logger'),
|
||||
socketCookieParser = express.cookieParser(nconf.get('secret')),
|
||||
admin = {
|
||||
'categories': require('./admin/categories.js'),
|
||||
'user': require('./admin/user.js')
|
||||
'categories': require('./admin/categories'),
|
||||
'user': require('./admin/user')
|
||||
},
|
||||
plugins = require('./plugins'),
|
||||
winston = require('winston');
|
||||
plugins = require('./plugins');
|
||||
|
||||
|
||||
var users = {},
|
||||
@@ -43,8 +44,9 @@ module.exports.logoutUser = function(uid) {
|
||||
userSockets[uid][i].emit('event:disconnect');
|
||||
userSockets[uid][i].disconnect();
|
||||
|
||||
if(!userSockets[uid])
|
||||
if(!userSockets[uid]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,8 +58,6 @@ module.exports.isUserOnline = isUserOnline;
|
||||
|
||||
module.exports.init = function(io) {
|
||||
|
||||
global.io = io;
|
||||
|
||||
io.sockets.on('connection', function(socket) {
|
||||
var hs = socket.handshake,
|
||||
sessionID, uid, lastPostTime = 0;
|
||||
@@ -157,7 +157,7 @@ module.exports.init = function(io) {
|
||||
|
||||
for (var i = 0; i < clients.length; ++i) {
|
||||
var hs = clients[i].handshake;
|
||||
if (hs && clients[i].state.user.uid === 0) {
|
||||
if (hs && clients[i].state && clients[i].state.user.uid === 0) {
|
||||
++anonCount;
|
||||
}
|
||||
}
|
||||
@@ -169,11 +169,11 @@ module.exports.init = function(io) {
|
||||
var anonymousCount = getAnonymousCount(roomName);
|
||||
|
||||
if (uids.length === 0) {
|
||||
io.sockets. in (roomName).emit('api:get_users_in_room', { users: [], anonymousCount:0 });
|
||||
io.sockets. in (roomName).emit('api:get_users_in_room', { users: [], anonymousCount: anonymousCount });
|
||||
} else {
|
||||
user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) {
|
||||
if(!err)
|
||||
io.sockets. in (roomName).emit('api:get_users_in_room', { users: users, anonymousCount:anonymousCount });
|
||||
io.sockets. in (roomName).emit('api:get_users_in_room', { users: users, anonymousCount: anonymousCount });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -244,11 +244,7 @@ module.exports.init = function(io) {
|
||||
});
|
||||
|
||||
socket.on('post.stats', function(data) {
|
||||
posts.getTopicPostStats();
|
||||
});
|
||||
|
||||
socket.on('user.latest', function(data) {
|
||||
user.latest(socket, data);
|
||||
emitTopicPostStats();
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
@@ -368,12 +364,25 @@ module.exports.init = function(io) {
|
||||
posts.emitContentTooShortAlert(socket);
|
||||
} else if (err.message === 'too-many-posts') {
|
||||
posts.emitTooManyPostsAlert(socket);
|
||||
} else {
|
||||
socket.emit('event:alert', {
|
||||
title: 'Error',
|
||||
message: err.message,
|
||||
type: 'warning',
|
||||
timeout: 7500
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
posts.getTopicPostStats();
|
||||
io.sockets.in('category_' + data.category_id).emit('event:new_topic', result.topicData);
|
||||
io.sockets.in('recent_posts').emit('event:new_topic', result.topicData);
|
||||
io.sockets.in('user/' + uid).emit('event:new_post', {
|
||||
posts: result.postData
|
||||
});
|
||||
|
||||
emitTopicPostStats();
|
||||
|
||||
socket.emit('event:alert', {
|
||||
title: 'Thank you for posting',
|
||||
@@ -407,12 +416,12 @@ module.exports.init = function(io) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() - lastPostTime < meta.config.postDelay) {
|
||||
if (Date.now() - lastPostTime < meta.config.postDelay * 1000) {
|
||||
posts.emitTooManyPostsAlert(socket);
|
||||
return;
|
||||
}
|
||||
|
||||
posts.reply(data.topic_id, uid, data.content, function(err, result) {
|
||||
posts.reply(data.topic_id, uid, data.content, function(err, postData) {
|
||||
if(err) {
|
||||
|
||||
if(err.message === 'content-too-short') {
|
||||
@@ -430,9 +439,9 @@ module.exports.init = function(io) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (postData) {
|
||||
lastPostTime = Date.now();
|
||||
posts.getTopicPostStats();
|
||||
emitTopicPostStats();
|
||||
|
||||
socket.emit('event:alert', {
|
||||
title: 'Reply Successful',
|
||||
@@ -440,6 +449,12 @@ module.exports.init = function(io) {
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
});
|
||||
var socketData = {
|
||||
posts: [postData]
|
||||
};
|
||||
io.sockets.in('topic_' + postData.tid).emit('event:new_post', socketData);
|
||||
io.sockets.in('recent_posts').emit('event:new_post', socketData);
|
||||
io.sockets.in('user/' + postData.uid).emit('event:new_post', socketData);
|
||||
|
||||
}
|
||||
|
||||
@@ -480,42 +495,66 @@ module.exports.init = function(io) {
|
||||
});
|
||||
|
||||
socket.on('api:topic.delete', function(data) {
|
||||
threadTools.delete(data.tid, uid, function(err) {
|
||||
if (!err) {
|
||||
posts.getTopicPostStats();
|
||||
socket.emit('api:topic.delete', {
|
||||
status: 'ok',
|
||||
tid: data.tid
|
||||
threadTools.privileges(data.tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
threadTools.delete(data.tid, function(err) {
|
||||
if (!err) {
|
||||
emitTopicPostStats();
|
||||
socket.emit('api:topic.delete', {
|
||||
status: 'ok',
|
||||
tid: data.tid
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:topic.restore', function(data) {
|
||||
threadTools.restore(data.tid, uid, socket, function(err) {
|
||||
posts.getTopicPostStats();
|
||||
threadTools.privileges(data.tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
threadTools.restore(data.tid, socket, function(err) {
|
||||
emitTopicPostStats();
|
||||
|
||||
socket.emit('api:topic.restore', {
|
||||
status: 'ok',
|
||||
tid: data.tid
|
||||
});
|
||||
socket.emit('api:topic.restore', {
|
||||
status: 'ok',
|
||||
tid: data.tid
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:topic.lock', function(data) {
|
||||
threadTools.lock(data.tid, uid, socket);
|
||||
threadTools.privileges(data.tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
threadTools.lock(data.tid, socket);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:topic.unlock', function(data) {
|
||||
threadTools.unlock(data.tid, uid, socket);
|
||||
threadTools.privileges(data.tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
threadTools.unlock(data.tid, socket);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:topic.pin', function(data) {
|
||||
threadTools.pin(data.tid, uid, socket);
|
||||
threadTools.privileges(data.tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
threadTools.pin(data.tid, socket);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:topic.unpin', function(data) {
|
||||
threadTools.unpin(data.tid, uid, socket);
|
||||
threadTools.privileges(data.tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
threadTools.unpin(data.tid, socket);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:topic.move', function(data) {
|
||||
@@ -523,7 +562,7 @@ module.exports.init = function(io) {
|
||||
});
|
||||
|
||||
socket.on('api:categories.get', function() {
|
||||
categories.getAllCategories(function(categories) {
|
||||
categories.getAllCategories(0, function(err, categories) {
|
||||
socket.emit('api:categories.get', categories);
|
||||
});
|
||||
});
|
||||
@@ -533,7 +572,7 @@ module.exports.init = function(io) {
|
||||
});
|
||||
|
||||
socket.on('api:posts.getRawPost', function(data) {
|
||||
posts.getPostField(data.pid, 'content', function(raw) {
|
||||
posts.getPostField(data.pid, 'content', function(err, raw) {
|
||||
socket.emit('api:posts.getRawPost', {
|
||||
post: raw
|
||||
});
|
||||
@@ -560,15 +599,34 @@ module.exports.init = function(io) {
|
||||
postTools.edit(uid, data.pid, data.title, data.content, data.images);
|
||||
});
|
||||
|
||||
socket.on('api:posts.delete', function(data) {
|
||||
postTools.delete(uid, data.pid, function() {
|
||||
posts.getTopicPostStats();
|
||||
socket.on('api:posts.delete', function(data, callback) {
|
||||
postTools.delete(uid, data.pid, function(err) {
|
||||
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
emitTopicPostStats();
|
||||
|
||||
io.sockets.in('topic_' + data.tid).emit('event:post_deleted', {
|
||||
pid: data.pid
|
||||
});
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:posts.restore', function(data) {
|
||||
postTools.restore(uid, data.pid, function() {
|
||||
posts.getTopicPostStats();
|
||||
socket.on('api:posts.restore', function(data, callback) {
|
||||
postTools.restore(uid, data.pid, function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
emitTopicPostStats();
|
||||
|
||||
io.sockets.in('topic_' + data.tid).emit('event:post_restored', {
|
||||
pid: data.pid
|
||||
});
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -584,7 +642,9 @@ module.exports.init = function(io) {
|
||||
|
||||
socket.on('api:notifications.mark_all_read', function(data, callback) {
|
||||
notifications.mark_all_read(uid, function(err) {
|
||||
if (!err) callback();
|
||||
if (!err) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -677,7 +737,7 @@ module.exports.init = function(io) {
|
||||
});
|
||||
}
|
||||
|
||||
logger.monitorConfig(this, data);
|
||||
logger.monitorConfig({io: io}, data);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -688,7 +748,7 @@ module.exports.init = function(io) {
|
||||
socket.on('api:composer.push', function(data) {
|
||||
if (uid > 0 || meta.config.allowGuestPosting === '1') {
|
||||
if (parseInt(data.tid) > 0) {
|
||||
topics.getTopicData(data.tid, function(topicData) {
|
||||
topics.getTopicData(data.tid, function(err, topicData) {
|
||||
if (data.body)
|
||||
topicData.body = data.body;
|
||||
|
||||
@@ -714,9 +774,7 @@ module.exports.init = function(io) {
|
||||
|
||||
async.parallel([
|
||||
function(next) {
|
||||
posts.getPostFields(data.pid, ['content'], function(raw) {
|
||||
next(null, raw);
|
||||
});
|
||||
posts.getPostFields(data.pid, ['content'], next);
|
||||
},
|
||||
function(next) {
|
||||
topics.getTitleByPid(data.pid, function(title) {
|
||||
@@ -739,7 +797,7 @@ module.exports.init = function(io) {
|
||||
});
|
||||
|
||||
socket.on('api:composer.editCheck', function(pid) {
|
||||
posts.getPostField(pid, 'tid', function(tid) {
|
||||
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||
postTools.isMain(pid, tid, function(isMain) {
|
||||
socket.emit('api:composer.editCheck', {
|
||||
titleEditable: isMain
|
||||
@@ -800,8 +858,12 @@ module.exports.init = function(io) {
|
||||
var start = data.after,
|
||||
end = start + 9;
|
||||
|
||||
topics.getLatestTopics(uid, start, end, data.term, function(latestTopics) {
|
||||
callback(latestTopics);
|
||||
topics.getLatestTopics(uid, start, end, data.term, function(err, latestTopics) {
|
||||
if (!err) {
|
||||
callback(latestTopics);
|
||||
} else {
|
||||
winston.error('[socket api:topics.loadMoreRecentTopics] ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -830,13 +892,13 @@ module.exports.init = function(io) {
|
||||
});
|
||||
|
||||
socket.on('api:admin.topics.getMore', function(data, callback) {
|
||||
topics.getAllTopics(data.limit, data.after, function(topics) {
|
||||
topics.getAllTopics(data.limit, data.after, function(err, topics) {
|
||||
callback(JSON.stringify(topics));
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:admin.categories.create', function(data, callback) {
|
||||
admin.categories.create(data, function(err, data) {
|
||||
categories.create(data, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
});
|
||||
@@ -955,4 +1017,33 @@ module.exports.init = function(io) {
|
||||
socket.on('api:admin.theme.set', meta.themes.set);
|
||||
});
|
||||
|
||||
|
||||
function emitTopicPostStats() {
|
||||
RDB.mget(['totaltopiccount', 'totalpostcount'], function(err, data) {
|
||||
if (err) {
|
||||
return winston.err(err);
|
||||
}
|
||||
|
||||
var stats = {
|
||||
topics: data[0] ? data[0] : 0,
|
||||
posts: data[1] ? data[1] : 0
|
||||
};
|
||||
|
||||
io.sockets.emit('post.stats', stats);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.emitUserCount = function() {
|
||||
RDB.get('usercount', function(err, count) {
|
||||
io.sockets.emit('user.count', {
|
||||
count: count
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.in = function(room) {
|
||||
return io.sockets.in(room);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user