Merge remote-tracking branch 'origin/master' into webserver.js-refactor

Conflicts:
	public/templates/accountedit.tpl
	public/templates/header.tpl
	src/routes/meta.js
	src/webserver.js
v1.18.x
psychobunny 11 years ago
commit e84dd4fd94

@ -38,38 +38,7 @@ NodeBB requires the following software to be installed:
## Installation
First, we install our base software stack:
# apt-get install git nodejs redis-server build-essential imagemagick
If you want to use MongoDB instead of Redis install it from http://www.mongodb.org/downloads and remove 'redis-server' from the above command. [MongoDB-Setup](https://github.com/designcreateplay/NodeBB/wiki/Installing-NodeBB-With-MongoDB)
**If your package manager only installed a version of Node.js that is less than 0.8 (e.g. Ubuntu 12.10, 13.04):**
# add-apt-repository ppa:chris-lea/node.js
# apt-get update && apt-get dist-upgrade
Next, clone this repository:
$ cd /path/to/nodebb/install/location
$ git clone git://github.com/designcreateplay/NodeBB.git nodebb
Obtain all of the dependencies required by NodeBB:
$ cd nodebb
$ npm install
Initiate the setup script by running the app with the `--setup` flag:
$ ./nodebb setup
The default settings are for a local server running on the default port, with a redis store on the same machine/port.
Lastly, we run the forum.
$ ./nodebb start
NodeBB can also be started with helper programs, such as `supervisor` and `forever`. [Take a look at the options here](https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB).
[Please refer to platform-specific installation documentation](https://github.com/designcreateplay/NodeBB/wiki#wiki-installing-nodebb)
## Securing NodeBB

@ -55,23 +55,29 @@
nconf.set(dbType, testDbConfig);
db = require('../src/database');
var db = require('../src/database'),
meta = require('../src/meta');
before(function(done) {
db.init(function(err) {
//Clean up
db.flushdb(function(err) {
if(err){
if(err) {
winston.error(err);
throw new Error(err);
} else {
winston.info('test_database flushed');
done();
}
//TODO: data seeding, if needed at all
winston.info('test_database flushed');
meta.configs.init(function () {
var webserver = require('../src/webserver'),
sockets = require('../src/socket.io');
sockets.init(webserver.server);
done();
});
});
});
});

@ -189,8 +189,7 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
password_confirm.on('blur', onPasswordConfirmChanged);
$('#changePasswordBtn').on('click', function() {
if (passwordvalid && passwordsmatch && (currentPassword.val() || app.isAdmin)) {
if ((passwordvalid && passwordsmatch) || app.isAdmin) {
socket.emit('user.changePassword', {
'currentPassword': currentPassword.val(),
'newPassword': password.val(),

@ -52,7 +52,7 @@ define(function() {
return false;
});
$('#content input').focus();
$('#content #username').focus();
};
return Login;

@ -5,10 +5,12 @@ define(function() {
var searchQuery = $('#topic-results').attr('data-search-query');
$('.search-result-text').each(function() {
var text = $(this).html();
var result = $(this);
var text = result.html();
var regex = new RegExp(searchQuery, 'gi');
text = text.replace(regex, '<span class="label label-success">' + searchQuery + '</span>');
$(this).html(text);
result.html(text);
result.find('img').addClass('img-responsive');
});
$('#search-form input').val(searchQuery);

@ -622,7 +622,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
socket.on('get_users_in_room', function(data) {
if(data && data.room.indexOf('topic') !== -1) {
var activeEl = $('li.post-bar[data-index="0"] .thread_active_users');
var activeEl = $('.thread_active_users');
function createUserIcon(uid, picture, userslug, username) {
if(!activeEl.find('[data-uid="' + uid + '"]').length) {
@ -694,11 +694,9 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
// Get users who are currently replying to the topic entered
socket.emit('modules.composer.getUsersByTid', templates.get('topic_id'), function(err, uids) {
var activeUsersEl = $('.thread_active_users'),
x;
if (uids && uids.length) {
for(var x=0;x<uids.length;x++) {
activeUsersEl.find('[data-uid="' + uids[x] + '"]').addClass('replying');
activeEl.find('[data-uid="' + uids[x] + '"]').addClass('replying');
}
}
});
@ -1294,8 +1292,11 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
tid: tid,
after: after
}, function (err, data) {
infiniteLoaderActive = false;
indicatorEl.fadeOut();
indicatorEl.fadeOut(function() {
infiniteLoaderActive = false;
});
if(err) {
return app.alertError(err.message);
}

@ -0,0 +1,156 @@
<div class="account-username-box" data-userslug="{userslug}">
</div>
<div class="account">
<div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="[[user:change_picture]]" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">[[user:change_picture]]</h3>
</div>
<div class="modal-body">
<div id="gravatar-box">
<img id="user-gravatar-picture" src="" class="img-thumbnail user-profile-picture">
<span class="user-picture-label">[[user:gravatar]]</span>
<i class='fa fa-check fa-2x'></i>
</div>
<br/>
<div id="uploaded-box">
<img id="user-uploaded-picture" src="" class="img-thumbnail user-profile-picture">
<span class="user-picture-label">[[user:uploaded_picture]]</span>
<i class='fa fa-check fa-2x'></i>
</div>
<a id="uploadPictureBtn" href="#">[[user:upload_new_picture]]</a>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">[[global:close]]</button>
<button id="savePictureChangesBtn" class="btn btn-primary">[[global:save_changes]]</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="row">
<div class="col-md-2" style="text-align: center; margin-bottom:20px;">
<div class="account-picture-block text-center">
<img id="user-current-picture" class="user-profile-picture img-thumbnail" src="{picture}" /><br /><br />
<a id="changePictureBtn" href="#" class="btn btn-primary">[[user:change_picture]]</a>
</div>
</div>
<div class="col-md-5">
<div>
<form class='form-horizontal'>
<div class="control-group">
<label class="control-label" for="inputUsername">[[user:username]]</label>
<div class="controls">
<input class="form-control" type="text" id="inputUsername" placeholder="Username" value="{username}">
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputEmail">[[user:email]]</label>
<div class="controls">
<input class="form-control" type="text" id="inputEmail" placeholder="Email" value="{email}">
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputFullname">[[user:fullname]]</label>
<div class="controls">
<input class="form-control" type="text" id="inputFullname" placeholder="Full Name" value="{fullname}">
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputWebsite">[[user:website]]</label>
<div class="controls">
<input class="form-control" type="text" id="inputWebsite" placeholder="http://website.com" value="{website}">
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputLocation">[[user:location]]</label>
<div class="controls">
<input class="form-control" type="text" id="inputLocation" placeholder="Location" value="{location}">
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputBirthday">[[user:birthday]]</label>
<div class="controls">
<input class="form-control" type="date" id="inputBirthday" placeholder="mm/dd/yyyy" value="{birthday}">
</div>
</div>
<!-- IF !disableSignatures -->
<div class="control-group">
<label class="control-label" for="inputSignature">[[user:signature]]</label> <small><label id="signatureCharCountLeft"></label></small>
<div class="controls">
<textarea class="form-control" id="inputSignature" rows="5">{signature}</textarea>
</div>
</div>
<!-- ENDIF !disableSignatures -->
<input type="hidden" id="inputUID" value="{uid}"><br />
<div class="form-actions">
<a id="submitBtn" href="#" class="btn btn-primary">[[global:save_changes]]</a>
</div>
</form>
</div>
<hr class="visible-xs visible-sm"/>
</div>
<div class="col-md-5">
<div style="vertical-align:top;">
<form class='form-horizontal'>
<div class="control-group">
<label class="control-label" for="inputCurrentPassword">[[user:current_password]]</label>
<div class="controls">
<input class="form-control" type="password" id="inputCurrentPassword" placeholder="Current Password" value=""<!-- IF !hasPassword --> disabled<!-- ENDIF !hasPassword-->>
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputNewPassword">[[user:password]]</label>
<div class="input-group">
<input class="form-control" type="password" id="inputNewPassword" placeholder="New Password" value="">
<span class="input-group-addon">
<span id="password-notify"><i class="fa fa-circle-o"></i></span>
</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputNewPasswordAgain">[[user:confirm_password]]</label>
<div class="input-group">
<input class="form-control" type="password" id="inputNewPasswordAgain" placeholder="Confirm Password" value="">
<span class="input-group-addon">
<span id="password-confirm-notify"><i class="fa fa-circle-o"></i></span>
</span>
</div>
</div>
<br/>
<div class="form-actions">
<a id="changePasswordBtn" href="#" class="btn btn-primary">[[user:change_password]]</a>
</div>
</form>
</div>
</div>
</div>
</div>
<input type="hidden" template-variable="yourid" value="{yourid}" />
<input type="hidden" template-variable="theirid" value="{theirid}" />
<input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" />
<input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" />

@ -0,0 +1,218 @@
<!DOCTYPE html>
<html>
<head>
<title>{browserTitle}</title>
<!-- BEGIN metaTags -->
<meta<!-- IF metaTags.name --> name="{metaTags.name}"<!-- ENDIF metaTags.name --><!-- IF metaTags.property --> property="{metaTags.property}"<!-- ENDIF metaTags.property --><!-- IF metaTags.content --> content="{metaTags.content}"<!-- ENDIF metaTags.content --> />
<!-- END metaTags -->
<link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="{relative_path}/stylesheet.css?{cache-buster}" />
<!-- IF bootswatchCSS --><link href="{bootswatchCSS}" rel="stylesheet" media="screen"><!-- ENDIF bootswatchCSS -->
<!-- BEGIN linkTags -->
<link<!-- IF linkTags.link --> link="{linkTags.link}"<!-- ENDIF linkTags.link --><!-- IF linkTags.rel --> rel="{linkTags.rel}"<!-- ENDIF linkTags.rel --><!-- IF linkTags.type --> type="{linkTags.type}"<!-- ENDIF linkTags.type --><!-- IF linkTags.href --> href="{linkTags.href}"<!-- ENDIF linkTags.href --> />
<!-- END linkTags -->
<!-- IF useCustomCSS -->
<style type="text/css">{customCSS}</style>
<!-- ENDIF useCustomCSS -->
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/2.3.0/es5-shim.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7/html5shiv.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>
<script>__lt_ie_9__ = 1;</script>
<![endif]-->
<script>
var RELATIVE_PATH = "{relative_path}";
</script>
<script src="{relative_path}/socket.io/socket.io.js"></script>
<!-- BEGIN clientScripts -->
<script src="{relative_path}/{clientScripts.script}?{cache-buster}"></script>
<!-- END clientScripts -->
<script>
require.config({
baseUrl: "{relative_path}/src/modules",
waitSeconds: 3,
urlArgs: "{cache-buster}",
paths: {
"forum": '../forum'
}
});
</script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top header" role="navigation" id="header-menu">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div>
<a href="{relative_path}/">
<img class="{brand:logo:display} forum-logo" src="{brand:logo}" />
</a>
<a href="{relative_path}/">
<h1 class="navbar-brand forum-title">{title}</h1>
</a>
<div class="header-topic-title visible-xs">
<span></span>
</div>
</div>
</div>
<div class="navbar-collapse collapse navbar-ex1-collapse">
<ul id="main-nav" class="nav navbar-nav pull-left">
<li class="nodebb-loggedin">
<a href="{relative_path}/unread"><i id="unread-count" class="fa fa-fw fa-inbox" data-content="0" title="[[global:header.unread]]"></i><span class="visible-xs-inline"> [[global:header.unread]]</span></a>
</li>
<li>
<a href="{relative_path}/recent"><i class="fa fa-fw fa-clock-o" title="[[global:header.recent]]"></i><span class="visible-xs-inline"> [[global:header.recent]]</span></a>
</li>
<li>
<a href="{relative_path}/popular"><i class="fa fa-fw fa-fire" title="[[global:header.popular]]"></i><span class="visible-xs-inline"> [[global:header.popular]]</span></a>
</li>
<li>
<a href="{relative_path}/users"><i class="fa fa-fw fa-users" title="[[global:header.users]]"></i><span class="visible-xs-inline"> [[global:header.users]]</span></a>
</li>
<!-- IF isAdmin -->
<li>
<a href="{relative_path}/admin"><i class="fa fa-fw fa-cogs" title="[[global:header.admin]]"></i><span class="visible-xs-inline"> [[global:header.admin]]</span></a>
</li>
<!-- ENDIF isAdmin -->
<!-- IF searchEnabled -->
<li class="visible-xs">
<a id="mobile-search-button" href="{relative_path}/search"><i class="fa fa-search fa-fw" title="[[global:header.search]]"></i> [[global:header.search]]</a>
</li>
<!-- ENDIF searchEnabled -->
<!-- BEGIN navigation -->
<li class="{navigation.class}">
<a href="{relative_path}{navigation.route}" title="{navigation.title}">
<!-- IF navigation.iconClass -->
<i class="fa fa-fw {navigation.iconClass}"></i>
<!-- ENDIF navigation.iconClass -->
<!-- IF navigation.text -->
<span class="{navigation.textClass}">{navigation.text}</span>
<!-- ENDIF navigation.text -->
</a>
</li>
<!-- END navigation -->
</ul>
<ul id="logged-in-menu" class="nav navbar-nav navbar-right hide pull-right">
<li>
<a href="#" id="reconnect" class="hide" title="Connection to {title} has been lost, attempting to reconnect..."><i class="fa fa-check"></i></a>
</li>
<li class="notifications dropdown text-center hidden-xs">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="notif_dropdown"><i class="fa fa-fw fa-bell-o" data-content="0" title="[[global:header.notifications]]"></i></a>
<ul id="notif-list" class="dropdown-menu" aria-labelledby="notif_dropdown">
<li>
<a href="#"><i class="fa fa-refresh fa-spin"></i> [[global:notifications.loading]]</a>
</li>
</ul>
</li>
<li class="visible-xs">
<a href="{relative_path}/notifications"><i class="fa fa-exclamation-triangle fa-fw" title="[[notifications:title]]"></i> [[notifications:title]]</a>
</li>
<li class="chats dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="chat_dropdown"><i class="fa fa-comment-o fa-fw" title="[[global:header.chats]]"></i> <span class="visible-xs-inline">[[global:header.chats]]</span></a>
<ul id="chat-list" class="dropdown-menu" aria-labelledby="chat_dropdown">
<li>
<a href="#"><i class="fa fa-refresh fa-spin"></i> [[global:chats.loading]]</a>
</li>
</ul>
</li>
<li id="user_label" class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="user_dropdown" title="[[global:header.profile]]">
<img src=""/>
</a>
<ul id="user-control-list" class="dropdown-menu" aria-labelledby="user_dropdown">
<li>
<a id="user-profile-link" href=""><i class="fa fa-circle status-offline"></i><span>[[global:header.profile]]</span></a>
</li>
<li id="logout-link">
<a href="#">[[global:logout]]</a>
</li>
<li role="presentation" class="divider"></li>
<li>
<a href="#" class="user-status" data-status="online"><i class="fa fa-circle status online"></i><span> [[global:online]]</span></a>
</li>
<li>
<a href="#" class="user-status" data-status="away"><i class="fa fa-circle status away"></i><span> [[global:away]]</span></a>
</li>
<li>
<a href="#" class="user-status" data-status="dnd"><i class="fa fa-circle status dnd"></i><span> [[global:dnd]]</span></a>
</li>
<li>
<a href="#" class="user-status" data-status="offline"><i class="fa fa-circle status offline"></i><span> [[global:invisible]]</span></a>
</li>
</ul>
</li>
</ul>
<ul id="logged-out-menu" class="nav navbar-nav navbar-right pull-right">
<!-- IF allowRegistration -->
<li>
<a href="{relative_path}/register">
<i class="fa fa-pencil visible-xs-inline"></i>
<span>[[global:register]]</span>
</a>
</li>
<!-- ENDIF allowRegistration -->
<li>
<a href="{relative_path}/login">
<i class="fa fa-sign-in visible-xs-inline"></i>
<span>[[global:login]]</span>
</a>
</li>
</ul>
<!-- IF searchEnabled -->
<ul id="logged-conditional-menu" class="nav navbar-nav navbar-right">
<li>
<form id="search-form" class="navbar-form navbar-right hidden-xs" role="search" method="GET" action="">
<div class="hide" id="search-fields">
<div class="form-group">
<input type="text" class="form-control" placeholder="[[global:search]]" name="query" value="">
</div>
<button type="submit" class="btn btn-default hide">[[global:search]]</button>
</div>
<button id="search-button" type="button" class="btn btn-link hide"><i class="fa fa-search fa-fw" title="[[global:header.search]]"></i></button>
</form>
</li>
</ul>
<!-- ENDIF searchEnabled -->
<ul class="nav navbar-nav navbar-right pagination-block hidden visible-lg visible-md">
<li class="active">
<a href="#">
<i class="fa fa-chevron-up pointer"></i>
<span id="pagination"></span>
<i class="fa fa-chevron-down pointer"></i>
<div class="progress-container">
<div class="progress-bar"></div>
</div>
</a>
</li>
</ul>
<div class="header-topic-title pull-right hidden-xs">
<span></span>
</div>
</div>
</div>
</div>
<input id="csrf_token" type="hidden" template-variable="csrf" value="{csrf}" />
<div class="container" id="content">

@ -31,7 +31,7 @@
<a href="../../topic/{topics.slug}" class="search-result-text">
{topics.title}
<h4>{topics.title}</h4>
</a>
<div>
@ -61,10 +61,13 @@
<!-- BEGIN posts -->
<div class="topic-row panel panel-default clearfix">
<div class="panel-body">
<a href="../../topic/{posts.topic.slug}#{posts.pid}" class="search-result-text">
{posts.content}
<h4>{posts.topic.title}</h4>
</a>
<div class="search-result-text">
{posts.content}
</div>
<div>
<small>
<span class="pull-right">

@ -166,15 +166,8 @@
module.flushdb = function(callback) {
db.dropDatabase(function(err, result) {
if (err) {
winston.error(err.message);
if (typeof callback === 'function') {
return callback(err);
}
}
if (typeof callback === 'function') {
callback();
callback(err);
}
});
};
@ -551,8 +544,8 @@
value = value.toString();
}
var data = {
score:score,
value:value
score: parseInt(score, 10),
value: value
};
db.collection('objects').update({_key:key, value:value}, {$set:data}, {upsert:true, w: 1}, function(err, result) {

@ -117,11 +117,9 @@
module.flushdb = function(callback) {
redisClient.send_command('flushdb', [], function(err) {
if (err) {
winston.error(err.message);
return callback(err);
if (typeof callback === 'function') {
callback(err);
}
callback();
});
};

@ -253,12 +253,6 @@ var async = require('async'),
}, {
field: 'chatMessagesToDisplay',
value: 50
}, {
field: 'theme:type',
value: 'local'
}, {
field: 'theme:id',
value: 'nodebb-theme-cerulean'
}];
async.each(defaults, function (configObj, next) {
@ -282,6 +276,15 @@ var async = require('async'),
}
}
},
function(next) {
var meta = require('./meta');
winston.info('Enabling default theme: Lavender');
meta.themes.set({
type: 'local',
id: 'nodebb-theme-lavender'
}, next);
},
function (next) {
// Check if an administrator needs to be created
var Groups = require('./groups');

@ -89,6 +89,9 @@ var fs = require('fs'),
Meta.themes = {
get: function (callback) {
var themePath = nconf.get('themes_path');
if (typeof themePath !== 'string') {
return callback(null, []);
}
fs.readdir(themePath, function (err, files) {
async.filter(files, function (file, next) {
fs.stat(path.join(themePath, file), function (err, fileStat) {

@ -163,7 +163,6 @@ middleware.renderHeader = function(req, res, callback) {
}],
templateValues = {
bootswatchCSS: meta.config['theme:src'],
pluginCSS: plugins.cssFiles.map(function(file) { return { path: nconf.get('relative_path') + file.replace(/\\/g, '/') }; }),
title: meta.config.title || '',
description: meta.config.description || '',
'brand:logo': meta.config['brand:logo'] || '',

@ -63,6 +63,8 @@ var fs = require('fs'),
Plugins.loadedHooks = {};
Plugins.staticDirs = {};
Plugins.cssFiles.length = 0;
Plugins.lessFiles.length = 0;
Plugins.clientScripts.length = 0;
// Read the list of activated plugins and require their libraries
async.waterfall([
@ -196,21 +198,22 @@ var fs = require('fs'),
winston.info('[plugins] Found ' + pluginData.css.length + ' CSS file(s) for plugin ' + pluginData.id);
}
if (!pluginData.staticDir) {
Plugins.cssFiles = Plugins.cssFiles.concat(pluginData.css.map(function(file) {
return path.join('/plugins', file);
}));
} else {
winston.warn('[plugins/' + pluginData.id + '] staticDir is deprecated, define CSS files with new staticDirs instead.');
Plugins.cssFiles = Plugins.cssFiles.concat(pluginData.css.map(function(file) {
return path.join('/plugins', pluginData.id, file);
}));
}
next();
} else {
next();
Plugins.cssFiles = Plugins.cssFiles.concat(pluginData.css.map(function(file) {
if (fs.existsSync(path.join(__dirname, '../node_modules', pluginData.id, file))) {
return path.join(pluginData.id, file);
} else {
// Backwards compatibility with < v0.4.0, remove this for v0.5.0
if (pluginData.staticDir) {
return path.join(pluginData.id, pluginData.staticDir, file);
} else {
winston.error('[plugins/' + pluginData.id + '] This plugin\'s CSS is incorrectly configured, please contact the plugin author.');
return null;
}
}
}).filter(function(path) { return path })); // Filter out nulls, remove this for v0.5.0
}
next();
},
function(next) {
// LESS files for plugins

@ -50,6 +50,11 @@ function sendStylesheet(req, res, next) {
source += '\n@import "./' + plugins.lessFiles[x] + '";';
}
// ... and for each CSS file
for(x=0,numCSS=plugins.cssFiles.length;x<numCSS;x++) {
source += '\n@import (inline) "./' + plugins.cssFiles[x] + '";';
}
var parser = new (less.Parser)({
paths: paths
});
@ -75,4 +80,4 @@ module.exports = function(app, middleware, controllers) {
app.get('/nodebb.min.js', sendMinifiedJS);
app.get('/sitemap.xml', controllers.sitemap);
app.get('/robots.txt', controllers.robots);
};
};

@ -197,13 +197,16 @@ Upgrade.upgrade = function(callback) {
db.setAdd('plugins:active', 'nodebb-widget-essentials', function(err) {
winston.info('[2014/2/20] Activating NodeBB Essential Widgets');
Plugins.reload(function() {
next(err);
if (err) {
next(err);
} else {
Upgrade.update(thisSchemaDate, next);
}
});
});
} else {
winston.info('[2014/2/20] Activating NodeBB Essential Widgets - skipped');
Upgrade.update(thisSchemaDate, next);
next();
}
},
function(next) {
@ -220,8 +223,11 @@ Upgrade.upgrade = function(callback) {
}
db.getListRange('categories:cid', 0, -1, function(err, cids) {
// Naive type-checking, becaue DBAL does not have .type() support
if(err) {
return next(err);
// Most likely upgraded already. Skip.
winston.info('[2014/2/22] Added categories to sorted set - skipped');
return Upgrade.update(thisSchemaDate, next);
}
if(!Array.isArray(cids)) {

@ -58,7 +58,7 @@ var bcrypt = require('bcryptjs'),
},
function(next) {
if (userData.email) {
User.isEmailAvailable(userData.email, function(err, available) {
User.email.available(userData.email, function(err, available) {
if (err) {
return next(err);
}
@ -185,8 +185,13 @@ var bcrypt = require('bcryptjs'),
return callback(err);
}
if (data && data.password) {
delete data.password;
if (data) {
if (data.password) {
data.password = null;
data.hasPassword = true;
} else {
data.hasPassword = false;
}
}
callback(err, data);
});
@ -278,7 +283,7 @@ var bcrypt = require('bcryptjs'),
return next(null, true);
}
User.isEmailAvailable(data.email, function(err, available) {
User.email.available(data.email, function(err, available) {
if (err) {
return next(err, null);
}
@ -422,12 +427,6 @@ var bcrypt = require('bcryptjs'),
});
};
User.isEmailAvailable = function(email, callback) {
db.isObjectField('email:uid', email, function(err, exists) {
callback(err, !exists);
});
};
User.changePassword = function(uid, data, callback) {
if(!data || !data.uid) {
return callback(new Error('invalid-uid'));
@ -473,13 +472,18 @@ var bcrypt = require('bcryptjs'),
return callback(err);
}
bcrypt.compare(data.currentPassword, currentPassword, function(err, res) {
if (err || !res) {
return callback(err || new Error('Your current password is not correct!'));
}
if (currentPassword !== null) {
bcrypt.compare(data.currentPassword, currentPassword, function(err, res) {
if (err || !res) {
return callback(err || new Error('Your current password is not correct!'));
}
hashAndSetPassword(callback);
});
} else {
// No password in account (probably SSO login)
hashAndSetPassword(callback);
});
}
});
}
};
@ -1029,6 +1033,11 @@ var bcrypt = require('bcryptjs'),
});
}
});
},
available: function(email, callback) {
db.isObjectField('email:uid', email, function(err, exists) {
callback(err, !exists);
});
}
};

@ -1,31 +1,39 @@
var winston = require('winston');
process.on('uncaughtException', function (err) {
winston.error('Encountered error while running test suite: ' + err.message);
});
'use strict';
var assert = require('assert'),
db = require('../mocks/databasemock');
var Topics = require('../src/topics');
db = require('../mocks/databasemock'),
topics = require('../src/topics'),
categories = require('../src/categories');
describe('Topic\'s', function() {
var topic;
beforeEach(function(){
topic = {
userId: 1,
categoryId: 1,
title: 'Test Topic Title',
content: 'The content of test topic'
};
var topic,
categoryObj;
before(function(done) {
categories.create({
name: 'Test Category',
description: 'Test category created by testing script',
icon: 'fa-check',
blockclass: 'category-blue',
order: '5'
}, function(err, category) {
categoryObj = category;
topic = {
userId: 1,
categoryId: categoryObj.cid,
title: 'Test Topic Title',
content: 'The content of test topic'
};
done();
});
});
describe('.post', function() {
it('should create a new topic with proper parameters', function(done) {
Topics.post({uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
topics.post({uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
assert.equal(err, null, 'was created with error');
assert.ok(result);
@ -34,9 +42,7 @@ describe('Topic\'s', function() {
});
it('should fail to create new topic with wrong parameters', function(done) {
topic.userId = null;
Topics.post({uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
topics.post({uid: null, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
assert.equal(err.message, 'invalid-user');
done();
});
@ -47,8 +53,8 @@ describe('Topic\'s', function() {
var newTopic;
var newPost;
beforeEach(function(done){
Topics.post({uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
beforeEach(function(done) {
topics.post({uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
newTopic = result.topicData;
newPost = result.postData;
done();
@ -57,13 +63,13 @@ describe('Topic\'s', function() {
describe('.getTopicData', function() {
it('should not receive errors', function(done) {
Topics.getTopicData(newTopic.tid, done);
topics.getTopicData(newTopic.tid, done);
});
});
describe('.getTopicDataWithUser', function() {
it('should not receive errors', function(done) {
Topics.getTopicDataWithUser(newTopic.tid, done);
topics.getTopicDataWithUser(newTopic.tid, done);
});
});
});

@ -16,7 +16,7 @@ describe('User', function() {
beforeEach(function(){
userData = {
name: 'John Smith',
username: 'John Smith',
password: 'swordfish',
email: 'john@example.com',
callback: undefined
@ -25,8 +25,8 @@ describe('User', function() {
describe('when created', function() {
it('should be created properly', function(done){
User.create({usename: userData.name, password: userData.password, email: userData.email}, function(error,userId){
it('should be created properly', function(done) {
User.create({username: userData.username, password: userData.password, email: userData.email}, function(error,userId){
assert.equal(error, null, 'was created with error');
assert.ok(userId);
done();
@ -35,7 +35,7 @@ describe('User', function() {
it('should have a valid email, if using an email', function() {
assert.throws(
User.create({username: userData.name, password: userData.password, email: 'fakeMail'},function(){}),
User.create({username: userData.username, password: userData.password, email: 'fakeMail'},function(){}),
Error,
'does not validate email'
);

Loading…
Cancel
Save