Merge branch 'master' of github.com:designcreateplay/NodeBB

v1.18.x
Julian Lam 11 years ago
commit ef2dbe8df6

@ -1,5 +1,6 @@
# <img alt="NodeBB" src="http://i.imgur.com/3yj1n6N.png" /> # <img alt="NodeBB" src="http://i.imgur.com/3yj1n6N.png" />
[![Dependency Status](https://david-dm.org/designcreateplay/nodebb.png)](https://david-dm.org/designcreateplay/nodebb) [![Dependency Status](https://david-dm.org/designcreateplay/nodebb.png)](https://david-dm.org/designcreateplay/nodebb)
[![Code Climate](https://codeclimate.com/github/designcreateplay/NodeBB.png)](https://codeclimate.com/github/designcreateplay/NodeBB)
**NodeBB Forum Software** is powered by Node.js and built on a Redis database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions. **NodeBB Forum Software** is powered by Node.js and built on a Redis database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions.

@ -3,6 +3,7 @@
var config = {}, var config = {},
templates, templates,
fs = null, fs = null,
path = null,
available_templates = [], available_templates = [],
parsed_variables = {}, parsed_variables = {},
apiXHR; apiXHR;
@ -12,7 +13,8 @@
}; };
try { try {
fs = require('fs'); fs = require('fs'),
path = require('path');
} catch (e) {} } catch (e) {}
templates.force_refresh = function (tpl) { templates.force_refresh = function (tpl) {
@ -127,6 +129,27 @@
loadTemplates(templates_to_load || [], custom_templates || false); loadTemplates(templates_to_load || [], custom_templates || false);
} }
templates.render = function(filename, options, fn) {
if ('function' === typeof options) {
fn = options, options = false;
}
var tpl = filename
.replace(path.join(__dirname + '/../templates/'), '')
.replace('.' + options.settings['view engine'], '');
if (!templates[tpl]) {
fs.readFile(filename, function (err, html) {
templates[tpl] = html.toString();
templates.prepare(templates[tpl]);
return fn(err, templates[tpl].parse(options));
});
} else {
return fn(null, templates[tpl].parse(options));
}
}
templates.getTemplateNameFromUrl = function (url) { templates.getTemplateNameFromUrl = function (url) {
var parts = url.split('?')[0].split('/'); var parts = url.split('?')[0].split('/');
@ -419,6 +442,8 @@
})(data, "", template); })(data, "", template);
} }
module.exports.__express = module.exports.render;
if ('undefined' !== typeof window) { if ('undefined' !== typeof window) {
window.templates = module.exports; window.templates = module.exports;
templates.init(); templates.init();

@ -1,9 +1,15 @@
<input type="hidden" template-variable="category_id" value="{cid}" />
<input type="hidden" template-variable="category_name" value="{name}" />
<input type="hidden" template-variable="currentPage" value="{currentPage}" />
<input type="hidden" template-variable="pageCount" value="{pageCount}" />
<ol class="breadcrumb"> <ol class="breadcrumb">
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a> <a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
</li> </li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">{category_name} <a target="_blank" href="../{category_id}.rss"><i class="fa fa-rss-square"></i></a></span> <span itemprop="title">{name} <a target="_blank" href="../{cid}.rss"><i class="fa fa-rss-square"></i></a></span>
</li> </li>
</ol> </ol>
@ -11,13 +17,13 @@
<!-- IF privileges.write --> <!-- IF privileges.write -->
<button id="new_post" class="btn btn-primary">[[category:new_topic_button]]</button> <button id="new_post" class="btn btn-primary">[[category:new_topic_button]]</button>
<!-- ENDIF privileges.write --> <!-- ENDIF privileges.write -->
<!-- IF !disableSocialButtons --> <!-- IF !config.disableSocialButtons -->
<div class="inline-block pull-right"> <div class="inline-block pull-right">
<a href="#" id="facebook-share"><i class="fa fa-facebook-square fa-2x"></i></a>&nbsp; <a href="#" id="facebook-share"><i class="fa fa-facebook-square fa-2x"></i></a>&nbsp;
<a href="#" id="twitter-share"><i class="fa fa-twitter-square fa-2x"></i></a>&nbsp; <a href="#" id="twitter-share"><i class="fa fa-twitter-square fa-2x"></i></a>&nbsp;
<a href="#" id="google-share"><i class="fa fa-google-plus-square fa-2x"></i></a>&nbsp; <a href="#" id="google-share"><i class="fa fa-google-plus-square fa-2x"></i></a>&nbsp;
</div> </div>
<!-- ENDIF !disableSocialButtons --> <!-- ENDIF !config.disableSocialButtons -->
</div> </div>
<hr/> <hr/>
@ -109,7 +115,3 @@
<!-- ENDIF topics.length --> <!-- ENDIF topics.length -->
</div> </div>
<input type="hidden" template-variable="category_id" value="{category_id}" />
<input type="hidden" template-variable="category_name" value="{category_name}" />
<input type="hidden" template-variable="currentPage" value="{currentPage}" />
<input type="hidden" template-variable="pageCount" value="{pageCount}" />

@ -3,17 +3,17 @@
<a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a> <a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
</li> </li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">{category_name} <a target="_blank" href="../{category_id}.rss"><i class="fa fa-rss-square"></i></a></span> <span itemprop="title">{name} <a target="_blank" href="../{cid}.rss"><i class="fa fa-rss-square"></i></a></span>
</li> </li>
</ol> </ol>
<ul class="topics" itemscope itemtype="http://www.schema.org/ItemList" data-nextstart="{nextStart}"> <ul class="topics" itemscope itemtype="http://www.schema.org/ItemList" data-nextstart="{nextStart}">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li itemprop="itemListElement"> <li itemprop="itemListElement">
<meta itemprop="name" content="{topics.title}"> <meta itemprop="name" content="{topics.title}">
<span class="timestamp">{topics.teaser_timestamp}</span> <span class="timestamp">{topics.teaser.timestamp}</span>
<a href="../../topic/{topics.slug}" itemprop="url">{topics.title} ({topics.postcount})</a> <a href="../../topic/{topics.slug}" itemprop="url">{topics.title} ({topics.postcount})</a>
<div class="teaser"> <div class="teaser">
<img class="img-thumbnail" src="{topics.teaser_userpicture}" /> <img class="img-thumbnail" src="{topics.teaser.picture}" />
<div class="clear"></div> <div class="clear"></div>
</div> </div>
</li> </li>

@ -3,10 +3,10 @@
<a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a> <a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
</li> </li>
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="{relative_path}/category/{category_slug}" itemprop="url"><span itemprop="title">{category_name}</span></a> <a href="{relative_path}/category/{category.slug}" itemprop="url"><span itemprop="title">{category.name}</span></a>
</li> </li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="fa fa-rss-square"></i></a></span> <span itemprop="title">{title} <a target="_blank" href="../{tid}.rss"><i class="fa fa-rss-square"></i></a></span>
</li> </li>
</ol> </ol>
<ul class="posts"> <ul class="posts">

@ -1,11 +1,11 @@
<input type="hidden" template-variable="expose_tools" value="{expose_tools}" /> <input type="hidden" template-variable="expose_tools" value="{expose_tools}" />
<input type="hidden" template-variable="topic_id" value="{topic_id}" /> <input type="hidden" template-variable="topic_id" value="{tid}" />
<input type="hidden" template-variable="currentPage" value="{currentPage}" /> <input type="hidden" template-variable="currentPage" value="{currentPage}" />
<input type="hidden" template-variable="pageCount" value="{pageCount}" /> <input type="hidden" template-variable="pageCount" value="{pageCount}" />
<input type="hidden" template-variable="locked" value="{locked}" /> <input type="hidden" template-variable="locked" value="{locked}" />
<input type="hidden" template-variable="deleted" value="{deleted}" /> <input type="hidden" template-variable="deleted" value="{deleted}" />
<input type="hidden" template-variable="pinned" value="{pinned}" /> <input type="hidden" template-variable="pinned" value="{pinned}" />
<input type="hidden" template-variable="topic_name" value="{topic_name}" /> <input type="hidden" template-variable="topic_name" value="{title}" />
<input type="hidden" template-variable="postcount" value="{postcount}" /> <input type="hidden" template-variable="postcount" value="{postcount}" />
<div class="topic"> <div class="topic">
@ -14,14 +14,14 @@
<a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a> <a href="{relative_path}/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
</li> </li>
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="{relative_path}/category/{category_slug}" itemprop="url"><span itemprop="title">{category_name}</span></a> <a href="{relative_path}/category/{category.slug}" itemprop="url"><span itemprop="title">{category.name}</span></a>
</li> </li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="fa fa-rss-square"></i></a></span> <span itemprop="title">{title} <a target="_blank" href="../{tid}.rss"><i class="fa fa-rss-square"></i></a></span>
</li> </li>
</ol> </ol>
<ul id="post-container" class="posts" data-tid="{topic_id}"> <ul id="post-container" class="posts" data-tid="{tid}">
<!-- BEGIN posts --> <!-- BEGIN posts -->
<li class="post-row infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-userslug="{posts.userslug}" data-index="{posts.index}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment"> <li class="post-row infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-userslug="{posts.userslug}" data-index="{posts.index}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a> <a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
@ -44,7 +44,7 @@
<img itemprop="image" src="{posts.picture}" align="left" class="img-thumbnail" width=150 height=150 /> <img itemprop="image" src="{posts.picture}" align="left" class="img-thumbnail" width=150 height=150 />
</a> </a>
<h3 class="main-post"> <h3 class="main-post">
<p id="topic_title_{posts.pid}" class="topic-title" itemprop="name">{topic_name}</p> <p id="topic_title_{posts.pid}" class="topic-title" itemprop="name">{title}</p>
</h3> </h3>
<div class="topic-buttons"> <div class="topic-buttons">
@ -99,11 +99,11 @@
<div class="dropdown share-dropdown"> <div class="dropdown share-dropdown">
<button title="[[topic:share]]"class="btn btn-sm btn-default share" data-toggle="dropdown" href="#"><i class="fa fa-share-square-o"></i></button> <button title="[[topic:share]]"class="btn btn-sm btn-default share" data-toggle="dropdown" href="#"><i class="fa fa-share-square-o"></i></button>
<ul class="dropdown-menu text-center pull-right" role="menu" aria-labelledby="dLabel"> <ul class="dropdown-menu text-center pull-right" role="menu" aria-labelledby="dLabel">
<!-- IF !disableSocialButtons --> <!-- IF !config.disableSocialButtons -->
<li class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="fa fa-facebook fa-fw"></i></li> <li class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="fa fa-facebook fa-fw"></i></li>
<li class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="fa fa-twitter fa-fw"></i></li> <li class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="fa fa-twitter fa-fw"></i></li>
<li class="btn btn-sm btn-default google-share" type="button" title=""><i class="fa fa-google-plus fa-fw"></i></li> <li class="btn btn-sm btn-default google-share" type="button" title=""><i class="fa fa-google-plus fa-fw"></i></li>
<!-- ENDIF !disableSocialButtons --> <!-- ENDIF !config.disableSocialButtons -->
<li> <li>
<input type="text" id="post_{posts.pid}_link" value="" class="form-control pull-right post-link" style=""></input> <input type="text" id="post_{posts.pid}_link" value="" class="form-control pull-right post-link" style=""></input>
<li> <li>

@ -59,39 +59,26 @@ var db = require('./database'),
Categories.markAsRead(cid, uid); Categories.markAsRead(cid, uid);
} }
function getCategoryData(next) {
Categories.getCategoryData(cid, next);
}
function getTopics(next) {
Categories.getCategoryTopics(cid, start, end, uid, next);
}
function getPageCount(next) {
Categories.getPageCount(cid, uid, next);
}
async.parallel({ async.parallel({
'category': getCategoryData, category: function(next) {
'topics': getTopics, Categories.getCategoryData(cid, next);
'pageCount': getPageCount },
topics: function(next) {
Categories.getCategoryTopics(cid, start, end, uid, next);
},
pageCount: function(next) {
Categories.getPageCount(cid, uid, next);
}
}, function(err, results) { }, function(err, results) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
var category = { var category = results.category;
'category_name': results.category.name, category.topics = results.topics.topics;
'category_description': results.category.description, category.nextStart = results.topics.nextStart;
'link': results.category.link, category.pageCount = results.pageCount;
'disabled': results.category.disabled, category.topic_row_size = 'col-md-9';
'topic_row_size': 'col-md-9',
'category_id': cid,
'topics': results.topics.topics,
'nextStart': results.topics.nextStart,
'pageCount': results.pageCount,
'disableSocialButtons': meta.config.disableSocialButtons !== undefined ? parseInt(meta.config.disableSocialButtons, 10) !== 0 : false,
};
callback(null, category); callback(null, category);
}); });
@ -103,26 +90,26 @@ var db = require('./database'),
Categories.getTopicIds(cid, start, stop, next); Categories.getTopicIds(cid, start, stop, next);
}, },
function(tids, next) { function(tids, next) {
topics.getTopicsByTids(tids, cid, uid, next); topics.getTopicsByTids(tids, uid, next);
}, },
function(topics, next) { function(topics, next) {
if (topics && topics.length > 0) { if (!topics || !topics.length) {
db.sortedSetRevRank('categories:' + cid + ':tid', topics[topics.length - 1].tid, function(err, rank) { return next(null, {
if(err) { topics: [],
return next(err); nextStart: 1
}
next(null, {
topics: topics,
nextStart: parseInt(rank, 10) + 1
});
}); });
} else { }
db.sortedSetRevRank('categories:' + cid + ':tid', topics[topics.length - 1].tid, function(err, rank) {
if(err) {
return next(err);
}
next(null, { next(null, {
topics: topics, topics: topics,
nextStart: 1 nextStart: parseInt(rank, 10) + 1
}); });
} });
} }
], callback); ], callback);
}; };
@ -299,14 +286,14 @@ var db = require('./database'),
Categories.getCategories = function(cids, uid, callback) { Categories.getCategories = function(cids, uid, callback) {
if (!cids || !Array.isArray(cids) || cids.length === 0) { if (!cids || !Array.isArray(cids) || cids.length === 0) {
return callback(new Error('invalid-cids'), null); return callback(new Error('invalid-cids'));
} }
function getCategory(cid, callback) { function getCategory(cid, callback) {
Categories.getCategoryData(cid, function(err, categoryData) { Categories.getCategoryData(cid, function(err, categoryData) {
if (err) { if (err) {
winston.warn('Attempted to retrieve cid ' + cid + ', but nothing was returned!'); winston.warn('Attempted to retrieve cid ' + cid + ', but nothing was returned!');
return callback(err, null); return callback(err);
} }
Categories.hasReadCategory(cid, uid, function(hasRead) { Categories.hasReadCategory(cid, uid, function(hasRead) {
@ -338,7 +325,7 @@ var db = require('./database'),
db.getSortedSetRange('uid:' + uid + ':posts', 0, -1, function(err, pids) { db.getSortedSetRange('uid:' + uid + ':posts', 0, -1, function(err, pids) {
if (err) { if (err) {
return callback(err, null); return callback(err);
} }
var index = 0, var index = 0,
@ -359,15 +346,11 @@ var db = require('./database'),
} }
++index; ++index;
callback(null); callback();
}); });
}, },
function(err) { function(err) {
if (err) { callback(err, active);
return callback(err, null);
}
callback(null, active);
} }
); );
}); });
@ -424,6 +407,6 @@ var db = require('./database'),
Categories.addActiveUser(cid, uid, timestamp); Categories.addActiveUser(cid, uid, timestamp);
}); });
} };
}(exports)); }(exports));

