Merge remote-tracking branch 'origin'

v1.18.x
Julian Lam 12 years ago
commit 4993b74c23

@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// Read config.js to grab redis info
var fs = require('fs'), var fs = require('fs'),
winston = require('winston'),
nconf = require('nconf'), nconf = require('nconf'),
pkg = require('./package.json'), pkg = require('./package.json'),
url = require('url'); url = require('url');
@ -28,11 +28,26 @@ global.env = process.env.NODE_ENV || 'production',
// Configuration setup // Configuration setup
nconf.argv().file({ file: __dirname + '/config.json'}); nconf.argv().file({ file: __dirname + '/config.json'});
winston.remove(winston.transports.Console);
winston.add(winston.transports.Console, {
colorize:true
});
winston.add(winston.transports.File, {
filename:'error.log',
level:'error'
})
// TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
winston.err = function(err) {
winston.error(err.stack);
};
// Log GNU copyright info along with server info // Log GNU copyright info along with server info
console.log('Info: NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.'); winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
console.log('Info: This program comes with ABSOLUTELY NO WARRANTY.'); winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
console.log('Info: This is free software, and you are welcome to redistribute it under certain conditions.'); winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
console.log('Info: ==='); winston.info('===');
if(nconf.get('upgrade')) { if(nconf.get('upgrade')) {
require('./src/upgrade').upgrade(); require('./src/upgrade').upgrade();
@ -41,8 +56,8 @@ if(nconf.get('upgrade')) {
nconf.set('upload_url', nconf.get('url') + 'uploads/'); nconf.set('upload_url', nconf.get('url') + 'uploads/');
global.nconf = nconf; global.nconf = nconf;
console.log('Info: Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.'); winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
console.log('Info: Base Configuration OK.'); winston.info('Base Configuration OK.');
// TODO: Replace this with nconf-redis // TODO: Replace this with nconf-redis
var meta = require('./src/meta.js'); var meta = require('./src/meta.js');
@ -84,10 +99,10 @@ if(nconf.get('upgrade')) {
//setup scripts to be moved outside of the app in future. //setup scripts to be moved outside of the app in future.
function setup_categories() { function setup_categories() {
console.log('Info: Checking categories...'); winston.info('Checking categories...');
categories.getAllCategories(function(data) { categories.getAllCategories(function(data) {
if (data.categories.length === 0) { if (data.categories.length === 0) {
console.log('Info: Setting up default categories...'); winston.info('Setting up default categories...');
fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) { fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
default_categories = JSON.parse(default_categories); default_categories = JSON.parse(default_categories);
@ -98,21 +113,24 @@ if(nconf.get('upgrade')) {
}); });
console.log('Info: Hardcoding uid 1 as an admin'); winston.info('Hardcoding uid 1 as an admin');
var user = require('./src/user.js'); var user = require('./src/user.js');
user.makeAdministrator(1); user.makeAdministrator(1);
} else { } else {
console.log('Info: Categories OK. Found ' + data.categories.length + ' categories.'); winston.info('Categories OK. Found ' + data.categories.length + ' categories.');
} }
}); });
} }
setup_categories(); setup_categories();
}(global.configuration)); }(global.configuration));
}); });
} else { } else {
// New install, ask setup questions // New install, ask setup questions
if (nconf.get('setup')) console.log('Info: NodeBB Setup Triggered via Command Line'); if (nconf.get('setup')) winston.info('NodeBB Setup Triggered via Command Line');
else console.log('Info: Configuration not found, starting NodeBB setup'); else winston.info('Configuration not found, starting NodeBB setup');
var install = require('./src/install'); var install = require('./src/install');
@ -124,7 +142,7 @@ if(nconf.get('upgrade')) {
install.setup(function(err) { install.setup(function(err) {
if (err) { if (err) {
console.log('Error: There was a problem completing NodeBB setup: ', err.message); winston.error('There was a problem completing NodeBB setup: ', err.message);
} else { } else {
if (!nconf.get('setup')) { if (!nconf.get('setup')) {
process.stdout.write( process.stdout.write(

@ -2,7 +2,7 @@
"name": "nodebb", "name": "nodebb",
"license": "GPLv3 or later", "license": "GPLv3 or later",
"description": "NodeBB Forum", "description": "NodeBB Forum",
"version": "0.0.4", "version": "0.0.5",
"homepage": "http://www.nodebb.org", "homepage": "http://www.nodebb.org",
"repository": { "repository": {
"type": "git", "type": "git",
@ -33,7 +33,8 @@
"sitemap": "~0.6.0", "sitemap": "~0.6.0",
"cheerio": "~0.12.0", "cheerio": "~0.12.0",
"request": "~2.25.0", "request": "~2.25.0",
"reds": "~0.2.4" "reds": "~0.2.4",
"winston": "~0.7.2"
}, },
"bugs": { "bugs": {
"url": "https://github.com/designcreateplay/NodeBB/issues" "url": "https://github.com/designcreateplay/NodeBB/issues"

@ -197,6 +197,12 @@ footer.footer {
font-weight:bold; font-weight:bold;
} }
.account-block {
div {
padding-bottom:10px;
}
}
.account-picture-block{ .account-picture-block{
display:inline-block; display:inline-block;
vertical-align:top; vertical-align:top;
@ -213,7 +219,6 @@ footer.footer {
.user-profile-picture { .user-profile-picture {
width:128px; width:128px;
margin-bottom:10px;
} }
.user-picture-label { .user-picture-label {
@ -392,7 +397,7 @@ body .navbar .nodebb-inline-block {
#admin-redis-info { #admin-redis-info {
span { span {
display:inline-block; display:inline-block;
width:200px; width:220px;
} }
} }
@ -829,7 +834,7 @@ body .navbar .nodebb-inline-block {
.form-search { .form-search {
float: left; float: left;
margin-top: 5px; margin-top: 5px;
margin-bottom:0px; margin-bottom: 5px;
} }
.search-result-post { .search-result-post {

@ -117,8 +117,6 @@
.topic-title { .topic-title {
width: auto; width: auto;
white-space: nowrap;
text-overflow:ellipsis;
overflow: hidden; overflow: hidden;
margin: 0; margin: 0;
padding: 0; padding: 0;

@ -26,6 +26,7 @@ var ajaxify = {};
}; };
ajaxify.go = function(url, callback, template, quiet) { ajaxify.go = function(url, callback, template, quiet) {
$(window).off('scroll');
// leave room and join global // leave room and join global
app.enter_room('global'); app.enter_room('global');

@ -5,11 +5,12 @@ var socket,
(function() { (function() {
var showWelcomeMessage = false;
function loadConfig() { function loadConfig() {
$.ajax({ $.ajax({
url: RELATIVE_PATH + '/config.json?v=' + new Date().getTime(), url: RELATIVE_PATH + '/api/config',
success: function(data) { success: function(data) {
API_URL = data.api_url; API_URL = data.api_url;
@ -29,10 +30,6 @@ var socket,
app.alert(data); app.alert(data);
}); });
socket.on('event:consolelog', function(data) {
console.log(data);
});
socket.on('connect', function(data){ socket.on('connect', function(data){
if(reconnecting) { if(reconnecting) {
setTimeout(function(){ setTimeout(function(){
@ -286,7 +283,8 @@ var socket,
}); });
} }
if(location.href.indexOf('loggedin') !== -1) { if(showWelcomeMessage) {
showWelcomeMessage = false;
if(document.readyState !== 'complete') { if(document.readyState !== 'complete') {
$(document).ready(showAlert); $(document).ready(showAlert);
} else { } else {
@ -371,6 +369,8 @@ var socket,
}) })
}); });
showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
loadConfig(); loadConfig();

@ -8,6 +8,11 @@
return (parent.attr('data-admin') !== "0"); return (parent.attr('data-admin') !== "0");
} }
function isUserBanned(element) {
var parent = $(element).parents('.users-box');
return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0");
}
function getUID(element) { function getUID(element) {
var parent = $(element).parents('.users-box'); var parent = $(element).parents('.users-box');
return parent.attr('data-uid'); return parent.attr('data-uid');
@ -34,6 +39,20 @@
deleteBtn.show(); deleteBtn.show();
}); });
jQuery('.ban-btn').each(function(index, element) {
var banBtn = $(element);
var isAdmin = isUserAdmin(banBtn);
var isBanned = isUserBanned(banBtn);
if(isAdmin)
banBtn.addClass('disabled');
else if(isBanned)
banBtn.addClass('btn-warning');
else
banBtn.removeClass('btn-warning');
});
jQuery('.admin-btn').on('click', function() { jQuery('.admin-btn').on('click', function() {
var adminBtn = $(this); var adminBtn = $(this);
var isAdmin = isUserAdmin(adminBtn); var isAdmin = isUserAdmin(adminBtn);
@ -74,13 +93,38 @@
return false; return false;
}); });
jQuery('.ban-btn').on('click', function() {
var banBtn = $(this);
var isAdmin = isUserAdmin(banBtn);
var isBanned = isUserBanned(banBtn);
var parent = banBtn.parents('.users-box');
var uid = getUID(banBtn);
if(!isAdmin) {
if(isBanned) {
socket.emit('api:admin.user.unbanUser', uid);
banBtn.removeClass('btn-warning');
parent.attr('data-banned', 0);
} else {
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') +'"?', function(confirm) {
socket.emit('api:admin.user.banUser', uid);
banBtn.addClass('btn-warning');
parent.attr('data-banned', 1);
});
}
}
return false;
});
} }
jQuery('document').ready(function() { jQuery('document').ready(function() {
var yourid = templates.get('yourid'); var yourid = templates.get('yourid'),
var timeoutId = 0; timeoutId = 0,
loadingMoreUsers = false;
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
@ -114,7 +158,6 @@
socket.removeAllListeners('api:admin.user.search'); socket.removeAllListeners('api:admin.user.search');
socket.on('api:admin.user.search', function(data) { socket.on('api:admin.user.search', function(data) {
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
users: data users: data
}), }),
@ -139,6 +182,45 @@
initUsers(); initUsers();
}); });
function onUsersLoaded(users) {
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ users: users });
$('#users-container').append(html);
}
function loadMoreUsers() {
var set = '';
if(active === 'latest') {
set = 'users:joindate';
} else if(active === 'sort-posts') {
set = 'users:postcount';
} else if(active === 'sort-reputation') {
set = 'users:reputation';
}
if(set) {
loadingMoreUsers = true;
socket.emit('api:users.loadMore', {
set: set,
after: $('#users-container').children().length
}, function(data) {
if(data.users.length) {
onUsersLoaded(data.users);
}
loadingMoreUsers = false;
});
}
}
$('#load-more-users-btn').on('click', loadMoreUsers);
$(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreUsers) {
loadMoreUsers();
}
});
}); });
}()); }());

@ -122,10 +122,9 @@
} }
$(window).off('scroll').on('scroll', function(ev) { $(window).off('scroll').on('scroll', function(ev) {
var windowHeight = document.body.offsetHeight - $(window).height(), var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
half = windowHeight / 2;
if (document.body.scrollTop > half && !loadingMoreTopics) { if (document.body.scrollTop > bottom && !loadingMoreTopics) {
loadMoreTopics(cid); loadMoreTopics(cid);
} }
}); });

@ -27,11 +27,15 @@
url: RELATIVE_PATH + '/login', url: RELATIVE_PATH + '/login',
data: loginData, data: loginData,
success: function(data, textStatus, jqXHR) { success: function(data, textStatus, jqXHR) {
if(!data.success) {
$('#login-error-notify').html(data.message).show();
} else {
$('#login-error-notify').hide(); $('#login-error-notify').hide();
window.location.replace(RELATIVE_PATH + "/?loggedin"); window.location.replace(RELATIVE_PATH + "/?loggedin");
}
}, },
error : function(data, textStatus, jqXHR) { error : function(data, textStatus, jqXHR) {
$('#login-error-notify').show().delay(1000).fadeOut(250); $('#login-error-notify').show();
}, },
dataType: 'json', dataType: 'json',
async: true, async: true,

@ -69,10 +69,9 @@
} }
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var windowHeight = document.body.offsetHeight - $(window).height(), var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
half = windowHeight / 2;
if (document.body.scrollTop > half && !loadingMoreTopics) { if (document.body.scrollTop > bottom && !loadingMoreTopics) {
loadMoreTopics(); loadMoreTopics();
} }
}); });

@ -202,10 +202,9 @@
// Infinite scrolling of posts // Infinite scrolling of posts
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var windowHeight = document.body.offsetHeight - $(window).height(), var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
half = windowHeight / 2;
if (document.body.scrollTop > half && !app.infiniteLoaderActive && $('#post-container').children().length) { if (document.body.scrollTop > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
app.loadMorePosts(tid); app.loadMorePosts(tid);
} }
}); });
@ -255,11 +254,9 @@
var element = $(this).find('i'); var element = $(this).find('i');
if(element.attr('class') == 'icon-star-empty') { if(element.attr('class') == 'icon-star-empty') {
element.attr('class', 'icon-star');
socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room}); socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room});
} }
else { else {
element.attr('class', 'icon-star-empty');
socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room}); socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room});
} }
}); });
@ -388,7 +385,14 @@
}); });
socket.on('api:posts.favourite', function(data) { socket.on('api:posts.favourite', function(data) {
if (data.status !== 'ok' && data.pid) { if (data.status === 'ok' && data.pid) {
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
if (favEl) favEl.className = 'icon-star';
}
});
socket.on('api:posts.unfavourite', function(data) {
if (data.status === 'ok' && data.pid) {
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling; var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
if (favEl) favEl.className = 'icon-star-empty'; if (favEl) favEl.className = 'icon-star-empty';
} }

@ -84,10 +84,9 @@
} }
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var windowHeight = document.body.offsetHeight - $(window).height(), var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
half = windowHeight / 2;
if (document.body.scrollTop > half && !loadingMoreTopics) { if (document.body.scrollTop > bottom && !loadingMoreTopics) {
loadMoreTopics(); loadMoreTopics();
} }
}); });

