mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-02 20:16:04 +01:00
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
This commit is contained in:
33
README.md
33
README.md
@@ -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,7 +55,9 @@
|
||||
|
||||
nconf.set(dbType, testDbConfig);
|
||||
|
||||
db = require('../src/database');
|
||||
var db = require('../src/database'),
|
||||
meta = require('../src/meta');
|
||||
|
||||
before(function(done) {
|
||||
|
||||
db.init(function(err) {
|
||||
@@ -64,14 +66,18 @@
|
||||
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) {
|
||||
|
||||
indicatorEl.fadeOut(function() {
|
||||
infiniteLoaderActive = false;
|
||||
indicatorEl.fadeOut();
|
||||
});
|
||||
|
||||
if(err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
156
public/templates/accountedit.tpl
Normal file
156
public/templates/accountedit.tpl
Normal file
@@ -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}" />
|
||||
218
public/templates/header.tpl
Normal file
218
public/templates/header.tpl
Normal file
@@ -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}
|
||||
</a>
|
||||
|
||||
<a href="../../topic/{posts.topic.slug}#{posts.pid}" class="search-result-text">
|
||||
<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,7 +544,7 @@
|
||||
value = value.toString();
|
||||
}
|
||||
var data = {
|
||||
score:score,
|
||||
score: parseInt(score, 10),
|
||||
value: value
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}));
|
||||
if (fs.existsSync(path.join(__dirname, '../node_modules', pluginData.id, file))) {
|
||||
return path.join(pluginData.id, 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);
|
||||
}));
|
||||
// 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();
|
||||
} else {
|
||||
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
|
||||
});
|
||||
|
||||
@@ -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() {
|
||||
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)) {
|
||||
|
||||
29
src/user.js
29
src/user.js
@@ -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,6 +472,7 @@ var bcrypt = require('bcryptjs'),
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
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!'));
|
||||
@@ -480,6 +480,10 @@ var bcrypt = require('bcryptjs'),
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
beforeEach(function(){
|
||||
topic = {
|
||||
userId: 1,
|
||||
categoryId: 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();
|
||||
});
|
||||
@@ -48,7 +54,7 @@ describe('Topic\'s', function() {
|
||||
var newPost;
|
||||
|
||||
beforeEach(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) {
|
||||
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
|
||||
@@ -26,7 +26,7 @@ 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){
|
||||
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'
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user