@ -153,24 +153,13 @@ var async = require('async'),
downvoted: function(next) { downvoted: function(next) {
db.isSetMember('pid:' + pid + ':downvote', uid, next); db.isSetMember('pid:' + pid + ':downvote', uid, next);
} }
}, function(err, results) { }, callback);
callback(err, results)
});
}; };
Favourites.getVoteStatusByPostIDs = function(pids, uid, callback) { Favourites.getVoteStatusByPostIDs = function(pids, uid, callback) {
var data = {}; async.map(pids, function(pid, next) {
Favourites.hasVoted(pid, uid, next);
function iterator(pid, next) { }, callback);
Favourites.hasVoted(pid, uid, function(err, voteStatus) {
data[pid] = voteStatus;
next()
});
}
async.each(pids, iterator, function(err) {
callback(data);
});
}; };
Favourites.favourite = function (pid, room_id, uid, socket) { Favourites.favourite = function (pid, room_id, uid, socket) {
@ -248,33 +237,15 @@ var async = require('async'),
}; };
Favourites.getFavouritesByPostIDs = function(pids, uid, callback) { Favourites.getFavouritesByPostIDs = function(pids, uid, callback) {
var data = {}; async.map(pids, function(pid, next) {
Favourites.hasFavourited(pid, uid, next);
function iterator(pid, next) { }, callback);
Favourites.hasFavourited(pid, uid, function(err, hasFavourited) {
data[pid] = hasFavourited;
next()
});
}
async.each(pids, iterator, function(err) {
callback(data);
});
}; };
Favourites.getFavouritedUidsByPids = function(pids, callback) { Favourites.getFavouritedUidsByPids = function(pids, callback) {
var data = {}; async.map(pids, function(pid, next) {
db.getSetMembers('pid:' + pid + ':users_favourited', next);
function getUids(pid, next) { }, callback)
db.getSetMembers('pid:' + pid + ':users_favourited', function(err, uids) {
data[pid] = uids;
next();
});
}
async.each(pids, getUids, function(err) {
callback(data);
});
}; };
}(exports)); }(exports));