@ -2,6 +2,7 @@
$(document).ready(function() { $(document).ready(function() {
var timeoutId = 0; var timeoutId = 0;
var loadingMoreUsers = false;
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
@ -74,6 +75,46 @@
$(element).html(app.addCommas($(element).html())); $(element).html(app.addCommas($(element).html()));
}); });
function onUsersLoaded(users) {
var html = templates.prepare(templates['users'].blocks['users']).parse({ users: users });
$('#users-container').append(html);
}
function loadMoreUsers() {
var set = '';
if(active === 'users-latest' || active === 'users') {
set = 'users:joindate';
} else if(active === 'users-sort-posts') {
set = 'users:postcount';
} else if(active === 'users-sort-reputation') {
set = 'users:reputation';
}
if(set) {
loadingMoreUsers = true;
socket.emit('api:users.loadMore', {
set: set,
after: $('#users-container').children().length
}, function(data) {
if(data.users.length) {
onUsersLoaded(data.users);
} else {
$('#load-more-users-btn').addClass('disabled');
}
loadingMoreUsers = false;
});
}
}
$('#load-more-users-btn').on('click', loadMoreUsers);
$(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreUsers) {
loadMoreUsers();
}
});
}); });
}()); }());

@ -299,22 +299,22 @@ define(['taskbar'], function(taskbar) {
titleEl.value = titleEl.value.trim(); titleEl.value = titleEl.value.trim();
bodyEl.value = bodyEl.value.trim(); bodyEl.value = bodyEl.value.trim();
if (titleEl.value.length < 3) { if (titleEl.value.length < config.minimumTitleLength) {
return app.alert({ return app.alert({
type: 'error', type: 'error',
timeout: 2000, timeout: 2000,
title: 'Title too short', title: 'Title too short',
message: "Please enter a longer title. At least 3 characters.", message: "Please enter a longer title. At least " + config.minimumTitleLength+ " characters.",
alert_id: 'post_error' alert_id: 'post_error'
}); });
} }
if (bodyEl.value.length < 8) { if (bodyEl.value.length < config.minimumPostLength) {
return app.alert({ return app.alert({
type: 'error', type: 'error',
timeout: 2000, timeout: 2000,
title: 'Content too short', title: 'Content too short',
message: "Please enter a longer post. At least 8 characters.", message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
alert_id: 'post_error' alert_id: 'post_error'
}); });
} }

@ -17,13 +17,16 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="span2" style="text-align: center; margin-bottom:20px;"> <div class="span2 account-block" style="text-align: center; margin-bottom:20px;">
<div class="account-picture-block"> <div class="account-picture-block">
<img src="{picture}" class="user-profile-picture img-polaroid"/> <img src="{picture}" class="user-profile-picture img-polaroid"/>
</div> </div>
<div class="account-online-status"> <div class="account-online-status">
<span><i class="icon-circle-blank"></i> <span>offline</span></span> <span><i class="icon-circle-blank"></i> <span>offline</span></span>
</div> </div>
<div class="{show_banned}">
<span class="label label-important">banned</span>
</div>
<div id="user-actions"> <div id="user-actions">
<a id="follow-btn" href="#" class="btn hide">Follow</a> <a id="follow-btn" href="#" class="btn hide">Follow</a>
<a id="unfollow-btn" href="#" class="btn hide">Unfollow</a> <a id="unfollow-btn" href="#" class="btn hide">Unfollow</a>

@ -16,6 +16,7 @@
<hr/> <hr/>
<span>Total Connections Received</span> <span class="text-right">{total_connections_received}</span><br/> <span>Total Connections Received</span> <span class="text-right">{total_connections_received}</span><br/>
<span>Total Commands Processed</span> <span class="text-right">{total_commands_processed}</span><br/> <span>Total Commands Processed</span> <span class="text-right">{total_commands_processed}</span><br/>
<span>Instantaneous Ops. Per Second</span> <span class="text-right">{instantaneous_ops_per_sec}</span><br/>
<hr/> <hr/>
<span>Keyspace Hits</span> <span class="text-right">{keyspace_hits}</span><br/> <span>Keyspace Hits</span> <span class="text-right">{keyspace_hits}</span><br/>
<span>Keyspace Misses</span> <span class="text-right">{keyspace_misses}</span><br/> <span>Keyspace Misses</span> <span class="text-right">{keyspace_misses}</span><br/>

@ -48,6 +48,16 @@
</div> </div>
</form> </form>
<form>
<h3>Post Settings</h3>
<div class="alert alert-notify">
<strong>Post Delay</strong><br /> <input type="text" class="" value="10000" data-field="postDelay"><br />
<strong>Minimum Title Length</strong><br /> <input type="text" class="" value="3" data-field="minimumTitleLength"><br />
<strong>Minimum Post Length</strong><br /> <input type="text" class="" value="8" data-field="minimumPostLength"><br />
</div>
</form>
<button class="btn btn-large btn-primary" id="save">Save</button> <button class="btn btn-large btn-primary" id="save">Save</button>
<script> <script>

@ -14,9 +14,9 @@
<span id="user-notfound-notify" class="label label-important hide">User not found!</span><br/> <span id="user-notfound-notify" class="label label-important hide">User not found!</span><br/>
</div> </div>
<ul class="users"> <ul id="users-container" class="users">
<!-- BEGIN users --> <!-- BEGIN users -->
<div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}"> <div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}" data-banned="{users.banned}">
<a href="/users/{users.userslug}"> <a href="/users/{users.userslug}">
<img src="{users.picture}" class="img-polaroid"/> <img src="{users.picture}" class="img-polaroid"/>
</a> </a>
@ -37,10 +37,16 @@
<div> <div>
<a href="#" class="btn delete-btn btn-danger">Delete</a> <a href="#" class="btn delete-btn btn-danger">Delete</a>
</div> </div>
<div>
<a href="#" class="btn ban-btn">Ban</a>
</div>
</div> </div>
<!-- END users --> <!-- END users -->
</ul> </ul>
<div class="text-center {loadmore_display}">
<button id="load-more-users-btn" class="btn">Load More</button>
</div>
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />

@ -62,11 +62,12 @@
<li><a href="#"><i class="icon-refresh icon-spin"></i> Loading Notifications</a></li> <li><a href="#"><i class="icon-refresh icon-spin"></i> Loading Notifications</a></li>
</ul> </ul>
</li> </li>
<form id="search-form" <li>
class="form-search form-inline" action="" method="GET"> <form id="search-form" class="form-search form-inline" action="" method="GET">
<input type="text" name="query" class="input-medium search-query"> <input type="text" name="query" class="input-medium search-query">
<button type="submit" class="btn hide">Search</button> <button type="submit" class="btn hide">Search</button>
</form> </form>
</li>
</ul> </ul>
</div> </div>

@ -110,7 +110,6 @@
contentEl.addEventListener('click', function(e) { contentEl.addEventListener('click', function(e) {
if (e.target.hasAttribute('data-path')) { if (e.target.hasAttribute('data-path')) {
var href = 'install/' + e.target.getAttribute('data-path'); var href = 'install/' + e.target.getAttribute('data-path');
console.log(href);
if (!e.target.disabled) ajaxify.go(href); if (!e.target.disabled) ajaxify.go(href);
} }
}, false); }, false);

@ -14,7 +14,7 @@
<button class="btn btn-primary" id="login" type="submit">Login</button> &nbsp; <a href="/reset">Forgot Password?</a> <button class="btn btn-primary" id="login" type="submit">Login</button> &nbsp; <a href="/reset">Forgot Password?</a>
</form> </form>
<span id="login-error-notify" class="label label-important hide">Invalid username/password</span><br/> <div id="login-error-notify" class="alert alert-danger hide">Invalid username/password</div>
</div> </div>
<div class="well span6 {alternate_logins:display}"> <div class="well span6 {alternate_logins:display}">

@ -89,6 +89,7 @@
<i class="icon-star"></i><span class="user_rep_{posts.uid} formatted-number">{posts.user_rep}</span> <i class="icon-star"></i><span class="user_rep_{posts.uid} formatted-number">{posts.user_rep}</span>
<div id="ids_{posts.pid}_{posts.uid}" class="chat hidden-phone" title="Chat"><i class="icon-comment"></i></div> <div id="ids_{posts.pid}_{posts.uid}" class="chat hidden-phone" title="Chat"><i class="icon-comment"></i></div>
</div> </div>
<span class="label label-important {posts.show_banned}">banned</span>
</div> </div>
<div class="span11 span12-tablet"> <div class="span11 span12-tablet">
<div class="post-block"> <div class="post-block">

@ -14,7 +14,7 @@
<span id="user-notfound-notify" class="label label-important hide">User not found!</span><br/> <span id="user-notfound-notify" class="label label-important hide">User not found!</span><br/>
</div> </div>
<ul class="users"> <ul id="users-container" class="users">
<!-- BEGIN users --> <!-- BEGIN users -->
<div class="users-box"> <div class="users-box">
<a href="/users/{users.userslug}"> <a href="/users/{users.userslug}">
@ -36,4 +36,8 @@
</ul> </ul>
</div> </div>
<div class="text-center {loadmore_display}">
<button id="load-more-users-btn" class="btn">Load More</button>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/users.js"></script> <script type="text/javascript" src="{relative_path}/src/forum/users.js"></script>

@ -58,8 +58,39 @@ var RDB = require('./../redis.js'),
}); });
} }
}); });
});
};
UserAdmin.banUser = function(uid, theirid, socket) {
user.isAdministrator(uid, function(amIAdmin) {
user.isAdministrator(theirid, function(areTheyAdmin) {
if(amIAdmin && !areTheyAdmin) {
user.ban(theirid, function(err, result) {
socket.emit('event:alert', {
title: 'User Banned',
message: 'This user is banned!',
type: 'success',
timeout: 2000
});
});
}
});
});
};
UserAdmin.unbanUser = function(uid, theirid, socket) {
user.isAdministrator(uid, function(amIAdmin) {
if(amIAdmin) {
user.unban(theirid, function(err, result) {
socket.emit('event:alert', {
title: 'User Unbanned',
message: 'This user is unbanned!',
type: 'success',
timeout: 2000
});
});
}
}); });
}; };