@ -60,6 +60,7 @@ var path = require('path'),
config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1; config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1;
config.allowTopicsThumbnail = parseInt(meta.config.allowTopicsThumbnail, 10) === 1; config.allowTopicsThumbnail = parseInt(meta.config.allowTopicsThumbnail, 10) === 1;
config.usePagination = parseInt(meta.config.usePagination, 10) === 1; config.usePagination = parseInt(meta.config.usePagination, 10) === 1;
config.disableSocialButtons = parseInt(meta.config.disableSocialButtons, 10) === 1;
config.topicsPerPage = meta.config.topicsPerPage || 20; config.topicsPerPage = meta.config.topicsPerPage || 20;
config.postsPerPage = meta.config.postsPerPage || 20; config.postsPerPage = meta.config.postsPerPage || 20;
config.maximumFileSize = meta.config.maximumFileSize; config.maximumFileSize = meta.config.maximumFileSize;
@ -169,7 +170,8 @@ var path = require('path'),
}); });
app.get('/topic/:id/:slug?', function (req, res, next) { app.get('/topic/:id/:slug?', function (req, res, next) {
var uid = (req.user) ? req.user.uid : 0; var uid = req.user? parseInt(req.user.uid, 10) : 0;
var tid = req.params.id;
var page = 1; var page = 1;
if(req.query && req.query.page) { if(req.query && req.query.page) {
page = req.query.page; page = req.query.page;
@ -187,29 +189,41 @@ var path = require('path'),
var start = (page - 1) * settings.postsPerPage; var start = (page - 1) * settings.postsPerPage;
var end = start + settings.postsPerPage - 1; var end = start + settings.postsPerPage - 1;
ThreadTools.privileges(req.params.id, uid, function(err, privileges) { ThreadTools.privileges(tid, uid, function(err, privileges) {
if (privileges.read) { if(err) {
topics.getTopicWithPosts(req.params.id, uid, start, end, false, function (err, data) { return next(err);
if(err) { }
return next(err);
}
if(page > data.pageCount) { if(!privileges.read) {
return res.send(404); res.send(403);
} }
data.currentPage = page; topics.getTopicWithPosts(tid, uid, start, end, function (err, data) {
data.privileges = privileges; if(err) {
return next(err);
}
if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) { if(page > data.pageCount) {
return res.json(404, {}); return res.send(404);
} }
res.json(data); if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) {
}); return res.json(404, {});
} else { }
res.send(403);
} data.currentPage = page;
data.privileges = privileges;
if (uid) {
topics.markAsRead(tid, uid, function(err) {
topics.pushUnreadCount(uid);
});
}
topics.increaseViewCount(tid);
res.json(data);
});
}); });
}); });
}); });
@ -401,7 +415,7 @@ var path = require('path'),
return callback(err, null); return callback(err, null);
} }
topics.getTopicsByTids(tids, 0, 0, callback); topics.getTopicsByTids(tids, 0, callback);
}); });
} }
@ -503,6 +517,14 @@ var path = require('path'),
app.get('/500', function(req, res) { app.get('/500', function(req, res) {
res.json({errorMessage: 'testing'}); res.json({errorMessage: 'testing'});
}); });
app.namespace('/categories', function() {
app.get(':cid/moderators', function(req, res) {
categories.getModerators(req.params.cid, function(err, moderators) {
res.json({moderators: moderators});
})
});
});
}); });
} }
}(exports)); }(exports));

@ -55,7 +55,7 @@
function generateForTopic(req, res, next) { function generateForTopic(req, res, next) {
var tid = req.params.topic_id; var tid = req.params.topic_id;
topics.getTopicWithPosts(tid, 0, 0, 25, true, function (err, topicData) { topics.getTopicWithPosts(tid, 0, 0, 25, function (err, topicData) {
if (err) { if (err) {
return next(err); return next(err);
} }
@ -65,7 +65,7 @@
var author = topicData.posts.length ? topicData.posts[0].username : ''; var author = topicData.posts.length ? topicData.posts[0].username : '';
var feed = new rss({ var feed = new rss({
title: topicData.topic_name, title: topicData.title,
description: description, description: description,
feed_url: nconf.get('url') + '/topic/' + tid + '.rss', feed_url: nconf.get('url') + '/topic/' + tid + '.rss',
site_url: nconf.get('url') + '/topic/' + topicData.slug, site_url: nconf.get('url') + '/topic/' + topicData.slug,
@ -85,7 +85,7 @@
dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString(); dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString();
feed.item({ feed.item({
title: 'Reply to ' + topicData.topic_name + ' on ' + dateStamp, title: 'Reply to ' + topicData.title + ' on ' + dateStamp,
description: postData.content, description: postData.content,
url: nconf.get('url') + '/topic/' + topicData.slug + '#' + postData.pid, url: nconf.get('url') + '/topic/' + topicData.slug + '#' + postData.pid,
author: postData.username, author: postData.username,
@ -109,10 +109,10 @@
} }
var feed = new rss({ var feed = new rss({
title: categoryData.category_name, title: categoryData.name,
description: categoryData.category_description, description: categoryData.description,
feed_url: nconf.get('url') + '/category/' + cid + '.rss', feed_url: nconf.get('url') + '/category/' + cid + '.rss',
site_url: nconf.get('url') + '/category/' + categoryData.category_id, site_url: nconf.get('url') + '/category/' + categoryData.cid,
ttl: 60 ttl: 60
}); });

@ -207,30 +207,37 @@ SocketPosts.getPrivileges = function(socket, pid, callback) {
SocketPosts.getFavouritedUsers = function(socket, pid, callback) { SocketPosts.getFavouritedUsers = function(socket, pid, callback) {
favourites.getFavouritedUidsByPids([pid], function(data) { favourites.getFavouritedUidsByPids([pid], function(err, data) {
if(err) {
return callback(err);
}
if(!Array.isArray(data) || !data.length) {
callback(null, "");
}
var max = 5; //hardcoded var max = 5; //hardcoded
var usernames = ""; var usernames = "";
var pid_uids = data[pid]; var pid_uids = data[0];
var rest_amount = 0; var rest_amount = 0;
if (data.hasOwnProperty(pid) && pid_uids.length > 0) {
if (pid_uids.length > max) { if (pid_uids.length > max) {
rest_amount = pid_uids.length - max; rest_amount = pid_uids.length - max;
pid_uids = pid_uids.slice(0, max); pid_uids = pid_uids.slice(0, max);
}
user.getUsernamesByUids(pid_uids, function(err, result) {
if(err) {
return callback(err);
}
usernames = result.join(', ') + (rest_amount > 0
? " and " + rest_amount + (rest_amount > 1 ? " others" : " other")
: "");
callback(null, usernames);
});
} else {
callback(null, "");
} }
user.getUsernamesByUids(pid_uids, function(err, result) {
if(err) {
return callback(err);
}
usernames = result.join(', ') + (rest_amount > 0
? " and " + rest_amount + (rest_amount > 1 ? " others" : " other")
: "");
callback(null, usernames);
});
}); });
}; };

@ -247,7 +247,7 @@ SocketTopics.loadMore = function(socket, data, callback) {
async.parallel({ async.parallel({
posts: function(next) { posts: function(next) {
topics.getTopicPosts(data.tid, start, end, socket.uid, next); topics.getTopicPosts(data.tid, start, end, socket.uid, false, next);
}, },
privileges: function(next) { privileges: function(next) {
threadTools.privileges(data.tid, socket.uid, next); threadTools.privileges(data.tid, socket.uid, next);

@ -1,3 +1,5 @@
"use strict";
var async = require('async'), var async = require('async'),
gravatar = require('gravatar'), gravatar = require('gravatar'),
path = require('path'), path = require('path'),
@ -53,7 +55,7 @@ var async = require('async'),
}; };
if(thumb) { if(thumb) {
topicData['thumb'] = thumb; topicData.thumb = thumb;
} }
db.setObject('topic:' + tid, topicData, function(err) { db.setObject('topic:' + tid, topicData, function(err) {
@ -109,8 +111,8 @@ var async = require('async'),
categoryTools.exists(cid, next); categoryTools.exists(cid, next);
}, },
function(categoryExists, next) { function(categoryExists, next) {
if(!categoryExists) { if (!categoryExists) {
return next(new Error('category doesn\'t exist')) return next(new Error('category doesn\'t exist'));
} }
categoryTools.privileges(cid, uid, next); categoryTools.privileges(cid, uid, next);
}, },
@ -134,7 +136,7 @@ var async = require('async'),
next(null, postData); next(null, postData);
}, },
function(postData, next) { function(postData, next) {
Topics.getTopicsByTids([postData.tid], data.cid, uid, function(err, topicData) { Topics.getTopicsByTids([postData.tid], uid, function(err, topicData) {
if(err) { if(err) {
return next(err); return next(err);
} }
@ -350,12 +352,7 @@ var async = require('async'),
}); });
}; };
Topics.getTopicPosts = function(tid, start, end, current_user, reverse, callback) { Topics.getTopicPosts = function(tid, start, end, uid, reverse, callback) {
if (typeof reverse === 'function') {
callback = reverse;
reverse = false;
}
posts.getPostsByTid(tid, start, end, reverse, function(err, postData) { posts.getPostsByTid(tid, start, end, reverse, function(err, postData) {
if(err) { if(err) {
return callback(err); return callback(err);
@ -369,69 +366,39 @@ var async = require('async'),
postData[i].index = start + i; postData[i].index = start + i;
} }
pids = postData.map(function(post) { var pids = postData.map(function(post) {
return post.pid; return post.pid;
}); });
function getFavouritesData(next) { async.parallel({
favourites.getFavouritesByPostIDs(pids, current_user, function(fav_data) { favourites : function(next) {
next(null, fav_data); favourites.getFavouritesByPostIDs(pids, uid, next);
}); },
} voteData : function(next) {
favourites.getVoteStatusByPostIDs(pids, uid, next);
function getVoteStatusData(next) { },
favourites.getVoteStatusByPostIDs(pids, current_user, function(vote_data) { userData : function(next) {
next(null, vote_data); async.each(postData, posts.addUserInfoToPost, next);
}) },
} privileges : function(next) {
async.map(pids, function (pid, next) {
function addUserInfoToPosts(next) { postTools.privileges(pid, uid, next);
function iterator(post, callback) { }, next);
posts.addUserInfoToPost(post, function() {
callback(null);
});
}
async.each(postData, iterator, function(err) {
next(err, null);
});
}
function getPrivileges(next) {
var privs = {};
async.each(pids, getPostPrivileges, function(err) {
next(err, privs);
});
function getPostPrivileges(pid, next) {
postTools.privileges(pid, current_user, function(err, postPrivileges) {
if(err) {
return next(err);
}
privs[pid] = postPrivileges;
next();
});
} }
} }, function(err, results) {
async.parallel([getFavouritesData, addUserInfoToPosts, getPrivileges, getVoteStatusData], function(err, results) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
var fav_data = results[0],
privileges = results[2],
voteStatus = results[3];
for (var i = 0; i < postData.length; ++i) { for (var i = 0; i < postData.length; ++i) {
var pid = postData[i].pid; postData[i].favourited = results.favourites[i];
postData[i].favourited = fav_data[pid]; postData[i].upvoted = results.voteData[i].upvoted;
postData[i].upvoted = voteStatus[pid].upvoted; postData[i].downvoted = results.voteData[i].downvoted;
postData[i].downvoted = voteStatus[pid].downvoted;
postData[i].votes = postData[i].votes || 0; postData[i].votes = postData[i].votes || 0;
postData[i].display_moderator_tools = (current_user != 0) && privileges[pid].editable; postData[i].display_moderator_tools = parseInt(uid, 10) !== 0 && results.privileges[i].editable;
postData[i].display_move_tools = privileges[pid].move; postData[i].display_move_tools = results.privileges[i].move;
if(parseInt(postData[i].deleted, 10) === 1 && !privileges[pid].view_deleted) {
if(parseInt(postData[i].deleted, 10) === 1 && !results.privileges[i].view_deleted) {
postData[i].content = 'This post is deleted!'; postData[i].content = 'This post is deleted!';
} }
} }
@ -484,7 +451,7 @@ var async = require('async'),
next(!err && privileges.read); next(!err && privileges.read);
}); });
}, function(tids) { }, function(tids) {
Topics.getTopicsByTids(tids, 0, uid, function(err, topicData) { Topics.getTopicsByTids(tids, uid, function(err, topicData) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
@ -495,7 +462,7 @@ var async = require('async'),
db.sortedSetRevRank(set, topicData[topicData.length - 1].tid, function(err, rank) { db.sortedSetRevRank(set, topicData[topicData.length - 1].tid, function(err, rank) {
if(err) { if(err) {
return calllback(err); return callback(err);
} }
returnTopics.nextStart = parseInt(rank, 10) + 1; returnTopics.nextStart = parseInt(rank, 10) + 1;
@ -513,7 +480,7 @@ var async = require('async'),
month: 2592000000 month: 2592000000
}; };
var since = terms['day']; var since = terms.day;
if(terms[term]) { if(terms[term]) {
since = terms[term]; since = terms[term];
} }
@ -608,7 +575,7 @@ var async = require('async'),
function sendUnreadTopics(topicIds) { function sendUnreadTopics(topicIds) {
Topics.getTopicsByTids(topicIds, 0, uid, function(err, topicData) { Topics.getTopicsByTids(topicIds, uid, function(err, topicData) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
@ -670,7 +637,7 @@ var async = require('async'),
}); });
}; };
Topics.getTopicsByTids = function(tids, cid, current_user, callback) { Topics.getTopicsByTids = function(tids, uid, callback) {
if (!Array.isArray(tids) || tids.length === 0) { if (!Array.isArray(tids) || tids.length === 0) {
return callback(null, []); return callback(null, []);
@ -679,13 +646,13 @@ var async = require('async'),
function getTopicInfo(topicData, callback) { function getTopicInfo(topicData, callback) {
async.parallel({ async.parallel({
hasread : function (next) { hasread : function (next) {
Topics.hasReadTopic(topicData.tid, current_user, next); Topics.hasReadTopic(topicData.tid, uid, next);
}, },
teaser : function (next) { teaser : function (next) {
Topics.getTeaser(topicData.tid, next); Topics.getTeaser(topicData.tid, next);
}, },
privileges : function (next) { privileges : function (next) {
categoryTools.privileges(topicData.cid, current_user, next); categoryTools.privileges(topicData.cid, uid, next);
}, },
categoryData : function (next) { categoryData : function (next) {
categories.getCategoryFields(topicData.cid, ['name', 'slug', 'icon'], next); categories.getCategoryFields(topicData.cid, ['name', 'slug', 'icon'], next);
@ -696,7 +663,7 @@ var async = require('async'),
function isTopicVisible(topicData, topicInfo) { function isTopicVisible(topicData, topicInfo) {
var deleted = parseInt(topicData.deleted, 10) !== 0; var deleted = parseInt(topicData.deleted, 10) !== 0;
return !deleted || (deleted && topicInfo.privileges.view_deleted) || topicData.uid === current_user; return !deleted || (deleted && topicInfo.privileges.view_deleted) || parseInt(topicData.uid, 10) === parseInt(uid, 10);
} }
function loadTopic(tid, next) { function loadTopic(tid, next) {
@ -722,7 +689,7 @@ var async = require('async'),
topicData.pinned = parseInt(topicData.pinned, 10) === 1; topicData.pinned = parseInt(topicData.pinned, 10) === 1;
topicData.locked = parseInt(topicData.locked, 10) === 1; topicData.locked = parseInt(topicData.locked, 10) === 1;
topicData.deleted = parseInt(topicData.deleted, 10) === 1; topicData.deleted = parseInt(topicData.deleted, 10) === 1;
topicData.unread = !(topicInfo.hasread && parseInt(current_user, 10) !== 0); topicData.unread = !(topicInfo.hasread && parseInt(uid, 10) !== 0);
topicData.unreplied = parseInt(topicData.postcount, 10) === 1; topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
topicData.category = topicInfo.categoryData; topicData.category = topicInfo.categoryData;
@ -746,78 +713,46 @@ var async = require('async'),
}); });
}; };
Topics.getTopicWithPosts = function(tid, current_user, start, end, quiet, callback) { Topics.getTopicWithPosts = function(tid, uid, start, end, callback) {
threadTools.exists(tid, function(err, exists) { threadTools.exists(tid, function(err, exists) {
if (err || !exists) { if (err || !exists) {
return callback(err || new Error('Topic tid \'' + tid + '\' not found')); return callback(err || new Error('Topic tid \'' + tid + '\' not found'));
} }
// "quiet" is used for things like RSS feed updating, HTML parsing for non-js users, etc async.parallel({
if (!quiet) { topicData : function(next) {
Topics.markAsRead(tid, current_user, function(err) { Topics.getTopicData(tid, next);
Topics.pushUnreadCount(current_user); },
}); posts : function(next) {
Topics.increaseViewCount(tid); Topics.getTopicPosts(tid, start, end, uid, false, next);
} },
privileges : function(next) {
function getTopicData(next) { threadTools.privileges(tid, uid, next);
Topics.getTopicData(tid, next); },
} category : function(next) {
Topics.getCategoryData(tid, next);
function getTopicPosts(next) { },
Topics.getTopicPosts(tid, start, end, current_user, next); pageCount : function(next) {
} Topics.getPageCount(tid, uid, next);
},
function getPrivileges(next) { threadTools : function(next) {
threadTools.privileges(tid, current_user, next); Plugins.fireHook('filter:topic.thread_tools', [], next);
} }
}, function(err, results) {
function getCategoryData(next) {
Topics.getCategoryData(tid, next);
}
function getPageCount(next) {
Topics.getPageCount(tid, current_user, next);
}
function getThreadTools(next) {
Plugins.fireHook('filter:topic.thread_tools', [], function(err, threadTools) {
next(err, threadTools);
});
}
async.parallel([getTopicData, getTopicPosts, getPrivileges, getCategoryData, getPageCount, getThreadTools], function(err, results) {
if (err) { if (err) {
winston.error('[Topics.getTopicWithPosts] Could not retrieve topic data: ', err.message); winston.error('[Topics.getTopicWithPosts] Could not retrieve topic data: ', err.message);
return callback(err); return callback(err);
} }
var topicData = results[0], var topicData = results.topicData;
privileges = results[2], topicData.category = results.category;
categoryData = results[3], topicData.posts = results.posts;
pageCount = results[4], topicData.thread_tools = results.threadTools;
threadTools = results[5]; topicData.pageCount = results.pageCount;
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
callback(null, { topicData.expose_tools = results.privileges.editable ? 1 : 0;
'topic_name': topicData.title,
'category_name': categoryData.name, callback(null, topicData);
'category_slug': categoryData.slug,
'locked': topicData.locked,
'deleted': topicData.deleted,
'pinned': topicData.pinned,
'timestamp': topicData.timestamp,
'slug': topicData.slug,
'thumb': topicData.thumb,
'postcount': topicData.postcount,
'viewcount': topicData.viewcount,
'pageCount': pageCount,
'unreplied': parseInt(topicData.postcount, 10) === 1,
'topic_id': tid,
'expose_tools': privileges.editable ? 1 : 0,
'thread_tools': threadTools,
'disableSocialButtons': meta.config.disableSocialButtons !== undefined ? parseInt(meta.config.disableSocialButtons, 10) !== 0 : false,
'posts': results[1]
});
}); });
}); });
}; };
@ -895,7 +830,7 @@ var async = require('async'),
if(err) { if(err) {
return callback(err); return callback(err);
} }
Topics.markCategoryUnreadForAll(tid, callback) Topics.markCategoryUnreadForAll(tid, callback);
}); });
}; };
@ -958,7 +893,7 @@ var async = require('async'),
return callback(null, []); return callback(null, []);
} }
async.map(tids, Topics.getTeaser, callback) async.map(tids, Topics.getTeaser, callback);
}; };
Topics.getTeaser = function(tid, callback) { Topics.getTeaser = function(tid, callback) {
@ -993,19 +928,19 @@ var async = require('async'),
}); });
}); });
}); });
} };
Topics.getTopicField = function(tid, field, callback) { Topics.getTopicField = function(tid, field, callback) {
db.getObjectField('topic:' + tid, field, callback); db.getObjectField('topic:' + tid, field, callback);
} };
Topics.getTopicFields = function(tid, fields, callback) { Topics.getTopicFields = function(tid, fields, callback) {
db.getObjectFields('topic:' + tid, fields, callback); db.getObjectFields('topic:' + tid, fields, callback);
} };
Topics.setTopicField = function(tid, field, value, callback) { Topics.setTopicField = function(tid, field, value, callback) {
db.setObjectField('topic:' + tid, field, value, callback); db.setObjectField('topic:' + tid, field, value, callback);
} };
Topics.increasePostCount = function(tid, callback) { Topics.increasePostCount = function(tid, callback) {
db.incrObjectField('topic:' + tid, 'postcount', function(err, value) { db.incrObjectField('topic:' + tid, 'postcount', function(err, value) {
@ -1014,7 +949,7 @@ var async = require('async'),
} }
db.sortedSetAdd('topics:posts', value, tid, callback); db.sortedSetAdd('topics:posts', value, tid, callback);
}); });
} };
Topics.decreasePostCount = function(tid, callback) { Topics.decreasePostCount = function(tid, callback) {
db.decrObjectField('topic:' + tid, 'postcount', function(err, value) { db.decrObjectField('topic:' + tid, 'postcount', function(err, value) {
@ -1023,7 +958,7 @@ var async = require('async'),
} }
db.sortedSetAdd('topics:posts', value, tid, callback); db.sortedSetAdd('topics:posts', value, tid, callback);
}); });
} };
Topics.increaseViewCount = function(tid, callback) { Topics.increaseViewCount = function(tid, callback) {
db.incrObjectField('topic:' + tid, 'viewcount', function(err, value) { db.incrObjectField('topic:' + tid, 'viewcount', function(err, value) {
@ -1032,7 +967,7 @@ var async = require('async'),
} }
db.sortedSetAdd('topics:views', value, tid, callback); db.sortedSetAdd('topics:views', value, tid, callback);
}); });
} };
Topics.isLocked = function(tid, callback) { Topics.isLocked = function(tid, callback) {
Topics.getTopicField(tid, 'locked', function(err, locked) { Topics.getTopicField(tid, 'locked', function(err, locked) {
@ -1041,30 +976,30 @@ var async = require('async'),
} }
callback(null, parseInt(locked, 10) === 1); callback(null, parseInt(locked, 10) === 1);
}); });
} };
Topics.updateTimestamp = function(tid, timestamp) { Topics.updateTimestamp = function(tid, timestamp) {
db.sortedSetAdd('topics:recent', timestamp, tid); db.sortedSetAdd('topics:recent', timestamp, tid);
Topics.setTopicField(tid, 'lastposttime', timestamp); Topics.setTopicField(tid, 'lastposttime', timestamp);
} };
Topics.onNewPostMade = function(tid, pid, timestamp, callback) { Topics.onNewPostMade = function(tid, pid, timestamp, callback) {
Topics.increasePostCount(tid); Topics.increasePostCount(tid);
Topics.updateTimestamp(tid, timestamp); Topics.updateTimestamp(tid, timestamp);
Topics.addPostToTopic(tid, pid, timestamp, callback); Topics.addPostToTopic(tid, pid, timestamp, callback);
} };
Topics.addPostToTopic = function(tid, pid, timestamp, callback) { Topics.addPostToTopic = function(tid, pid, timestamp, callback) {
db.sortedSetAdd('tid:' + tid + ':posts', timestamp, pid, callback); db.sortedSetAdd('tid:' + tid + ':posts', timestamp, pid, callback);
} };
Topics.removePostFromTopic = function(tid, pid, callback) { Topics.removePostFromTopic = function(tid, pid, callback) {
db.sortedSetRemove('tid:' + tid + ':posts', pid, callback); db.sortedSetRemove('tid:' + tid + ':posts', pid, callback);
} };
Topics.getPids = function(tid, callback) { Topics.getPids = function(tid, callback) {
db.getSortedSetRange('tid:' + tid + ':posts', 0, -1, callback); db.getSortedSetRange('tid:' + tid + ':posts', 0, -1, callback);
} };
Topics.getUids = function(tid, callback) { Topics.getUids = function(tid, callback) {
var uids = {}; var uids = {};
@ -1088,7 +1023,7 @@ var async = require('async'),
callback(null, Object.keys(uids)); callback(null, Object.keys(uids));
}); });
}); });
} };
Topics.updateTopicCount = function(callback) { Topics.updateTopicCount = function(callback) {
db.sortedSetCard('topics:recent', function(err, count) { db.sortedSetCard('topics:recent', function(err, count) {

@ -1,3 +1,5 @@
'use strict';
var bcrypt = require('bcryptjs'), var bcrypt = require('bcryptjs'),
async = require('async'), async = require('async'),
nconf = require('nconf'), nconf = require('nconf'),
@ -17,7 +19,7 @@ var bcrypt = require('bcryptjs'),
Emailer = require('./emailer'); Emailer = require('./emailer');
(function(User) { (function(User) {
'use strict';
User.create = function(userData, callback) { User.create = function(userData, callback) {
userData = userData || {}; userData = userData || {};
userData.userslug = utils.slugify(userData.username); userData.userslug = utils.slugify(userData.username);
@ -210,7 +212,7 @@ var bcrypt = require('bcryptjs'),
} }
if(!settings) { if(!settings) {
settings = {} settings = {};
} }
settings.showemail = settings.showemail ? parseInt(settings.showemail, 10) !== 0 : false; settings.showemail = settings.showemail ? parseInt(settings.showemail, 10) !== 0 : false;
@ -220,7 +222,7 @@ var bcrypt = require('bcryptjs'),
callback(null, settings); callback(null, settings);
}); });
} };
User.saveSettings = function(uid, data, callback) { User.saveSettings = function(uid, data, callback) {
@ -234,7 +236,7 @@ var bcrypt = require('bcryptjs'),
topicsPerPage: data.topicsPerPage, topicsPerPage: data.topicsPerPage,
postsPerPage: data.postsPerPage postsPerPage: data.postsPerPage
}, callback); }, callback);
} };
User.updateLastOnlineTime = function(uid, callback) { User.updateLastOnlineTime = function(uid, callback) {
User.getUserField(uid, 'status', function(err, status) { User.getUserField(uid, 'status', function(err, status) {
@ -418,7 +420,7 @@ var bcrypt = require('bcryptjs'),
} }
callback(); callback();
}); });
} };
User.isEmailAvailable = function(email, callback) { User.isEmailAvailable = function(email, callback) {
db.isObjectField('email:uid', email, function(err, exists) { db.isObjectField('email:uid', email, function(err, exists) {
@ -561,29 +563,28 @@ var bcrypt = require('bcryptjs'),
}); });
}; };
// thanks to @akhoury
User.getUsersCSV = function(callback) { User.getUsersCSV = function(callback) {
var csvContent = ""; var csvContent = "";
db.getObjectValues('username:uid', function(err, uids) { db.getObjectValues('username:uid', function(err, uids) {
if(err) {
return callback(err);
}
async.each(uids, function(uid, next) { async.each(uids, function(uid, next) {
User.getUserFields(uid, ['email', 'username'], function(err, userData) { User.getUserFields(uid, ['email', 'username'], function(err, userData) {
if(err) { if(err) {
return next(err); return next(err);
} }
csvContent += userData.email+ ',' + userData.username + ',' + uid +'\n'; csvContent += userData.email + ',' + userData.username + ',' + uid + '\n';
next(); next();
}); });
}, function(err) { }, function(err) {
if (err) {
throw err;
}
callback(err, csvContent); callback(err, csvContent);
}); });
}); });
} };
User.search = function(query, callback) { User.search = function(query, callback) {
if (!query || query.length === 0) { if (!query || query.length === 0) {
@ -727,7 +728,7 @@ var bcrypt = require('bcryptjs'),
User.getFollowerCount(uid, next); User.getFollowerCount(uid, next);
} }
}, callback); }, callback);
} };
User.getDataForUsers = function(uids, callback) { User.getDataForUsers = function(uids, callback) {
@ -850,22 +851,11 @@ var bcrypt = require('bcryptjs'),
}; };
User.isModerator = function(uid, cid, callback) { User.isModerator = function(uid, cid, callback) {
groups.isMemberByGroupName(uid, 'cid:' + cid + ':privileges:mod', function(err, isMember) { groups.isMemberByGroupName(uid, 'cid:' + cid + ':privileges:mod', callback);
if(err) {
return calback(err);
}
callback(err, isMember);
});
}; };
User.isAdministrator = function(uid, callback) { User.isAdministrator = function(uid, callback) {
groups.getGidFromName('administrators', function(err, gid) { groups.isMemberByGroupName(uid, 'administrators', callback);
if(err) {
return callback(err);
}
groups.isMember(uid, gid, callback);
});
}; };
User.reset = { User.reset = {
@ -882,7 +872,7 @@ var bcrypt = require('bcryptjs'),
return callback(err); return callback(err);
} }
if (expiry >= +Date.now() / 1000 | 0) { if (parseInt(expiry, 10) >= Date.now() / 1000) {
callback(null, true); callback(null, true);
} else { } else {
// Expired, delete from db // Expired, delete from db
@ -909,15 +899,15 @@ var bcrypt = require('bcryptjs'),
// Generate a new reset code // Generate a new reset code
var reset_code = utils.generateUUID(); var reset_code = utils.generateUUID();
db.setObjectField('reset:uid', reset_code, uid); db.setObjectField('reset:uid', reset_code, uid);
db.setObjectField('reset:expiry', reset_code, (60 * 60) + new Date() / 1000 | 0); // Active for one hour db.setObjectField('reset:expiry', reset_code, (60 * 60) + Math.floor(Date.now() / 1000));
var reset_link = nconf.get('url') + '/reset/' + reset_code; var reset_link = nconf.get('url') + '/reset/' + reset_code;
Emailer.send('reset', uid, { Emailer.send('reset', uid, {
'site_title': (meta.config['title'] || 'NodeBB'), 'site_title': (meta.config.title || 'NodeBB'),
'reset_link': reset_link, 'reset_link': reset_link,
subject: 'Password Reset Requested - ' + (meta.config['title'] || 'NodeBB') + '!', subject: 'Password Reset Requested - ' + (meta.config.title || 'NodeBB') + '!',
template: 'reset', template: 'reset',
uid: uid uid: uid
}); });
@ -1003,11 +993,11 @@ var bcrypt = require('bcryptjs'),
// Send intro email w/ confirm code // Send intro email w/ confirm code
User.getUserField(uid, 'username', function(err, username) { User.getUserField(uid, 'username', function(err, username) {
Emailer.send('welcome', uid, { Emailer.send('welcome', uid, {
'site_title': (meta.config['title'] || 'NodeBB'), 'site_title': (meta.config.title || 'NodeBB'),
username: username, username: username,
'confirm_link': confirm_link, 'confirm_link': confirm_link,
subject: 'Welcome to ' + (meta.config['title'] || 'NodeBB') + '!', subject: 'Welcome to ' + (meta.config.title || 'NodeBB') + '!',
template: 'welcome', template: 'welcome',
uid: uid uid: uid
}); });
@ -1086,7 +1076,7 @@ var bcrypt = require('bcryptjs'),
} }
}, function(err, notifications) { }, function(err, notifications) {
if(err) { if(err) {
return calback(err); return callback(err);
} }
// Remove empties // Remove empties

@ -226,6 +226,10 @@ process.on('uncaughtException', function(err) {
// Middlewares // Middlewares
app.configure(function() { app.configure(function() {
app.engine('tpl', templates.__express);
app.set('view engine', 'tpl');
app.set('views', path.join(__dirname, '../public/templates'));
async.series([ async.series([
function(next) { function(next) {
// Pre-router middlewares // Pre-router middlewares
@ -596,7 +600,7 @@ process.on('uncaughtException', function(err) {
var start = (page - 1) * settings.topicsPerPage, var start = (page - 1) * settings.topicsPerPage,
end = start + settings.topicsPerPage - 1; end = start + settings.topicsPerPage - 1;
topics.getTopicWithPosts(tid, uid, start, end, true, function (err, topicData) { topics.getTopicWithPosts(tid, uid, start, end, function (err, topicData) {
if (topicData) { if (topicData) {
if (parseInt(topicData.deleted, 10) === 1 && parseInt(topicData.expose_tools, 10) === 0) { if (parseInt(topicData.deleted, 10) === 1 && parseInt(topicData.expose_tools, 10) === 0) {
return next(new Error('Topic deleted'), null); return next(new Error('Topic deleted'), null);
@ -642,7 +646,7 @@ process.on('uncaughtException', function(err) {
metaTags: [ metaTags: [
{ {
name: "title", name: "title",
content: topicData.topic_name content: topicData.title
}, },
{ {
name: "description", name: "description",
@ -650,7 +654,7 @@ process.on('uncaughtException', function(err) {
}, },
{ {
property: 'og:title', property: 'og:title',
content: topicData.topic_name content: topicData.title
}, },
{ {
property: 'og:description', property: 'og:description',
@ -682,7 +686,7 @@ process.on('uncaughtException', function(err) {
}, },
{ {
property: 'article:section', property: 'article:section',
content: topicData.category_name content: topicData.category.name
} }
], ],
linkTags: [ linkTags: [
@ -693,7 +697,7 @@ process.on('uncaughtException', function(err) {
}, },
{ {
rel: 'up', rel: 'up',
href: nconf.get('url') + '/category/' + topicData.category_slug href: nconf.get('url') + '/category/' + topicData.category.slug
} }
] ]
}, function (err, header) { }, function (err, header) {
@ -783,15 +787,15 @@ process.on('uncaughtException', function(err) {
metaTags: [ metaTags: [
{ {
name: 'title', name: 'title',
content: categoryData.category_name content: categoryData.name
}, },
{ {
property: 'og:title', property: 'og:title',
content: categoryData.category_name content: categoryData.name
}, },
{ {
name: 'description', name: 'description',
content: categoryData.category_description content: categoryData.description
}, },
{ {
property: "og:type", property: "og:type",

@ -33,8 +33,8 @@ describe('Categories', function() {
it('should retrieve a newly created category by its ID', function(done) { it('should retrieve a newly created category by its ID', function(done) {
Categories.getCategoryById(categoryObj.cid, 0, -1, 0, function(err, categoryData) { Categories.getCategoryById(categoryObj.cid, 0, -1, 0, function(err, categoryData) {
assert(categoryData); assert(categoryData);
assert.equal(categoryObj.name, categoryData.category_name); assert.equal(categoryObj.name, categoryData.name);
assert.equal(categoryObj.description, categoryData.category_description); assert.equal(categoryObj.description, categoryData.description);
done(); done();
}); });

Loading…
Cancel
Save