@ -3,7 +3,8 @@ var RDB = require('./redis.js'),
utils = require('./../public/src/utils.js'), utils = require('./../public/src/utils.js'),
user = require('./user.js'), user = require('./user.js'),
async = require('async'), async = require('async'),
topics = require('./topics.js'); topics = require('./topics.js'),
winston = require('winston');
(function(Categories) { (function(Categories) {
@ -65,7 +66,6 @@ var RDB = require('./redis.js'),
categoryData.moderators = moderators; categoryData.moderators = moderators;
categoryData.show_sidebar = 'hidden'; categoryData.show_sidebar = 'hidden';
categoryData.no_topics_message = 'show'; categoryData.no_topics_message = 'show';
callback(null, categoryData); callback(null, categoryData);
}); });
} else { } else {
@ -187,8 +187,9 @@ var RDB = require('./redis.js'),
Categories.getRecentReplies = function(cid, count, callback) { Categories.getRecentReplies = function(cid, count, callback) {
RDB.zrevrange('categories:recent_posts:cid:' + cid, 0, (count<10)?10:count, function(err, pids) { RDB.zrevrange('categories:recent_posts:cid:' + cid, 0, (count<10)?10:count, function(err, pids) {
if(err) { if(err) {
console.log(err); winston.err(err);
callback([]); callback([]);
return; return;
} }
@ -198,11 +199,11 @@ var RDB = require('./redis.js'),
return; return;
} }
posts.getPostSummaryByPids(pids, function(posts) { posts.getPostSummaryByPids(pids, function(postData) {
if(posts.length > count) { if(postData.length > count) {
posts = posts.slice(0, count); postData = postData.slice(0, count);
} }
callback(posts); callback(postData);
}); });
}); });
} }
@ -222,12 +223,12 @@ var RDB = require('./redis.js'),
if(!err) { if(!err) {
callback(null, 1) callback(null, 1)
} else { } else {
console.log(err); winston.err(err);
callback(err, null); callback(err, null);
} }
}); });
} else { } else {
console.log(err); winston.err(err);
callback(err, null); callback(err, null);
} }
}); });
@ -249,7 +250,7 @@ var RDB = require('./redis.js'),
if(err === null) if(err === null)
callback(data); callback(data);
else else
console.log(err); winston.err(err);
}); });
} }
@ -288,7 +289,7 @@ var RDB = require('./redis.js'),
async.eachSeries(cids, getCategory, function(err) { async.eachSeries(cids, getCategory, function(err) {
if(err) { if(err) {
console.log(err); winston.err(err);
callback(null); callback(null);
return; return;
} }

@ -13,11 +13,6 @@ var RDB = require('./redis.js'),
type: 'error', type: 'error',
timeout: 5000 timeout: 5000
}); });
socket.emit('api:posts.favourite', {
status: 'error',
pid: pid
});
return; return;
} }
@ -28,15 +23,19 @@ var RDB = require('./redis.js'),
RDB.sadd('pid:' + pid + ':users_favourited', uid); RDB.sadd('pid:' + pid + ':users_favourited', uid);
RDB.hincrby('post:' + pid, 'reputation', 1); RDB.hincrby('post:' + pid, 'reputation', 1);
if (uid !== uid_of_poster) if (uid !== uid_of_poster) {
user.incrementUserFieldBy(uid_of_poster, 'reputation', 1); user.incrementUserFieldBy(uid_of_poster, 'reputation', 1, function(err, newreputation) {
RDB.zadd('users:reputation', newreputation, uid_of_poster);
});
}
if (room_id) { if (room_id) {
io.sockets.in(room_id).emit('event:rep_up', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid}); io.sockets.in(room_id).emit('event:rep_up', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
} }
socket.emit('api:posts.favourite', { socket.emit('api:posts.favourite', {
status: 'ok' status: 'ok',
pid: pid
}); });
} }
}); });
@ -63,12 +62,20 @@ var RDB = require('./redis.js'),
RDB.srem('pid:' + pid + ':users_favourited', uid); RDB.srem('pid:' + pid + ':users_favourited', uid);
RDB.hincrby('post:' + pid, 'reputation', -1); RDB.hincrby('post:' + pid, 'reputation', -1);
if (uid !== uid_of_poster) if (uid !== uid_of_poster) {
user.incrementUserFieldBy(uid_of_poster, 'reputation', -1); user.incrementUserFieldBy(uid_of_poster, 'reputation', -1, function(err, newreputation) {
RDB.zadd('users:reputation', newreputation, uid_of_poster);
});
}
if (room_id) { if (room_id) {
io.sockets.in(room_id).emit('event:rep_down', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid}); io.sockets.in(room_id).emit('event:rep_down', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
} }
socket.emit('api:posts.unfavourite', {
status: 'ok',
pid: pid
});
} }
}); });
}); });

@ -4,12 +4,13 @@
posts = require('./posts.js'), posts = require('./posts.js'),
topics = require('./topics.js'), topics = require('./topics.js'),
fs = require('fs'), fs = require('fs'),
rss = require('node-rss'); rss = require('node-rss'),
winston = require('winston');
function saveFeed(location, feed) { function saveFeed(location, feed) {
fs.writeFile(location, rss.getFeedXML(feed), function (err) { fs.writeFile(location, rss.getFeedXML(feed), function (err) {
if(err) { if(err) {
console.log(err); winston.err(err);
} }
}); });
} }
@ -32,7 +33,7 @@
var cache_time_in_seconds = 60; var cache_time_in_seconds = 60;
topics.getTopicWithPosts(tid, 0, function(err, topicData) { topics.getTopicWithPosts(tid, 0, function(err, topicData) {
if (err) console.log('Error: Problem saving topic RSS feed', err); if (err) winston.error('Problem saving topic RSS feed', err.stack);
var location = '/topic/' + topicData.slug, var location = '/topic/' + topicData.slug,
xml_url = '/topic/' + tid + '.rss'; xml_url = '/topic/' + tid + '.rss';
@ -69,7 +70,7 @@
Feed.updateCategory = function(cid) { Feed.updateCategory = function(cid) {
categories.getCategoryById(cid, 0, function(err, categoryData) { categories.getCategoryById(cid, 0, function(err, categoryData) {
if (err) { if (err) {
console.log('Error: Could not update RSS feed for category ' + cid); winston.error('Could not update RSS feed for category ' + cid, err.stack);
return; return;
} }

@ -3,6 +3,7 @@ var async = require('async'),
fs = require('fs'), fs = require('fs'),
url = require('url'), url = require('url'),
path = require('path'), path = require('path'),
meta = require('./meta'),
install = { install = {
questions: [ questions: [
'base_url|Publically accessible URL of this installation? (http://localhost)', 'base_url|Publically accessible URL of this installation? (http://localhost)',
@ -85,6 +86,10 @@ var async = require('async'),
server_conf.relative_path = relative_path; server_conf.relative_path = relative_path;
server_conf.imgurClientID = ''; server_conf.imgurClientID = '';
meta.config.set('postDelay', 10000);
meta.config.set('minimumPostLength', 8);
meta.config.set('minimumTitleLength', 3);
install.save(server_conf, client_conf, callback); install.save(server_conf, client_conf, callback);
}); });
}, },

@ -2,7 +2,8 @@
var user = require('./user.js'), var user = require('./user.js'),
bcrypt = require('bcrypt'), bcrypt = require('bcrypt'),
RDB = require('./redis.js'), RDB = require('./redis.js'),
path = require('path'); path = require('path'),
winston = require('winston');
(function(Login){ (function(Login){
@ -24,10 +25,16 @@ var user = require('./user.js'),
}); });
} }
user.getUserField(uid, 'password', function(user_password) { user.getUserFields(uid, ['password', 'banned'], function(userData) {
bcrypt.compare(password, user_password, function(err, res) { if(userData.banned && userData.banned === '1') {
return next({
status: "error",
message: "user-banned"
});
}
bcrypt.compare(password, userData.password, function(err, res) {
if(err) { if(err) {
console.log(err); winston.err(err);
next({ next({
status: "error", status: "error",
message: 'bcrypt compare error' message: 'bcrypt compare error'

@ -9,6 +9,7 @@ var utils = require('./../public/src/utils.js'),
get: function(callback) { get: function(callback) {
RDB.hgetall('config', function(err, config) { RDB.hgetall('config', function(err, config) {
if (!err) { if (!err) {
config = config || {};
config.status = 'ok'; config.status = 'ok';
callback(config); callback(config);
} else { } else {
@ -18,9 +19,13 @@ var utils = require('./../public/src/utils.js'),
} }
}); });
}, },
getFields: function(fields, callback) {
RDB.hmgetObject('config', fields, callback);
},
set: function(field, value, callback) { set: function(field, value, callback) {
RDB.hset('config', field, value, function(err, res) { RDB.hset('config', field, value, function(err, res) {
callback(err); if(callback)
callback(err, res);
}); });
}, },
remove: function(field) { remove: function(field) {
@ -76,7 +81,7 @@ var utils = require('./../public/src/utils.js'),
var title; var title;
if (err) title = global.config.title || 'NodeBB'; if (err) title = global.config.title || 'NodeBB';
else title = (values.notifCount > 0 ? '(' + values.notifCount + ') ' : '') + (values.title ? values.title + ' | ' : '') + global.config.title || 'NodeBB'; else title = (values.notifCount > 0 ? '(' + values.notifCount + ') ' : '') + (values.title ? values.title + ' | ' : '') + (global.config.title || 'NodeBB');
callback(null, title); callback(null, title);
}); });

@ -2,12 +2,13 @@ var fs = require('fs'),
path = require('path'), path = require('path'),
RDB = require('./redis.js'), RDB = require('./redis.js'),
async = require('async'), async = require('async'),
winston = require('winston'),
plugins = { plugins = {
libraries: [], libraries: [],
loadedHooks: {}, loadedHooks: {},
init: function() { init: function() {
if (this.initialized) return; if (this.initialized) return;
if (global.env === 'development') console.log('Info: [plugins] Initializing plugins system'); if (global.env === 'development') winston.info('[plugins] Initializing plugins system');
var _self = this; var _self = this;
@ -32,12 +33,12 @@ var fs = require('fs'),
_self.registerHook(pluginData.id, pluginData.hooks[x]); _self.registerHook(pluginData.id, pluginData.hooks[x]);
} }
} }
if (global.env === 'development') console.log('Info: [plugins] Loaded plugin: ' + pluginData.id); if (global.env === 'development') winston.info('[plugins] Loaded plugin: ' + pluginData.id);
next(); next();
}); });
} else { } else {
if (global.env === 'development') console.log('Info: [plugins] Plugin \'' + plugin + '\' not found'); if (global.env === 'development') winston.info('[plugins] Plugin \'' + plugin + '\' not found');
next(); // Ignore this plugin silently next(); // Ignore this plugin silently
} }
}) })
@ -45,11 +46,11 @@ var fs = require('fs'),
} }
], function(err) { ], function(err) {
if (err) { if (err) {
if (global.env === 'development') console.log('Info: [plugins] NodeBB encountered a problem while loading plugins', err.message); if (global.env === 'development') winston.info('[plugins] NodeBB encountered a problem while loading plugins', err.message);
return; return;
} }
if (global.env === 'development') console.log('Info: [plugins] Plugins OK'); if (global.env === 'development') winston.info('[plugins] Plugins OK');
}); });
}, },
initialized: false, initialized: false,
@ -66,7 +67,7 @@ var fs = require('fs'),
if (data.hook && data.method) { if (data.hook && data.method) {
_self.loadedHooks[data.hook] = _self.loadedHooks[data.hook] || []; _self.loadedHooks[data.hook] = _self.loadedHooks[data.hook] || [];
_self.loadedHooks[data.hook].push([id, data.method]); _self.loadedHooks[data.hook].push([id, data.method]);
if (global.env === 'development') console.log('Info: [plugins] Hook registered: ' + data.hook + ' will call ' + id); if (global.env === 'development') winston.info('[plugins] Hook registered: ' + data.hook + ' will call ' + id);
} else return; } else return;
}, },
fireHook: function(hook, args, callback) { fireHook: function(hook, args, callback) {
@ -75,7 +76,7 @@ var fs = require('fs'),
hookList = this.loadedHooks[hook]; hookList = this.loadedHooks[hook];
if (hookList && Array.isArray(hookList)) { if (hookList && Array.isArray(hookList)) {
if (global.env === 'development') console.log('Info: [plugins] Firing hook: \'' + hook + '\''); if (global.env === 'development') winston.info('[plugins] Firing hook: \'' + hook + '\'');
var hookType = hook.split(':')[0]; var hookType = hook.split(':')[0];
switch(hookType) { switch(hookType) {
case 'filter': case 'filter':
@ -94,7 +95,7 @@ var fs = require('fs'),
} }
}, function(err) { }, function(err) {
if (err) { if (err) {
if (global.env === 'development') console.log('Info: [plugins] Problem executing hook: ' + hook); if (global.env === 'development') winston.info('[plugins] Problem executing hook: ' + hook);
} }
callback(returnVal); callback(returnVal);
@ -109,7 +110,7 @@ var fs = require('fs'),
) { ) {
_self.libraries[hookObj[0]][hookObj[1]].apply(_self.libraries[hookObj[0]], args); _self.libraries[hookObj[0]][hookObj[1]].apply(_self.libraries[hookObj[0]], args);
} else { } else {
if (global.env === 'development') console.log('Info: [plugins] Expected method \'' + hookObj[1] + '\' in plugin \'' + hookObj[0] + '\' not found, skipping.'); if (global.env === 'development') winston.info('[plugins] Expected method \'' + hookObj[1] + '\' in plugin \'' + hookObj[0] + '\' not found, skipping.');
} }
}); });
break; break;
@ -129,13 +130,13 @@ var fs = require('fs'),
toggleActive: function(id, callback) { toggleActive: function(id, callback) {
this.isActive(id, function(err, active) { this.isActive(id, function(err, active) {
if (err) { if (err) {
if (global.env === 'development') console.log('Info: [plugins] Could not toggle active state on plugin \'' + id + '\''); if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
return; return;
} }
RDB[(active ? 'srem' : 'sadd')]('plugins:active', id, function(err, success) { RDB[(active ? 'srem' : 'sadd')]('plugins:active', id, function(err, success) {
if (err) { if (err) {
if (global.env === 'development') console.log('Info: [plugins] Could not toggle active state on plugin \'' + id + '\''); if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
return; return;
} }

@ -9,7 +9,8 @@ var RDB = require('./redis.js'),
plugins = require('./plugins'), plugins = require('./plugins'),
reds = require('reds'), reds = require('reds'),
postSearch = reds.createSearch('nodebbpostsearch'), postSearch = reds.createSearch('nodebbpostsearch'),
topicSearch = reds.createSearch('nodebbtopicsearch'); topicSearch = reds.createSearch('nodebbtopicsearch'),
winston = require('winston');
(function(PostTools) { (function(PostTools) {
PostTools.isMain = function(pid, tid, callback) { PostTools.isMain = function(pid, tid, callback) {
@ -99,7 +100,9 @@ var RDB = require('./redis.js'),
posts.getPostFields(pid, ['tid', 'uid'], function(postData) { posts.getPostFields(pid, ['tid', 'uid'], function(postData) {
user.decrementUserFieldBy(postData.uid, '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', { io.sockets.in('topic_' + postData.tid).emit('event:post_deleted', {
pid: pid pid: pid
@ -109,7 +112,7 @@ var RDB = require('./redis.js'),
threadTools.get_latest_undeleted_pid(postData.tid, function(err, pid) { threadTools.get_latest_undeleted_pid(postData.tid, function(err, pid) {
if (err && err.message === 'no-undeleted-pids-found') { if (err && err.message === 'no-undeleted-pids-found') {
threadTools.delete(postData.tid, -1, function(err) { threadTools.delete(postData.tid, -1, function(err) {
if (err) console.log('Error: Could not delete topic (tid: ' + postData.tid + ')'); if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
}); });
} else { } else {
posts.getPostField(pid, 'timestamp', function(timestamp) { posts.getPostField(pid, 'timestamp', function(timestamp) {

@ -10,12 +10,12 @@ var RDB = require('./redis.js'),
async = require('async'), async = require('async'),
plugins = require('./plugins'), plugins = require('./plugins'),
reds = require('reds'), reds = require('reds'),
postSearch = reds.createSearch('nodebbpostsearch'); nconf = require('nconf'),
postSearch = reds.createSearch('nodebbpostsearch'),
winston = require('winston');
(function(Posts) { (function(Posts) {
Posts.minimumPostLength = 8;
Posts.getPostsByTid = function(tid, start, end, callback) { Posts.getPostsByTid = function(tid, start, end, callback) {
RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) { RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
@ -32,12 +32,13 @@ var RDB = require('./redis.js'),
} }
Posts.addUserInfoToPost = function(post, callback) { Posts.addUserInfoToPost = function(post, callback) {
user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature'], function(userData) { user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(userData) {
post.username = userData.username || 'anonymous'; post.username = userData.username || 'anonymous';
post.userslug = userData.userslug || ''; post.userslug = userData.userslug || '';
post.user_rep = userData.reputation || 0; post.user_rep = userData.reputation || 0;
post.user_postcount = userData.postcount || 0; post.user_postcount = userData.postcount || 0;
post.user_banned = userData.banned || '0';
post.picture = userData.picture || require('gravatar').url('', {}, https=global.nconf.get('https')); post.picture = userData.picture || require('gravatar').url('', {}, https=global.nconf.get('https'));
post.signature = postTools.markdownToHTML(userData.signature, true); post.signature = postTools.markdownToHTML(userData.signature, true);
@ -55,7 +56,7 @@ var RDB = require('./redis.js'),
Posts.getPostSummaryByPids = function(pids, callback) { Posts.getPostSummaryByPids = function(pids, callback) {
var returnData = []; var posts = [];
function getPostSummary(pid, callback) { function getPostSummary(pid, callback) {
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) { Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) {
@ -70,7 +71,7 @@ var RDB = require('./redis.js'),
postData.content = utils.strip_tags(postTools.markdownToHTML(postData.content)); postData.content = utils.strip_tags(postTools.markdownToHTML(postData.content));
postData.topicSlug = topicSlug; postData.topicSlug = topicSlug;
returnData.push(postData); posts.push(postData);
callback(null); callback(null);
}); });
}); });
@ -80,13 +81,19 @@ var RDB = require('./redis.js'),
async.eachSeries(pids, getPostSummary, function(err) { async.eachSeries(pids, getPostSummary, function(err) {
if(!err) { if(!err) {
callback(returnData); callback(posts);
} else { } else {
console.log(err); console.log(err);
} }
}); });
}; };
Posts.filterBannedPosts = function(posts) {
return posts.filter(function(post) {
return post.user_banned === '0';
});
}
Posts.getPostData = function(pid, callback) { Posts.getPostData = function(pid, callback) {
RDB.hgetall('post:' + pid, function(err, data) { RDB.hgetall('post:' + pid, function(err, data) {
if(err === null) { if(err === null) {
@ -174,7 +181,7 @@ var RDB = require('./redis.js'),
type: 'error', type: 'error',
timeout: 2000, timeout: 2000,
title: 'Content too short', title: 'Content too short',
message: "Please enter a longer post. At least " + Posts.minimumPostLength + " characters.", message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
alert_id: 'post_error' alert_id: 'post_error'
}); });
} }
@ -182,7 +189,7 @@ var RDB = require('./redis.js'),
Posts.emitTooManyPostsAlert = function(socket) { Posts.emitTooManyPostsAlert = function(socket) {
socket.emit('event:alert', { socket.emit('event:alert', {
title: 'Too many posts!', title: 'Too many posts!',
message: 'You can only post every '+ (config.post_delay / 1000) + ' seconds.', message: 'You can only post every '+ config.postDelay/1000 + ' seconds.',
type: 'error', type: 'error',
timeout: 2000 timeout: 2000
}); });
@ -193,14 +200,13 @@ var RDB = require('./redis.js'),
content = content.trim(); content = content.trim();
} }
if (!content || content.length < Posts.minimumPostLength) { if (!content || content.length < config.minimumPostLength) {
callback(new Error('content-too-short'), null); callback(new Error('content-too-short'), null);
return; return;
} }
user.getUserField(uid, 'lastposttime', function(lastposttime) { user.getUserField(uid, 'lastposttime', function(lastposttime) {
if(Date.now() - lastposttime < config.postDelay) {
if(Date.now() - lastposttime < config.post_delay) {
callback(new Error('too-many-posts'), null); callback(new Error('too-many-posts'), null);
return; return;
} }
@ -300,7 +306,7 @@ var RDB = require('./redis.js'),
uploadPostImages(postData, images, function(err, uploadedImages) { uploadPostImages(postData, images, function(err, uploadedImages) {
if(err) { if(err) {
console.log('Uploading images failed!'); winston.error('Uploading images failed!', err.stack);
} else { } else {
postData.uploadedImages = JSON.stringify(uploadedImages); postData.uploadedImages = JSON.stringify(uploadedImages);
Posts.setPostField(pid, 'uploadedImages', postData.uploadedImages); Posts.setPostField(pid, 'uploadedImages', postData.uploadedImages);

@ -1,7 +1,8 @@
(function(RedisDB) { (function(RedisDB) {
var redis = require('redis'), var redis = require('redis'),
nconf = require('nconf'), nconf = require('nconf'),
utils = require('./../public/src/utils.js'); utils = require('./../public/src/utils.js'),
winston = require('winston');
RedisDB.exports = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host')); RedisDB.exports = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'));
@ -11,14 +12,9 @@
RedisDB.exports.handle = function(error) { RedisDB.exports.handle = function(error) {
if (error !== null) { if (error !== null) {
winston.err(error);
if (global.env !== 'production') { if (global.env !== 'production') {
console.log("################# ERROR LOG ####################");
console.log(error);
console.log(arguments.callee.name);
console.log("################# ERROR LOG ####################");
throw new Error(error); throw new Error(error);
} else {
console.log(error);
} }
} }
} }

@ -4,7 +4,8 @@ var user = require('./../user.js'),
RDB = require('./../redis.js'), RDB = require('./../redis.js'),
pkg = require('./../../package.json'), pkg = require('./../../package.json'),
categories = require('./../categories.js'), categories = require('./../categories.js'),
plugins = require('../plugins'); plugins = require('../plugins'),
winston = require('winston');
(function(Admin) { (function(Admin) {
Admin.isAdmin = function(req, res, next) { Admin.isAdmin = function(req, res, next) {
@ -64,38 +65,29 @@ var user = require('./../user.js'),
}); });
app.get('/api/admin/users/search', function(req, res) { app.get('/api/admin/users/search', function(req, res) {
res.json({search_display: 'block', users: []}); res.json({search_display: 'block', loadmore_display:'none', users: []});
}); });
app.get('/api/admin/users/latest', function(req, res) { app.get('/api/admin/users/latest', function(req, res) {
user.getUserList(function(data) { user.getUsers('users:joindate', 0, 49, function(err, data) {
data = data.sort(function(a, b) { res.json({ search_display: 'none', loadmore_display:'block', users:data, yourid:req.user.uid });
return b.joindate - a.joindate;
});
res.json({search_display: 'none', users:data, yourid:req.user.uid});
}); });
}); });
app.get('/api/admin/users/sort-posts', function(req, res) { app.get('/api/admin/users/sort-posts', function(req, res) {
user.getUserList(function(data) { user.getUsers('users:postcount', 0, 49, function(err, data) {
data = data.sort(function(a, b) { res.json({ search_display: 'none', loadmore_display:'block', users:data, yourid:req.user.uid });
return b.postcount - a.postcount;
});
res.json({search_display: 'none', users:data, yourid:req.user.uid});
}); });
}); });
app.get('/api/admin/users/sort-reputation', function(req, res) { app.get('/api/admin/users/sort-reputation', function(req, res) {
user.getUserList(function(data) { user.getUsers('users:reputation', 0, 49, function(err, data) {
data = data.sort(function(a, b) { res.json({ search_display: 'none', loadmore_display:'block', users:data, yourid:req.user.uid });
return b.reputation - a.reputation;
});
res.json({search_display: 'none', users:data, yourid:req.user.uid});
}); });
}); });
app.get('/api/admin/users', function(req, res) { app.get('/api/admin/users', function(req, res) {
user.getUserList(function(data) { user.getUsers('users:joindate', 0, 49, function(err, data) {
res.json({ search_display: 'none', users:data, yourid:req.user.uid }); res.json({ search_display: 'none', users:data, yourid:req.user.uid });
}); });
}); });
@ -139,6 +131,9 @@ var user = require('./../user.js'),
for(var i in data) { for(var i in data) {
if(data[i].indexOf(':') == -1 || !data[i])
continue;
try { try {
data[i] = data[i].replace(/:/,"\":\""); data[i] = data[i].replace(/:/,"\":\"");
var json = "{\"" + data[i] + "\"}"; var json = "{\"" + data[i] + "\"}";
@ -148,7 +143,7 @@ var user = require('./../user.js'),
finalData[key] = jsonObject[key]; finalData[key] = jsonObject[key];
} }
} catch(err){ } catch(err){
console.log(err); winston.warn('can\'t parse redis status variable, ignoring', i, data[i], err);
} }
} }

@ -3,7 +3,8 @@ var user = require('./../user.js'),
topics = require('./../topics.js'), topics = require('./../topics.js'),
categories = require('./../categories.js') categories = require('./../categories.js')
utils = require('./../../public/src/utils.js'), utils = require('./../../public/src/utils.js'),
pkg = require('../../package.json'); pkg = require('../../package.json'),
meta = require('./../meta.js');
(function(Api) { (function(Api) {
@ -14,6 +15,19 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/config', function(req, res, next) {
meta.config.getFields(['postDelay', 'minimumTitleLength', 'minimumPostLength'], function(err, metaConfig) {
if(err) return next();
var clientConfig = require('../../public/config.json');
for (var attrname in metaConfig) {
clientConfig[attrname] = metaConfig[attrname];
}
res.json(200, clientConfig);
})
});
app.get('/api/home', function(req, res) { app.get('/api/home', function(req, res) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
categories.getAllCategories(function(data) { categories.getAllCategories(function(data) {
@ -187,7 +201,7 @@ var user = require('./../user.js'),
search(topicSearch, function(err, tids) { search(topicSearch, function(err, tids) {
if(err) if(err)
return callback(err, null); return callback(err, null);
console.log(tids);
topics.getTopicsByTids(tids, 0, function(topics) { topics.getTopicsByTids(tids, 0, function(topics) {
callback(null, topics); callback(null, topics);
}, 0); }, 0);

@ -6,7 +6,8 @@
passportFacebook = require('passport-facebook').Strategy, passportFacebook = require('passport-facebook').Strategy,
login_strategies = [], login_strategies = [],
nconf = require('nconf'), nconf = require('nconf'),
users = require('../user'), user = require('../user'),
winston = require('winston'),
login_module = require('./../login.js'); login_module = require('./../login.js');
passport.use(new passportLocal(function(user, password, next) { passport.use(new passportLocal(function(user, password, next) {
@ -85,7 +86,7 @@
app.get('/logout', function(req, res) { app.get('/logout', function(req, res) {
if (req.user && req.user.uid > 0) { if (req.user && req.user.uid > 0) {
console.log('info: [Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')'); winston.info('[Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')');
login_module.logout(req.sessionID, function(logout) { login_module.logout(req.sessionID, function(logout) {
req.logout(); req.logout();
app.build_header({ req: req, res: res }, function(err, header) { app.build_header({ req: req, res: res }, function(err, header) {
@ -132,20 +133,30 @@
app.get('/reset', function(req, res) { app.get('/reset', function(req, res) {
app.build_header({ req: req, res: res }, function(err, header) { app.build_header({ req: req, res: res }, function(err, header) {
console.log(header);
res.send(header + templates['reset'] + templates['footer']); res.send(header + templates['reset'] + templates['footer']);
}); });
}); });
app.post('/login', function(req, res, next) {
app.post('/login', passport.authenticate('local'), function(req, res) { passport.authenticate('local', function(err, user, info) {
res.json({success:1}); if(err) {
return next(err);
}
if (!user) {
return res.send({ success : false, message : info.message });
}
req.login({
uid: user.uid
}, function() {
res.send({ success : true, message : 'authentication succeeded' });
});
})(req, res, next);
}); });
app.post('/register', function(req, res) { app.post('/register', function(req, res) {
users.create(req.body.username, req.body.password, req.body.email, function(err, uid) { user.create(req.body.username, req.body.password, req.body.email, function(err, uid) {
if (err === null && uid > 0) { if (err === null && uid) {
req.login({ req.login({
uid: uid uid: uid
}, function() { }, function() {

@ -4,7 +4,8 @@ var user = require('./../user.js'),
fs = require('fs'), fs = require('fs'),
utils = require('./../../public/src/utils.js'), utils = require('./../../public/src/utils.js'),
path = require('path'), path = require('path'),
marked = require('marked'); marked = require('marked'),
winston = require('winston');
(function(User) { (function(User) {
User.create_routes = function(app) { User.create_routes = function(app) {
@ -63,13 +64,13 @@ var user = require('./../user.js'),
user.get_uid_by_userslug(req.params.userslug, function(uid) { user.get_uid_by_userslug(req.params.userslug, function(uid) {
if(!uid) { if(!uid) {
next(); return next();
return;
} }
app.build_header({ req: req, res: res }, function(err, header) { app.build_header({ req: req, res: res }, function(err, header) {
res.send(header + app.create_route('users/' + req.params.userslug, 'account') + templates['footer']); res.send(header + app.create_route('users/' + req.params.userslug, 'account') + templates['footer']);
}); });
}); });
}); });
@ -136,7 +137,7 @@ var user = require('./../user.js'),
fs.unlink(absolutePath, function(err) { fs.unlink(absolutePath, function(err) {
if(err) { if(err) {
console.error('[%d] %s', Date.now(), + err); winston.error('[%d] %s', Date.now(), + err);
} }
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res); uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
@ -155,8 +156,7 @@ var user = require('./../user.js'),
var filename = uid + '-profileimg' + extension; var filename = uid + '-profileimg' + extension;
var uploadPath = path.join(global.configuration['ROOT_DIRECTORY'], global.nconf.get('upload_path'), filename); var uploadPath = path.join(global.configuration['ROOT_DIRECTORY'], global.nconf.get('upload_path'), filename);
// @todo move to proper logging code - this should only be temporary winston.info('Attempting upload to: '+ uploadPath);
console.log('Info: Attempting upload to: '+ uploadPath);
var is = fs.createReadStream(tempPath); var is = fs.createReadStream(tempPath);
var os = fs.createWriteStream(uploadPath); var os = fs.createWriteStream(uploadPath);
@ -176,11 +176,7 @@ var user = require('./../user.js'),
height: 128 height: 128
}, function(err, stdout, stderr){ }, function(err, stdout, stderr){
if (err) { if (err) {
// @todo: better logging method; for now, send to stderr. winston.err(err.message, err.stack);
// ideally, this should be happening in another process
// to avoid poisoning the main process on error or allowing a significant problem
// to crash the main process
console.error('[%d] %s', Date.now(), + err);
} }
res.json({ path: imageUrl }); res.json({ path: imageUrl });
@ -189,7 +185,7 @@ var user = require('./../user.js'),
}); });
os.on('error', function(err) { os.on('error', function(err) {
console.error('[%d] %s', Date.now(), + err); winston.error('[%d] %s', Date.now(), + err);
}); });
is.pipe(os); is.pipe(os);
@ -316,49 +312,33 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/users', function(req, res) { app.get('/api/users', getUsersSortedByJoinDate);
user.getUserList(function(data) {
data = data.sort(function(a, b) {
return b.joindate - a.joindate;
});
res.json({ search_display: 'none', users: data });
});
});
app.get('/api/users-sort-posts', getUsersSortedByPosts); app.get('/api/users-sort-posts', getUsersSortedByPosts);
app.get('/api/users-sort-reputation', getUsersSortedByReputation); app.get('/api/users-sort-reputation', getUsersSortedByReputation);
app.get('/api/users-latest', getUsersSortedByJoinDate); app.get('/api/users-latest', getUsersSortedByJoinDate);
app.get('/api/users-search', getUsersForSearch); app.get('/api/users-search', getUsersForSearch);
function getUsersSortedByPosts(req, res) {
user.getUserList(function(data) { function getUsersSortedByJoinDate(req, res) {
data = data.sort(function(a, b) { user.getUsers('users:joindate', 0, 49, function(err, data) {
return b.postcount - a.postcount; res.json({ search_display: 'none', loadmore_display:'block', users:data });
});
res.json({ search_display: 'none', users:data });
}); });
} }
function getUsersSortedByReputation(req, res) { function getUsersSortedByPosts(req, res) {
user.getUserList(function(data) { user.getUsers('users:postcount', 0, 49, function(err, data) {
data = data.sort(function(a, b) { res.json({ search_display: 'none', loadmore_display:'block', users:data });
return b.reputation - a.reputation;
});
res.json({ search_display: 'none', users:data });
}); });
} }
function getUsersSortedByJoinDate(req, res) { function getUsersSortedByReputation(req, res) {
user.getUserList(function(data) { user.getUsers('users:reputation', 0, 49, function(err, data) {
data = data.sort(function(a, b) { res.json({ search_display: 'none', loadmore_display:'block', users:data });
return b.joindate - a.joindate;
});
res.json({ search_display: 'none', users:data });
}); });
} }
function getUsersForSearch(req, res) { function getUsersForSearch(req, res) {
res.json({ search_display: 'block', users: [] }); res.json({ search_display: 'block', loadmore_display:'none', users: [] });
} }
function getUserDataByUserSlug(userslug, callerUID, callback) { function getUserDataByUserSlug(userslug, callerUID, callback) {
@ -391,6 +371,7 @@ var user = require('./../user.js'),
else else
data.emailClass = "hide"; data.emailClass = "hide";
data.show_banned = data.banned === '1'?'':'hide';
data.uid = uid; data.uid = uid;
data.yourid = callerUID; data.yourid = callerUID;

@ -6,7 +6,8 @@ var RDB = require('./redis.js'),
notifications = require('./notifications.js'), notifications = require('./notifications.js'),
posts = require('./posts'), posts = require('./posts'),
reds = require('reds'), reds = require('reds'),
topicSearch = reds.createSearch('nodebbtopicsearch'); topicSearch = reds.createSearch('nodebbtopicsearch'),
winston = require('winston');
(function(ThreadTools) { (function(ThreadTools) {
@ -192,7 +193,7 @@ var RDB = require('./redis.js'),
categories.moveRecentReplies(tid, oldCid, cid, function(err, data) { categories.moveRecentReplies(tid, oldCid, cid, function(err, data) {
if(err) { if(err) {
console.log(err); winston.err(err);
} }
}); });
@ -297,7 +298,6 @@ var RDB = require('./redis.js'),
} }
} }
// If we got here, nothing was found...
callback(new Error('no-undeleted-pids-found')); callback(new Error('no-undeleted-pids-found'));
}); });
} }

@ -20,7 +20,7 @@ marked.setOptions({
(function(Topics) { (function(Topics) {
Topics.minimumTitleLength = 3;
Topics.getTopicData = function(tid, callback) { Topics.getTopicData = function(tid, callback) {
RDB.hgetall('topic:' + tid, function(err, data) { RDB.hgetall('topic:' + tid, function(err, data) {
@ -81,6 +81,7 @@ marked.setOptions({
for(var i=0; i<postData.length; ++i) { for(var i=0; i<postData.length; ++i) {
postData[i].fav_star_class = fav_data[postData[i].pid] ? 'icon-star' : 'icon-star-empty'; postData[i].fav_star_class = fav_data[postData[i].pid] ? 'icon-star' : 'icon-star-empty';
postData[i]['display_moderator_tools'] = (postData[i].uid == current_user || privileges.editable) ? 'show' : 'none'; postData[i]['display_moderator_tools'] = (postData[i].uid == current_user || privileges.editable) ? 'show' : 'none';
postData[i].show_banned = postData[i].user_banned === '1'?'show':'hide';
} }
callback(postData); callback(postData);
@ -149,6 +150,10 @@ marked.setOptions({
Topics.getTopicsByTids(topicIds, uid, function(topicData) { Topics.getTopicsByTids(topicIds, uid, function(topicData) {
unreadTopics.topics = topicData; unreadTopics.topics = topicData;
unreadTopics.nextStart = start + tids.length; unreadTopics.nextStart = start + tids.length;
if(!topicData || topicData.length === 0)
unreadTopics.no_topics_message = 'show';
if(uid === 0 || topicData.length === 0)
unreadTopics.show_markallread_button = 'hidden';
callback(unreadTopics); callback(unreadTopics);
}); });
} }
@ -190,9 +195,9 @@ marked.setOptions({
function getTopicInfo(topicData, callback) { function getTopicInfo(topicData, callback) {
function getUserName(next) { function getUserInfo(next) {
user.getUserField(topicData.uid, 'username', function(username) { user.getUserFields(topicData.uid, ['username'], function(userData) {
next(null, username); next(null, userData);
}); });
} }
@ -215,17 +220,13 @@ marked.setOptions({
}); });
} }
async.parallel([getUserName, hasReadTopic, getTeaserInfo, getPrivileges], function(err, results) { async.parallel([getUserInfo, hasReadTopic, getTeaserInfo, getPrivileges], function(err, results) {
var username = results[0],
hasReadTopic = results[1],
teaserInfo = results[2],
privileges = results[3];
callback({ callback({
username: username, username: results[0].username,
hasread: hasReadTopic, userbanned: results[0].banned,
teaserInfo: teaserInfo, hasread: results[1],
privileges: privileges teaserInfo: results[2],
privileges: results[3]
}); });
}); });
} }
@ -508,8 +509,10 @@ marked.setOptions({
var stripped = postData.content, var stripped = postData.content,
timestamp = postData.timestamp; timestamp = postData.timestamp;
if(postData.content) if(postData.content) {
stripped = utils.strip_tags(postTools.markdownToHTML(postData.content)); stripped = postData.content.replace(/>.+\n\n/, '');
stripped = utils.strip_tags(postTools.markdownToHTML(stripped));
}
callback(null, { callback(null, {
"text": stripped, "text": stripped,
@ -528,7 +531,7 @@ marked.setOptions({
type: 'error', type: 'error',
timeout: 2000, timeout: 2000,
title: 'Title too short', title: 'Title too short',
message: "Please enter a longer title. At least " + Topics.minimumTitleLength + " characters.", message: "Please enter a longer title. At least " + config.minimumTitleLength + " characters.",
alert_id: 'post_error' alert_id: 'post_error'
}); });
} }
@ -545,17 +548,17 @@ marked.setOptions({
if (uid === 0) { if (uid === 0) {
callback(new Error('not-logged-in'), null); callback(new Error('not-logged-in'), null);
return; return;
} else if(!title || title.length < Topics.minimumTitleLength) { } else if(!title || title.length < config.minimumTitleLength) {
callback(new Error('title-too-short'), null); callback(new Error('title-too-short'), null);
return; return;
} else if (!content || content.length < posts.miminumPostLength) { } else if (!content || content.length < config.miminumPostLength) {
callback(new Error('content-too-short'), null); callback(new Error('content-too-short'), null);
return; return;
} }
user.getUserField(uid, 'lastposttime', function(lastposttime) { user.getUserField(uid, 'lastposttime', function(lastposttime) {
if(Date.now() - lastposttime < config.post_delay) { if(Date.now() - lastposttime < config.postDelay) {
callback(new Error('too-many-posts'), null); callback(new Error('too-many-posts'), null);
return; return;
} }

@ -1,6 +1,6 @@
var RDB = require('./redis.js'), var RDB = require('./redis.js'),
async = require('async'); async = require('async'),
winston = require('winston');
function upgradeCategory(cid, callback) { function upgradeCategory(cid, callback) {
@ -20,7 +20,6 @@ function upgradeCategory(cid, callback) {
async.each(tids, moveTopic, function(err) { async.each(tids, moveTopic, function(err) {
if(!err) { if(!err) {
console.log('renaming ' + cid);
RDB.rename('temp_categories:' + cid + ':tid', 'categories:' + cid + ':tid'); RDB.rename('temp_categories:' + cid + ':tid', 'categories:' + cid + ':tid');
callback(null); callback(null);
} }
@ -30,43 +29,70 @@ function upgradeCategory(cid, callback) {
}); });
} else { } else {
console.log('category already upgraded '+ cid); winston.info('category already upgraded '+ cid);
callback(null); callback(null);
} }
}); });
} }
function upgradeUser(uid, callback) {
RDB.hmgetObject('user:' + uid, ['joindate', 'postcount', 'reputation'], function(err, userData) {
if(err)
return callback(err);
RDB.zadd('users:joindate', userData.joindate, uid);
RDB.zadd('users:postcount', userData.postcount, uid);
RDB.zadd('users:reputation', userData.reputation, uid);
callback(null);
});
}
exports.upgrade = function() { exports.upgrade = function() {
console.log('upgrading nodebb now'); winston.info('upgrading nodebb now');
var schema = [ var schema = [
function upgradeCategories(next) { function upgradeCategories(next) {
console.log('upgrading categories'); winston.info('upgrading categories');
RDB.lrange('categories:cid', 0, -1, function(err, cids) { RDB.lrange('categories:cid', 0, -1, function(err, cids) {
async.each(cids, upgradeCategory, function(err) { async.each(cids, upgradeCategory, function(err) {
if(!err) if(!err) {
next(null, 'upgraded categories'); winston.info('upgraded categories');
else next(null, null);
} else {
next(err, null); next(err, null);
}
}); });
}); });
}, },
function upgradeUsers(next) { function upgradeUsers(next) {
console.log('upgrading users'); winston.info('upgrading users');
next(null, 'upgraded users');
RDB.lrange('userlist', 0, -1, function(err, uids) {
async.each(uids, upgradeUser, function(err) {
if(!err) {
winston.info('upgraded users')
next(null, null);
} else {
next(err, null);
}
});
});
} }
]; ];
async.series(schema, function(err, results) { async.series(schema, function(err, results) {
if(!err) if(!err)
console.log('upgrade complete'); winston.info('upgrade complete');
else else
console.log(err); winston.err(err);
process.exit(); process.exit();

@ -41,12 +41,13 @@ var utils = require('./../public/src/utils.js'),
} else next(); } else next();
} }
], function(err, results) { ], function(err, results) {
if (err) return callback(err, 0); // FIXME: Maintaining the 0 for backwards compatibility. Do we need this? if (err) return callback(err, null);
RDB.incr('global:next_user_id', function(err, uid) { RDB.incr('global:next_user_id', function(err, uid) {
RDB.handle(err); RDB.handle(err);
var gravatar = User.createGravatarURLFromEmail(email); var gravatar = User.createGravatarURLFromEmail(email);
var timestamp = Date.now();
RDB.hmset('user:'+uid, { RDB.hmset('user:'+uid, {
'uid': uid, 'uid': uid,
@ -58,7 +59,7 @@ var utils = require('./../public/src/utils.js'),
'website':'', 'website':'',
'email' : email || '', 'email' : email || '',
'signature':'', 'signature':'',
'joindate' : Date.now(), 'joindate' : timestamp,
'picture': gravatar, 'picture': gravatar,
'gravatarpicture' : gravatar, 'gravatarpicture' : gravatar,
'uploadedpicture': '', 'uploadedpicture': '',
@ -66,6 +67,7 @@ var utils = require('./../public/src/utils.js'),
'postcount': 0, 'postcount': 0,
'lastposttime': 0, 'lastposttime': 0,
'administrator': (uid == 1) ? 1 : 0, 'administrator': (uid == 1) ? 1 : 0,
'banned': 0,
'showemail': 0 'showemail': 0
}); });
@ -83,7 +85,9 @@ var utils = require('./../public/src/utils.js'),
io.sockets.emit('user.count', {count: count}); io.sockets.emit('user.count', {count: count});
}); });
RDB.lpush('userlist', uid); RDB.zadd('users:joindate', timestamp, uid);
RDB.zadd('users:postcount', 0, uid);
RDB.zadd('users:reputation', 0, uid);
io.sockets.emit('user.latest', {userslug: userslug, username: username}); io.sockets.emit('user.latest', {userslug: userslug, username: username});
@ -112,7 +116,9 @@ var utils = require('./../public/src/utils.js'),
RDB.del('followers:' + uid); RDB.del('followers:' + uid);
RDB.del('following:' + uid); RDB.del('following:' + uid);
RDB.lrem('userlist', 1, uid); RDB.zrem('users:joindate', uid);
RDB.zrem('users:postcount', uid);
RDB.zrem('users:reputation', uid);
callback(true); callback(true);
}); });
@ -122,6 +128,14 @@ var utils = require('./../public/src/utils.js'),
}); });
} }
User.ban = function(uid, callback) {
User.setUserField(uid, 'banned', 1, callback);
}
User.unban = function(uid, callback) {
User.setUserField(uid, 'banned', 0, callback);
}
User.getUserField = function(uid, field, callback) { User.getUserField = function(uid, field, callback) {
RDB.hget('user:' + uid, field, function(err, data) { RDB.hget('user:' + uid, field, function(err, data) {
if(err === null) { if(err === null) {
@ -185,6 +199,12 @@ var utils = require('./../public/src/utils.js'),
}); });
} }
User.filterBannedUsers = function(users) {
return users.filter(function(user) {
return (!user.banned || user.banned === '0');
});
}
User.updateProfile = function(uid, data, callback) { User.updateProfile = function(uid, data, callback) {
var fields = ['email', 'fullname', 'website', 'location', 'birthday', 'signature']; var fields = ['email', 'fullname', 'website', 'location', 'birthday', 'signature'];
@ -304,28 +324,29 @@ var utils = require('./../public/src/utils.js'),
}); });
} }
User.setUserField = function(uid, field, value) { User.setUserField = function(uid, field, value, callback) {
RDB.hset('user:' + uid, field, value); RDB.hset('user:' + uid, field, value, callback);
} }
User.setUserFields = function(uid, data) { User.setUserFields = function(uid, data) {
RDB.hmset('user:' + uid, data); RDB.hmset('user:' + uid, data);
} }
User.incrementUserFieldBy = function(uid, field, value) { User.incrementUserFieldBy = function(uid, field, value, callback) {
RDB.hincrby('user:' + uid, field, value); RDB.hincrby('user:' + uid, field, value, callback);
} }
User.decrementUserFieldBy = function(uid, field, value) { User.decrementUserFieldBy = function(uid, field, value, callback) {
RDB.hincrby('user:' + uid, field, -value); RDB.hincrby('user:' + uid, field, -value, callback);
} }
User.getUserList = function(callback) { User.getUsers = function(set, start, stop, callback) {
var data = []; var data = [];
RDB.lrange('userlist', 0, -1, function(err, uids) { RDB.zrevrange(set, start, stop, function(err, uids) {
if(err) {
RDB.handle(err); return callback(err, null);
}
function iterator(uid, callback) { function iterator(uid, callback) {
User.getUserData(uid, function(userData) { User.getUserData(uid, function(userData) {
@ -336,13 +357,8 @@ var utils = require('./../public/src/utils.js'),
}); });
} }
async.each(uids, iterator, function(err) { async.eachSeries(uids, iterator, function(err) {
if(!err) { callback(err, data);
callback(data);
} else {
console.log(err);
callback(null);
}
}); });
}); });
} }
@ -401,7 +417,10 @@ var utils = require('./../public/src/utils.js'),
User.onNewPostMade = function(uid, tid, pid, timestamp) { User.onNewPostMade = function(uid, tid, pid, timestamp) {
User.addPostIdToUser(uid, pid); User.addPostIdToUser(uid, pid);
User.incrementUserFieldBy(uid, 'postcount', 1); User.incrementUserFieldBy(uid, 'postcount', 1, function(err, newpostcount) {
RDB.zadd('users:postcount', newpostcount, uid);
});
User.setUserField(uid, 'lastposttime', timestamp); User.setUserField(uid, 'lastposttime', timestamp);
User.sendPostNotificationToFollowers(uid, tid, pid); User.sendPostNotificationToFollowers(uid, tid, pid);
@ -602,7 +621,7 @@ var utils = require('./../public/src/utils.js'),
}; };
User.latest = function(socket) { User.latest = function(socket) {
RDB.lrange('userlist', 0, 0, function(err, uid) { RDB.zrevrange('users:joindate', 0, 0, function(err, uid) {
RDB.handle(err); RDB.handle(err);
User.getUserFields(uid, ['username', 'userslug'], function(userData) { User.getUserFields(uid, ['username', 'userslug'], function(userData) {

@ -108,7 +108,6 @@ var express = require('express'),
// respond with html page // respond with html page
if (req.accepts('html')) { if (req.accepts('html')) {
//res.json('404', { url: req.url }); //res.json('404', { url: req.url });
res.redirect(global.nconf.get('relative_path') + '/404'); res.redirect(global.nconf.get('relative_path') + '/404');
return; return;
@ -116,7 +115,6 @@ var express = require('express'),
// respond with json // respond with json
if (req.accepts('json')) { if (req.accepts('json')) {
console.log('sending 404 json');
res.send({ error: 'Not found' }); res.send({ error: 'Not found' });
return; return;
} }
@ -126,6 +124,7 @@ var express = require('express'),
}); });
app.use(function(err, req, res, next) { app.use(function(err, req, res, next) {
// we may use properties of the error object // we may use properties of the error object
// here and next(err) appropriately, or if // here and next(err) appropriately, or if
// we possibly recovered from the error, simply next(). // we possibly recovered from the error, simply next().
@ -401,6 +400,7 @@ var express = require('express'),
} }
}); });
}); });
}); });
}(WebServer)); }(WebServer));

@ -26,7 +26,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
'categories': require('./admin/categories.js'), 'categories': require('./admin/categories.js'),
'user': require('./admin/user.js') 'user': require('./admin/user.js')
}, },
plugins = require('./plugins'); plugins = require('./plugins'),
winston = require('winston');
(function(io) { (function(io) {
var users = {}, var users = {},
@ -366,7 +367,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
if(err) { if(err) {
if(err.message === 'content-too-short') { if(err.message === 'content-too-short') {
posts.emitContentTooShortAlert(socket); posts.emitContentTooShortAlert(socket);
} else if(err.messages === 'too-many-posts') { } else if(err.message === 'too-many-posts') {
posts.emitTooManyPostsAlert(socket); posts.emitTooManyPostsAlert(socket);
} else if(err.message === 'reply-error') { } else if(err.message === 'reply-error') {
socket.emit('event:alert', { socket.emit('event:alert', {
@ -462,7 +463,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
if(!data.title || data.title.length < topics.minimumTitleLength) { if(!data.title || data.title.length < topics.minimumTitleLength) {
topics.emitTitleTooShortAlert(socket); topics.emitTitleTooShortAlert(socket);
return; return;
} else if (!data.content || data.content.length < posts.minimumPostLength) { } else if (!data.content || data.content.length < require('../public/config.json').minimumPostLength) {
posts.emitContentTooShortAlert(socket); posts.emitContentTooShortAlert(socket);
return; return;
} }
@ -657,12 +658,24 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
var start = data.after, var start = data.after,
end = start + 9; end = start + 9;
console.log(start, end);
topics.getUnreadTopics(uid, start, end, function(unreadTopics) { topics.getUnreadTopics(uid, start, end, function(unreadTopics) {
callback(unreadTopics); callback(unreadTopics);
}); });
}); });
socket.on('api:users.loadMore', function(data, callback) {
var start = data.after,
end = start + 19;
user.getUsers(data.set, start, end, function(err, data) {
if(err) {
winston.err(err);
} else {
callback({users:data});
}
});
});
socket.on('api:admin.topics.getMore', function(data, callback) { socket.on('api:admin.topics.getMore', function(data, callback) {
topics.getAllTopics(data.limit, data.after, function(topics) { topics.getAllTopics(data.limit, data.after, function(topics) {
callback(JSON.stringify(topics)); callback(JSON.stringify(topics));
@ -691,14 +704,26 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
} }
}); });
socket.on('api:admin.user.banUser', function(theirid) {
if(uid && uid > 0) {
admin.user.banUser(uid, theirid, socket);
}
});
socket.on('api:admin.user.unbanUser', function(theirid) {
if(uid && uid > 0) {
admin.user.unbanUser(uid, theirid, socket);
}
});
socket.on('api:admin.user.search', function(username) { socket.on('api:admin.user.search', function(username) {
if(uid && uid > 0) { if(uid && uid > 0) {
user.search(username, function(data) { user.search(username, function(data) {
socket.emit('api:admin.user.search', data); socket.emit('api:admin.user.search', data);
}); });
} } else {
else
socket.emit('api:admin.user.search', null); socket.emit('api:admin.user.search', null);
}
}); });
socket.on('api:admin.themes.getInstalled', function(callback) { socket.on('api:admin.themes.getInstalled', function(callback) {

Loading…
Cancel
Save