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

v1.18.x
Julian Lam 12 years ago
commit 0bff6ee504

@ -16,7 +16,11 @@ Please support NodeBB development! Check out our IndieGoGo campaign and like, sh
## Requirements ## Requirements
NodeBB requires a version of Node.js at least 0.8 or greater, and a Redis version 2.6 or greater. NodeBB requires the following software to be installed:
* A version of Node.js at least 0.8 or greater
* Redis, version 2.6 or greater.
* nginx, version 1.3.13 or greater
## Installation ## Installation

@ -101,69 +101,87 @@
@-webkit-keyframes scroll-2 /* Safari and Chrome */ @-webkit-keyframes scroll-2 /* Safari and Chrome */
{ {
0% {top: 0px;} 0% {top: 0px;}
25% {top: -90px;} 3% {top:-90px;}
50% {top: -180px;} 25% {top: -90px;}
75% {top: -270px;} 28% {top:-180px;}
50% {top: -180px;}
53% {top: -270px;};
75% {top: -270px;}
78% {top: -360px;}
100% {top: -360px;} 100% {top: -360px;}
} }
@keyframes scroll-2 @keyframes scroll-2
{ {
0% {top: 0px;} 0% {top: 0px;}
25% {top: -90px;} 3% {top:-90px;}
50% {top: -180px;} 25% {top: -90px;}
75% {top: -270px;} 28% {top:-180px;}
50% {top: -180px;}
53% {top: -270px;};
75% {top: -270px;}
78% {top: -360px;}
100% {top: -360px;} 100% {top: -360px;}
} }
@-webkit-keyframes scroll-1 /* Safari and Chrome */ @-webkit-keyframes scroll-1 /* Safari and Chrome */
{ {
0% {top: 0px;} 0% {top: 0px;}
33% {top: -90px;} 3% {top:-90px;}
66% {top: -180px;} 33% {top: -90px;}
100% {top: -270px;} 36% {top: -180px;}
66% {top: -180px;}
69% {top: -270px;}
100% {top: -270px;}
} }
@keyframes scroll-1 @keyframes scroll-1
{ {
0% {top: 0px;} 0% {top: 0px;}
33% {top: -90px;} 3% {top:-90px;}
66% {top: -180px;} 33% {top: -90px;}
100% {top: -270px;} 36% {top: -180px;}
66% {top: -180px;}
69% {top: -270px;}
100% {top: -270px;}
} }
@-webkit-keyframes scroll-0 /* Safari and Chrome */ @-webkit-keyframes scroll-0 /* Safari and Chrome */
{ {
0% {top: 0px;} 0% {top: 0px;}
50% {top: -90px;} 3% {top:-90px;}
100% {top: -180px;} 50% {top: -90px;}
53% {top: -180px;}
100% {top: -180px;}
} }
@keyframes scroll-0 @keyframes scroll-0
{ {
0% {top: 0px;} 0% {top: 0px;}
50% {top: -90px;} 3% {top:-90px;}
100% {top: -180px;} 50% {top: -90px;}
53% {top: -180px;}
100% {top: -180px;}
} }
.category-slider-2:hover { .category-slider-2:hover {
position:relative; position:relative;
-webkit-animation: scroll-2 10s ease 0.5s infinite normal; -webkit-animation: scroll-2 10s ease-in 0.5s infinite normal;
animation: scroll-2 10s ease 0.5s infinite normal;/* Safari and Chrome: */ animation: scroll-2 10s ease-in 0.5s infinite normal;/* Safari and Chrome: */
} }
.category-slider-1:hover { .category-slider-1:hover {
position:relative; position:relative;
-webkit-animation: scroll-1 8s ease 0.5s infinite normal; -webkit-animation: scroll-1 8s ease-in 0.5s infinite normal;
animation: scroll-1 8s ease 0.5s infinite normal;/* Safari and Chrome: */ animation: scroll-1 8s ease-in 0.5s infinite normal;/* Safari and Chrome: */
} }
.category-slider-0:hover { .category-slider-0:hover {
position:relative; position:relative;
-webkit-animation: scroll-0 6s ease 0.5s infinite normal; -webkit-animation: scroll-0 6s ease-in 0.5s infinite normal;
animation: scroll-0 6s ease 0.5s infinite normal;/* Safari and Chrome: */ animation: scroll-0 6s ease-in 0.5s infinite normal;/* Safari and Chrome: */
} }

@ -136,10 +136,6 @@ footer.footer {
} }
} }
#right-menu{
float:right;
}
#chat-content { #chat-content {
height:200px; height:200px;
resize:none; resize:none;
@ -257,6 +253,11 @@ footer.footer {
/* END: post-window needs to go in its own plugin area */ /* END: post-window needs to go in its own plugin area */
//theme
#search-form .btn-link {
color: white;
}
//START: FIXES FOR BS3, may need to remove these when we get out of the RC releases //START: FIXES FOR BS3, may need to remove these when we get out of the RC releases
@media (max-width: 979px) { @media (max-width: 979px) {
@ -266,9 +267,11 @@ footer.footer {
} }
} }
.container > .navbar-header, .container > .navbar-collapse { @media (min-width: 760px) {
padding-right: 0; .container > .navbar-header, .container > .navbar-collapse {
margin-right: -11px; padding-right: 0;
margin-right: -11px;
}
} }
.badge { .badge {

@ -152,36 +152,38 @@
} }
.main-post { .main-post {
h3 { .main-post-buttons {
margin: 0; h3 {
margin: 0;
.topic-title {
width: auto; .topic-title {
overflow: hidden; width: auto;
margin: 0 0 -5px 0; overflow: hidden;
padding: 0 0 5px 0 margin: 0 0 -5px 0;
padding: 0 0 5px 0
}
} }
}
.main-avatar { .main-avatar {
color: white; color: white;
position: relative; position: relative;
float: left; float: left;
margin: 0 10px 0 0; margin: 0 10px 0 0;
padding-bottom: 0px; padding-bottom: 0px;
text-align: center; text-align: center;
width:100px; width:100px;
@media (max-width: 767px) {
display: none;
}
@media (max-width: 767px) { .img-thumbnail {
display: none; padding: 2px;
border-radius: 0;
}
} }
} }
.img-thumbnail {
padding: 2px;
border-radius: 0;
}
.post-content { .post-content {
min-height: 80px; min-height: 80px;
} }

@ -15,7 +15,7 @@ var socket,
API_URL = data.api_url; API_URL = data.api_url;
config = data; config = data;
socket = io.connect(config.socket.address + (config.socket.port ? ':' + config.socket.port : '')); socket = io.connect(config.socket.address);
var reconnecting = false; var reconnecting = false;
var reconnectTries = 0; var reconnectTries = 0;
@ -314,6 +314,18 @@ var socket,
}); });
} }
app.openChat = function(username, touid) {
require(['chat'], function(chat) {
var chatModal;
if(!chat.modalExists(touid)) {
chatModal = chat.createModal(username, touid);
} else {
chatModal = chat.getModal(touid);
}
chat.load(chatModal.attr('UUID'));
});
}
app.createNewPosts = function(data) { app.createNewPosts = function(data) {
data.posts[0].display_moderator_tools = 'none'; data.posts[0].display_moderator_tools = 'none';
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data), var html = templates.prepare(templates['topic'].blocks['posts']).parse(data),

@ -18,6 +18,9 @@
followBtn.show(); followBtn.show();
unfollowBtn.hide(); unfollowBtn.hide();
} }
} else {
followBtn.hide();
unfollowBtn.hide();
} }
followBtn.on('click', function() { followBtn.on('click', function() {
@ -54,17 +57,19 @@
var onlineStatus = $('.account-online-status'); var onlineStatus = $('.account-online-status');
socket.on('api:user.isOnline', function(online) { function handleUserOnline(data) {
if(online) { if(data.online) {
onlineStatus.find('span span').text('online'); onlineStatus.find('span span').text('online');
onlineStatus.find('i').attr('class', 'icon-circle'); onlineStatus.find('i').attr('class', 'icon-circle');
} else { } else {
onlineStatus.find('span span').text('offline'); onlineStatus.find('span span').text('offline');
onlineStatus.find('i').attr('class', 'icon-circle-blank'); onlineStatus.find('i').attr('class', 'icon-circle-blank');
} }
}); }
socket.on('api:user.isOnline', handleUserOnline);
socket.emit('api:user.isOnline', theirid); socket.emit('api:user.isOnline', theirid, handleUserOnline);
}); });

@ -7,7 +7,7 @@
$(document).ready(function() { $(document).ready(function() {
if(parseInt(followersCount, 10) === 0) { if(parseInt(followersCount, 10) === 0) {
$('#no-followers-notice').show(); $('#no-followers-notice').removeClass('hide');
} }
app.addCommasToNumbers(); app.addCommasToNumbers();

@ -7,7 +7,7 @@
$(document).ready(function() { $(document).ready(function() {
if(parseInt(followingCount, 10) === 0) { if(parseInt(followingCount, 10) === 0) {
$('#no-following-notice').show(); $('#no-following-notice').removeClass('hide');
} }

@ -44,6 +44,16 @@
socket.on('api:updateHeader', function(data) { socket.on('api:updateHeader', function(data) {
jQuery('#search-button').on('click', function() {
jQuery('#search-fields').removeClass('hide').show();
jQuery(this).hide();
jQuery('#search-fields input').focus()
jQuery('#search-form').on('submit', function() {
jQuery('#search-fields').hide();
jQuery('#search-button').show();
});
});
var rightMenu = $('#right-menu'), var rightMenu = $('#right-menu'),
isLoggedIn = data.uid > 0; isLoggedIn = data.uid > 0;
@ -162,25 +172,18 @@
socket.on('chatMessage', function(data) { socket.on('chatMessage', function(data) {
require(['chat'], function(chat) { require(['chat'], function(chat) {
var chatModal = chat.createModalIfDoesntExist(data.username, data.fromuid, function(created, modal) { var modal = null;
if(!created) if(chat.modalExists(data.fromuid)) {
chat.appendChatMessage(modal, data.message, data.timestamp); modal = chat.getModal(data.fromuid);
}); chat.appendChatMessage(modal, data.message, data.timestamp);
} else {
modal = chat.createModal(data.username, data.fromuid);
}
chatModal.show(); chat.load(modal.attr('UUID'));
chat.bringModalToTop(chatModal);
}); });
}); });
socket.on('chatGoOffline', function(data) {
require(['chat'], function(chat) {
if(chat.modalOpen(data.uid)) {
var modal = chat.getModal(data.uid);
chat.appendChatMessage(modal, data.username + ' went offline\n', data.timestamp);
}
});
})
require(['mobileMenu'], function(mobileMenu) { require(['mobileMenu'], function(mobileMenu) {
mobileMenu.init(); mobileMenu.init();
}); });

@ -10,9 +10,14 @@
if (password.value.length < 6) { if (password.value.length < 6) {
$('#error').hide(); $('#error').hide();
noticeEl.querySelector('strong').innerHTML = 'Invalid Password'; noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
noticeEl.querySelector('p').innerHTML = 'The password entered it too short, please pick a different password!'; noticeEl.querySelector('p').innerHTML = 'The password entered is too short, please pick a different password.';
noticeEl.style.display = 'block'; noticeEl.style.display = 'block';
} else if (password.value === repeat.value) { } else if (password.value !== repeat.value) {
$('#error').hide();
noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
noticeEl.querySelector('p').innerHTML = 'The two passwords you\'ve entered do not match.';
noticeEl.style.display = 'block';
} else {
socket.emit('user:reset.commit', { code: reset_code, password: password.value }); socket.emit('user:reset.commit', { code: reset_code, password: password.value });
} }
}, false); }, false);

@ -67,7 +67,8 @@
return false; return false;
}); });
moveThreadModal.on('shown', function() { moveThreadModal.on('shown.bs.modal', function() {
var loadingEl = document.getElementById('categories-loading'); var loadingEl = document.getElementById('categories-loading');
if (loadingEl) { if (loadingEl) {
socket.once('api:categories.get', function(data) { socket.once('api:categories.get', function(data) {
@ -287,11 +288,7 @@
if(username === app.username || !app.username) if(username === app.username || !app.username)
return; return;
require(['chat'], function(chat) { app.openChat(username, touid);
var chatModal = chat.createModalIfDoesntExist(username, touid);
chatModal.modal();
chat.bringModalToTop(chatModal); // I don't think this is necessary
});
}); });
ajaxify.register_events([ ajaxify.register_events([
@ -379,6 +376,18 @@
this.innerHTML = data.content; this.innerHTML = data.content;
$(this).fadeIn(250); $(this).fadeIn(250);
}); });
if(data.uploadedImages && data.uploadedImages.length) {
$('#images_'+data.pid).html('');
for(var i=0; i< data.uploadedImages.length; ++i) {
var img = $('<i class="icon-picture icon-1"></i><a href="' + data.uploadedImages[i].url +'"> '+data.uploadedImages[i].name+'</a><br/>');
$('#images_' + data.pid).append(img);
}
} else {
$('#images_'+data.pid).html('');
}
console.log('time to recreate images', data);
}); });
socket.on('api:posts.favourite', function(data) { socket.on('api:posts.favourite', function(data) {

@ -17,46 +17,74 @@ define(['taskbar'], function(taskbar) {
return $('#chat-modal-' + touid); return $('#chat-modal-' + touid);
} }
module.modalOpen = function(touid) { module.modalExists = function(touid) {
return $('#chat-modal-' + touid).length !== 0; return $('#chat-modal-' + touid).length !== 0;
} }
module.createModalIfDoesntExist = function(username, touid, callback) {
var chatModal = $('#chat-modal-' + touid); function checkStatus(chatModal, callback) {
socket.emit('api:user.isOnline', chatModal.touid, function(data) {
if(!chatModal.length) { if(data.online !== chatModal.online) {
var chatModal = $('#chat-modal').clone(); if(data.online) {
chatModal.attr('id','chat-modal-' + touid); module.appendChatMessage(chatModal, chatModal.username + ' has come online.\n', data.timestamp);
var uuid = utils.generateUUID(); } else {
chatModal.attr('UUID', uuid); module.appendChatMessage(chatModal, chatModal.username + ' has gone offline.\n', data.timestamp);
chatModal.appendTo($('body'));
chatModal.draggable({
start:function(){
module.bringModalToTop(chatModal);
} }
}); chatModal.online = data.online;
chatModal.find('#chat-with-name').html(username); }
if(callback)
callback(data.online);
});
}
chatModal.find('.close').on('click', function(e) { function checkOnlineStatus(chatModal) {
chatModal.hide(); if(chatModal.intervalId === 0) {
taskbar.discard('chat', uuid); chatModal.intervalId = setInterval(function(){
}); checkStatus(chatModal);
}, 1000);
}
}
module.createModal = function(username, touid, callback) {
var chatModal = $('#chat-modal').clone(),
uuid = utils.generateUUID();
chatModal.intervalId = 0;
chatModal.touid = touid;
chatModal.username = username;
chatModal.on('click', function(e) { chatModal.attr('id', 'chat-modal-' + touid);
chatModal.attr('UUID', uuid);
chatModal.appendTo($('body'));
chatModal.draggable({
start:function() {
module.bringModalToTop(chatModal); module.bringModalToTop(chatModal);
}); }
});
addSendHandler(chatModal, touid); chatModal.find('#chat-with-name').html(username);
getChatMessages(chatModal, touid, callback); chatModal.find('.close').on('click', function(e) {
clearInterval(chatModal.intervalId);
chatModal.intervalId = 0;
chatModal.hide();
taskbar.discard('chat', uuid);
});
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username}); chatModal.on('click', function(e) {
return chatModal; module.bringModalToTop(chatModal);
} });
if(callback) addSendHandler(chatModal);
callback(false, chatModal);
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username}); checkStatus(chatModal, function(online) {
chatModal.online = online;
getChatMessages(chatModal, function() {
checkOnlineStatus(chatModal);
});
});
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with ' + username});
return chatModal; return chatModal;
} }
@ -64,45 +92,46 @@ define(['taskbar'], function(taskbar) {
var chatModal = $('div[UUID="'+uuid+'"]'); var chatModal = $('div[UUID="'+uuid+'"]');
chatModal.show(); chatModal.show();
module.bringModalToTop(chatModal); module.bringModalToTop(chatModal);
checkOnlineStatus(chatModal);
} }
module.minimize = function(uuid) { module.minimize = function(uuid) {
var chatModal = $('div[UUID="'+uuid+'"]'); var chatModal = $('div[UUID="'+uuid+'"]');
chatModal.hide(); chatModal.hide();
taskbar.minimize('chat', uuid); taskbar.minimize('chat', uuid);
clearInterval(chatModal.intervalId);
chatModal.intervalId = 0;
} }
function getChatMessages(chatModal, touid, callback) { function getChatMessages(chatModal, callback) {
socket.emit('getChatMessages', {touid:touid}, function(messages) { socket.emit('getChatMessages', {touid:chatModal.touid}, function(messages) {
for(var i = 0; i<messages.length; ++i) { for(var i = 0; i<messages.length; ++i) {
module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp); module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
} }
callback();
if(callback)
callback(true, chatModal);
}); });
} }
function addSendHandler(chatModal, touid) { function addSendHandler(chatModal) {
chatModal.find('#chat-message-input').off('keypress'); chatModal.find('#chat-message-input').off('keypress');
chatModal.find('#chat-message-input').on('keypress', function(e) { chatModal.find('#chat-message-input').on('keypress', function(e) {
if(e.which === 13) { if(e.which === 13) {
sendMessage(chatModal, touid); sendMessage(chatModal);
} }
}); });
chatModal.find('#chat-message-send-btn').off('click'); chatModal.find('#chat-message-send-btn').off('click');
chatModal.find('#chat-message-send-btn').on('click', function(e){ chatModal.find('#chat-message-send-btn').on('click', function(e){
sendMessage(chatModal, touid); sendMessage(chatModal);
return false; return false;
}); });
} }
function sendMessage(chatModal, touid) { function sendMessage(chatModal) {
var msg = app.strip_tags(chatModal.find('#chat-message-input').val()); var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
if(msg.length) { if(msg.length) {
msg = msg +'\n'; msg = msg +'\n';
socket.emit('sendChatMessage', { touid:touid, message:msg}); socket.emit('sendChatMessage', { touid:chatModal.touid, message:msg});
chatModal.find('#chat-message-input').val(''); chatModal.find('#chat-message-input').val('');
} }
} }

@ -8,7 +8,7 @@ define(['taskbar'], function(taskbar) {
}; };
function createImageLabel(img, postImages) { function createImageLabel(img, postImages) {
var imageLabel = $('<div class="label label-primary"><span>'+ img.name +'</span></div>'); var imageLabel = $('<span class="label label-primary">' + img.name +'</span>');
var closeButton = $('<button class="close">&times;</button>'); var closeButton = $('<button class="close">&times;</button>');
closeButton.on('click', function(e) { closeButton.on('click', function(e) {
@ -152,7 +152,7 @@ define(['taskbar'], function(taskbar) {
pid: threadData.pid, pid: threadData.pid,
title: threadData.title || '', title: threadData.title || '',
body: threadData.body || '', body: threadData.body || '',
images: [] images: threadData.uploadedImages
}; };
composer.load(uuid); composer.load(uuid);
} else { } else {

@ -19,8 +19,8 @@
<span class="label label-danger">banned</span> <span class="label label-danger">banned</span>
</div> </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 btn-default">Follow</a>
<a id="unfollow-btn" href="#" class="btn hide">Unfollow</a> <a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a>
</div> </div>
</div> </div>

@ -25,7 +25,7 @@
<span class='formatted-number'>{following.postcount}</span> <span class='formatted-number'>{following.postcount}</span>
<i class='icon-pencil'></i> <i class='icon-pencil'></i>
</div> </div>
<a id="unfollow-btn" href="#" class="btn unfollow-btn" followingUid="{following.uid}" data-username="{following.username}">Unfollow</a> <a id="unfollow-btn" href="#" class="btn btn-default unfollow-btn" followingUid="{following.uid}" data-username="{following.username}">Unfollow</a>
</div> </div>
<!-- END following --> <!-- END following -->
</div> </div>

@ -18,7 +18,7 @@
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
<div id="chat-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true"> <div id="chat-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">

@ -53,29 +53,35 @@
<li> <li>
<a href="/users">Users</a> <a href="/users">Users</a>
</li> </li>
<!--Enable when /search form is completed <li class="visible-xs">
<a href="/search">Search</a>
</li>-->
<li> <li>
<a href="/"></a> <a href="/"></a>
</li> </li>
</ul> </ul>
<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="Search" name="query">
</div>
<button type="submit" class="btn btn-default hide">Search</button>
</div>
<button id="search-button" type="button" class="btn btn-link nodebb-loggedin"><i class="icon-search"></i></button>
</form>
<ul id="right-menu" class="nav navbar-nav navbar-right"> <ul id="right-menu" class="nav navbar-nav navbar-right">
<li class="notifications dropdown text-center"> <li class="notifications dropdown text-center hidden-xs">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="notif_dropdown"><i class="icon-circle-blank"></i></a> <a class="dropdown-toggle" data-toggle="dropdown" href="#" id="notif_dropdown"><i class="icon-circle-blank"></i></a>
<ul id="notif-list" class="dropdown-menu" aria-labelledby="notif_dropdown"> <ul id="notif-list" class="dropdown-menu" aria-labelledby="notif_dropdown">
<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>
<li>
<form id="search-form" class="navbar-form navbar-right" role="search" method="GET" action="">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" name="query">
</div>
<button type="submit" class="btn btn-default hide">Search</button>
</form>
</li>
</ul> </ul>
<div id="pagination"></div> <div id="pagination"></div>
</div> </div>
</div> </div>

@ -1,17 +1,30 @@
<h1>Reset Password</h1> <ol class="breadcrumb">
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="/" itemprop="url"><span itemprop="title">Home</span></a>
</li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">Reset Password</span>
</li>
</ol>
<div class="alert alert-info">
Please enter your <strong>email address</strong> and we will send you an email with instructions on how to reset your account.
</div>
<div class="well"> <div class="well">
<div class="alert alert-success" id="success" style="display:none"> <div class="alert alert-success" id="success" style="display:none">
<button type="button" class="close" data-dismiss="alert">&times;</button> <button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Password Reset Sent</strong> <strong>Password Reset Sent</strong>
<p></p>
</div> </div>
<div class="alert" id="error" style="display:none"> <div class="alert alert-danger" id="error" style="display:none">
<button type="button" class="close" data-dismiss="alert">&times;</button> <button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Invalid Email!</strong> <strong>Invalid Email / Email does not exist!</strong>
<p></p>
</div> </div>
<label for="email">Email Address</label><input type="text" placeholder="Enter Email Address" id="email" /><br /> <form onsubmit="return false;">
<button class="btn btn-primary" id="reset" type="submit">Reset Password</button> <input type="text" class="form-control input-block input-lg" placeholder="Enter Email Address" id="email" />
<br />
<button class="btn btn-primary btn-block btn-lg" id="reset" type="submit">Reset Password</button>
</form>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/reset.js"></script> <script type="text/javascript" src="{relative_path}/src/forum/reset.js"></script>

@ -1,23 +1,37 @@
<h1>Reset Password</h1> <ol class="breadcrumb">
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="/" itemprop="url"><span itemprop="title">Home</span></a>
</li>
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="/reset" itemprop="url"><span itemprop="title">Reset Password</span></a>
</li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">Update Password</span>
</li>
</ol>
<div class="well"> <div class="well">
<div class="alert alert-success" id="success" style="display:none"> <div class="alert alert-success" id="success" style="display:none">
<button type="button" class="close" data-dismiss="alert">&times;</button> <button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Password Changed</strong> <strong>Password Changed</strong>
<p>Password successfully reset, please <a href="/login">log in again</a>.</p> <p>Password successfully reset, please <a href="/login">log in again</a>.</p>
</div> </div>
<div class="alert" id="notice" style="display:none"> <div class="alert alert-warning" id="notice" style="display:none">
<strong></strong> <strong></strong>
<p></p> <p></p>
</div> </div>
<div class="alert alert-error" id="error" style="display:none"> <div class="alert alert-danger" id="error" style="display:none">
<strong>Incorrect Reset Code</strong> <strong>Incorrect Reset Code</strong>
<p>The reset code received was incorrect. Please try again, or <a href="/reset">request a new reset code</a></p> <p>The reset code received was incorrect. Please try again, or <a href="/reset">request a new reset code</a></p>
</div> </div>
<div id="reset-form"> <form onsubmit="return false;" id="reset-form">
<label for="password">New Password</label><input type="password" placeholder="A new password" id="password" /><br /> <label for="password">New Password</label>
<label for="repeat">... and again</label><input type="password" placeholder="The same password" id="repeat" /><br /> <input class="form-control input-lg" type="password" placeholder="A new password" id="password" /><br />
<button class="btn btn-primary" id="reset" type="submit" disabled>Reset Password</button> <label for="repeat">Confirm Password</label>
</div> <input class="form-control input-lg" type="password" placeholder="The same password" id="repeat" /><br />
<button class="btn btn-primary btn-lg btn-block" id="reset" type="submit" disabled>Reset Password</button>
</form>
</div> </div>
<input type="hidden" template-variable="reset_code" value="{reset_code}" /> <input type="hidden" template-variable="reset_code" value="{reset_code}" />

@ -170,23 +170,28 @@
</div> </div>
</div> </div>
<div id="move_thread_modal" class="modal hide fade">
<div class="modal-header"> <div id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <div class="modal-dialog">
<h3>Move Thread</h3> <div class="modal-content">
</div> <div class="modal-header">
<div class="modal-body"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<p id="categories-loading"><i class="icon-spin icon-refresh"></i> Loading Categories</p> <h3>Move Thread</h3>
<ul class="category-list"></ul> </div>
<div id="move-confirm" style="display: none;"> <div class="modal-body">
<hr /> <p id="categories-loading"><i class="icon-spin icon-refresh"></i> Loading Categories</p>
<div class="alert">This topic will be moved to the category <strong><span id="confirm-category-name"></span></strong></div> <ul class="category-list"></ul>
<div id="move-confirm" style="display: none;">
<hr />
<div class="alert">This topic will be moved to the category <strong><span id="confirm-category-name"></span></strong></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" id="move_thread_cancel">Close</a>
<button type="button" class="btn btn-primary" id="move_thread_commit" disabled>Move</a>
</div>
</div> </div>
</div> </div>
<div class="modal-footer">
<button type="button" class="btn" data-dismiss="modal" id="move_thread_cancel">Close</a>
<button type="button" class="btn btn-primary" id="move_thread_commit" disabled>Move</a>
</div>
</div> </div>
<input type="hidden" template-variable="expose_tools" value="{expose_tools}" /> <input type="hidden" template-variable="expose_tools" value="{expose_tools}" />

@ -1,7 +1,7 @@
<div> <div>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li class='active'><a href='/users-latest'>Latest Users</a></li> <li class=''><a href='/users-latest'>Latest Users</a></li>
<li class=''><a href='/users-sort-posts'>Top Posters</a></li> <li class=''><a href='/users-sort-posts'>Top Posters</a></li>
<li class=''><a href='/users-sort-reputation'>Most Reputation</a></li> <li class=''><a href='/users-sort-reputation'>Most Reputation</a></li>
<li class=''><a href='/users-search'>Search</a></li> <li class=''><a href='/users-search'>Search</a></li>

@ -15,6 +15,7 @@ var RDB = require('./redis.js'),
var category_name = categoryData.name, var category_name = categoryData.name,
category_slug = categoryData.slug, category_slug = categoryData.slug,
disabled = categoryData.disabled || '0',
category_description = categoryData.description; category_description = categoryData.description;
function getTopicIds(next) { function getTopicIds(next) {
@ -32,6 +33,7 @@ var RDB = require('./redis.js'),
var categoryData = { var categoryData = {
'category_name' : category_name, 'category_name' : category_name,
'category_description': category_description, 'category_description': category_description,
'disabled': disabled,
'show_sidebar' : 'show', 'show_sidebar' : 'show',
'show_topic_button': 'inline-block', 'show_topic_button': 'inline-block',
'no_topics_message': 'hidden', 'no_topics_message': 'hidden',

@ -75,8 +75,7 @@ var async = require('async'),
server_conf = config, server_conf = config,
client_conf = { client_conf = {
socket: { socket: {
address: protocol + '//' + host, address: protocol + '//' + host + (config.use_port ? ':' + config.port : '')
port: config.port
}, },
api_url: protocol + '//' + host + (config.use_port ? ':' + config.port : '') + relative_path + '/api/', api_url: protocol + '//' + host + (config.use_port ? ':' + config.port : '') + relative_path + '/api/',
relative_path: relative_path relative_path: relative_path

@ -55,7 +55,7 @@ var RDB = require('./redis.js'),
}); });
} }
PostTools.edit = function(uid, pid, title, content) { PostTools.edit = function(uid, pid, title, content, images) {
var success = function() { var success = function() {
posts.setPostField(pid, 'content', content); posts.setPostField(pid, 'content', content);
posts.setPostField(pid, 'edited', Date.now()); posts.setPostField(pid, 'edited', Date.now());
@ -66,6 +66,11 @@ var RDB = require('./redis.js'),
}); });
async.parallel([ async.parallel([
function(next) {
posts.uploadPostImages(pid, images, function(err, uploadedImages) {
next(err, uploadedImages);
});
},
function(next) { function(next) {
posts.getPostField(pid, 'tid', function(tid) { posts.getPostField(pid, 'tid', function(tid) {
PostTools.isMain(pid, tid, function(isMainPost) { PostTools.isMain(pid, tid, function(isMainPost) {
@ -76,7 +81,7 @@ var RDB = require('./redis.js'),
}); });
} }
next(null, tid); next(null, {tid:tid, isMainPost:isMainPost});
}); });
}); });
}, },
@ -84,10 +89,12 @@ var RDB = require('./redis.js'),
PostTools.toHTML(content, next); PostTools.toHTML(content, next);
} }
], function(err, results) { ], function(err, results) {
io.sockets.in('topic_' + results[0]).emit('event:post_edited', { io.sockets.in('topic_' + results[1].tid).emit('event:post_edited', {
pid: pid, pid: pid,
title: title, title: title,
content: results[1] isMainPost: results[1].isMainPost,
content: results[2],
uploadedImages:results[0]
}); });
}); });
}; };
@ -179,6 +186,7 @@ var RDB = require('./redis.js'),
} }
PostTools.toHTML = function(raw, callback) { PostTools.toHTML = function(raw, callback) {
raw = raw || '';
plugins.fireHook('filter:post.parse', raw, function(parsed) { plugins.fireHook('filter:post.parse', raw, function(parsed) {
var cheerio = require('cheerio'); var cheerio = require('cheerio');

@ -332,7 +332,7 @@ var RDB = require('./redis.js'),
async.parallel({ async.parallel({
uploadedImages: function(next) { uploadedImages: function(next) {
uploadPostImages(postData, images, function(err, uploadedImages) { Posts.uploadPostImages(postData.pid, images, function(err, uploadedImages) {
if(err) { if(err) {
winston.error('Uploading images failed!', err.stack); winston.error('Uploading images failed!', err.stack);
next(null, []); next(null, []);
@ -350,7 +350,6 @@ var RDB = require('./redis.js'),
} }
}, function(err, results) { }, function(err, results) {
postData.uploadedImages = results.uploadedImages; postData.uploadedImages = results.uploadedImages;
Posts.setPostField(pid, 'uploadedImages', JSON.stringify(postData.uploadedImages));
postData.content = results.content; postData.content = results.content;
callback(postData); callback(postData);
}); });
@ -366,41 +365,44 @@ var RDB = require('./redis.js'),
}); });
} }
function uploadPostImages(postData, images, callback) { Posts.uploadPostImages = function(pid, images, callback) {
var imgur = require('./imgur'); var imgur = require('./imgur');
imgur.setClientID(meta.config.imgurClientID); imgur.setClientID(meta.config.imgurClientID);
var uploadedImages = []; if(!images)
return callback(null, []);
var uploadedImages = images.filter(function(image) { return !!image.url; });
function uploadImage(image, next) {
if(!image.data)
return next(null);
function uploadImage(image, callback) {
imgur.upload(image.data, 'base64', function(err, data) { imgur.upload(image.data, 'base64', function(err, data) {
if(err) { if(err) {
callback(err); next(err);
} else { } else {
if(data.success) { if(data.success) {
var img= {url:data.data.link, name:image.name}; var img= {url:data.data.link, name:image.name};
uploadedImages.push(img); uploadedImages.push(img);
callback(null); next(null);
} else { } else {
winston.error('Can\'t upload image, did you set imgurClientID?'); winston.error('Can\'t upload image, did you set imgurClientID?');
callback(data); next(data);
} }
} }
}); });
} }
if(!images) { async.each(images, uploadImage, function(err) {
callback(null, uploadedImages); if(!err) {
} else { Posts.setPostField(pid, 'uploadedImages', JSON.stringify(uploadedImages));
async.each(images, uploadImage, function(err) { callback(null, uploadedImages);
if(!err) { } else {
callback(null, uploadedImages); console.log(err);
} else { callback(err, null);
console.log(err); }
callback(err, null); });
}
});
}
} }
Posts.getPostsByUid = function(uid, start, end, callback) { Posts.getPostsByUid = function(uid, start, end, callback) {

@ -7,7 +7,8 @@ var utils = require('./../public/src/utils.js'),
bcrypt = require('bcrypt'), bcrypt = require('bcrypt'),
notifications = require('./notifications.js'), notifications = require('./notifications.js'),
topics = require('./topics.js'), topics = require('./topics.js'),
async = require('async'); async = require('async'),
userSearch = require('reds').createSearch('nodebbusersearch');
(function(User) { (function(User) {
User.create = function(username, password, email, callback) { User.create = function(username, password, email, callback) {
@ -90,6 +91,8 @@ var utils = require('./../public/src/utils.js'),
RDB.zadd('users:postcount', 0, uid); RDB.zadd('users:postcount', 0, uid);
RDB.zadd('users:reputation', 0, uid); RDB.zadd('users:reputation', 0, uid);
userSearch.index(username, uid);
io.sockets.emit('user.latest', {userslug: userslug, username: username}); io.sockets.emit('user.latest', {userslug: userslug, username: username});
if (password !== undefined) { if (password !== undefined) {
@ -385,25 +388,42 @@ var utils = require('./../public/src/utils.js'),
}); });
} }
User.reIndexAll = function(callback) {
User.getUsers('users:joindate', 0, -1, function(err, usersData) {
if(err) {
return callback(err, null);
}
function reIndexUser(uid, username) {
userSearch.remove(uid, function(){
userSearch.index(username, uid);
})
}
for(var i=0; i<usersData.length; ++i) {
reIndexUser(usersData[i].uid, usersData[i].username);
}
callback(null, 1);
});
}
User.search = function(username, callback) { User.search = function(username, callback) {
if(!username) { if(!username) {
callback([]); callback([]);
return; return;
} }
RDB.keys('username:*'+ username + '*:uid', function(err, keys) { userSearch.query(query = username).type('or').end(function(err, uids) {
if(!err) { if(err) {
if(keys && keys.length) {
RDB.mget(keys, function(err, uids) {
User.getDataForUsers(uids, function(userdata) {
callback(userdata);
});
});
} else {
callback([]);
}
} else {
console.log(err); console.log(err);
return;
}
if(uids && uids.length) {
User.getDataForUsers(uids, function(userdata) {
callback(userdata);
});
} else {
callback([]);
} }
}); });
} }

@ -58,7 +58,7 @@ var express = require('express'),
// Middlewares // Middlewares
app.use(express.favicon(path.join(__dirname, '../', 'public', 'favicon.ico'))); app.use(express.favicon(path.join(__dirname, '../', 'public', 'favicon.ico')));
app.use(require('less-middleware')({ src: path.join(__dirname, '../', 'public') })); app.use(require('less-middleware')({ src: path.join(__dirname, '../', 'public'), prefix:nconf.get('relative_path') }));
app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public'))); app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public')));
app.use(express.bodyParser()); // Puts POST vars in request.body app.use(express.bodyParser()); // Puts POST vars in request.body
app.use(express.cookieParser()); // If you want to parse cookies (res.cookies) app.use(express.cookieParser()); // If you want to parse cookies (res.cookies)
@ -295,6 +295,11 @@ var express = require('express'),
async.waterfall([ async.waterfall([
function(next) { function(next) {
categories.getCategoryById(cid, 0, function(err, categoryData) { categories.getCategoryById(cid, 0, function(err, categoryData) {
if(categoryData) {
if(categoryData.disabled === '1')
return next(new Error('Category disabled'), null);
}
next(err, categoryData); next(err, categoryData);
}); });
}, },
@ -403,10 +408,16 @@ var express = require('express'),
app.get('/reindex', function(req, res) { app.get('/reindex', function(req, res) {
topics.reIndexAll(function(err) { topics.reIndexAll(function(err) {
if(err) { if(err) {
res.json(err); return res.json(err);
} else {
res.send('All topics reindexed');
} }
user.reIndexAll(function(err) {
if(err) {
return res.json(err);
} else {
res.send('Topics and users reindexed');
}
});
}); });
}); });

@ -32,8 +32,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
(function(io) { (function(io) {
var users = {}, var users = {},
userSockets = {}, userSockets = {},
rooms = {}, rooms = {};
chats = {};
global.io = io; global.io = io;
@ -57,13 +56,6 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
user.getUserField(uid, 'username', function(err, username) { user.getUserField(uid, 'username', function(err, username) {
socket.emit('event:connect', {status: 1, username:username, uid:uid}); socket.emit('event:connect', {status: 1, username:username, uid:uid});
if(chats[uid]) {
for(var i=0; i<chats[uid].length; ++i) {
io.sockets.in(chats[uid][i]).emit('chatMessage', {fromuid:uid, username:username, message: username+' came online\n', timestamp: Date.now()});
socket.join(chats[uid][i]);
}
}
}); });
} }
}); });
@ -82,18 +74,6 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
delete users[sessionID]; delete users[sessionID];
if(uid) { if(uid) {
io.sockets.in('global').emit('api:user.isOnline', isUserOnline(uid)); io.sockets.in('global').emit('api:user.isOnline', isUserOnline(uid));
user.getUserField(uid, 'username', function(err, username) {
if(chats[uid] && chats[uid].length) {
for(var i=0; i<chats[uid].length; ++i) {
io.sockets.in(chats[uid][i]).emit('chatGoOffline', {uid:uid, username:username, timestamp:Date.now()});
socket.leave(chats[uid][i]);
}
}
});
} }
} }
@ -269,8 +249,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('api:user.get_online_users', returnData); socket.emit('api:user.get_online_users', returnData);
}); });
socket.on('api:user.isOnline', function(uid) { socket.on('api:user.isOnline', function(uid, callback) {
socket.emit('api:user.isOnline', isUserOnline(uid)); callback({online:isUserOnline(uid), timestamp:Date.now()});
}); });
socket.on('api:user.changePassword', function(data, callback) { socket.on('api:user.changePassword', function(data, callback) {
@ -363,7 +343,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('event:alert', { socket.emit('event:alert', {
title: 'Thank you for posting', title: 'Thank you for posting',
message: 'You have successfully posted. Click here to view your post.', message: 'You have successfully posted. Click here to view your post.',
type: 'warning', type: 'success',
timeout: 2000 timeout: 2000
}); });
} }
@ -416,7 +396,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('event:alert', { socket.emit('event:alert', {
title: 'Reply Successful', title: 'Reply Successful',
message: 'You have successfully replied. Click here to view your reply.', message: 'You have successfully replied. Click here to view your reply.',
type: 'warning', type: 'success',
timeout: 2000 timeout: 2000
}); });
@ -496,7 +476,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
posts.emitContentTooShortAlert(socket); posts.emitContentTooShortAlert(socket);
return; return;
} }
postTools.edit(uid, data.pid, data.title, data.content); postTools.edit(uid, data.pid, data.title, data.content, data.images);
}); });
socket.on('api:posts.delete', function(data) { socket.on('api:posts.delete', function(data) {
@ -548,15 +528,12 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
var msg = utils.strip_tags(data.message); var msg = utils.strip_tags(data.message);
var uids = [uid, touid].sort();
var chatroom = 'chatroom_'+uids[0]+'_'+uids[1];
user.getUserField(uid, 'username', function(err, username) { user.getUserField(uid, 'username', function(err, username) {
var finalMessage = username + ': ' + msg, var finalMessage = username + ' : ' + msg,
notifText = 'New message from <strong>' + username + '</strong>'; notifText = 'New message from <strong>' + username + '</strong>';
if(!isUserOnline(touid)) { if(!isUserOnline(touid)) {
notifications.create(notifText, 5, '#', 'notification_' + uid + '_' + touid, function(nid) { notifications.create(notifText, 5, 'javascript:app.openChat(&apos;'+username+'&apos;, '+uid+');', 'notification_' + uid + '_' + touid, function(nid) {
notifications.push(nid, [touid], function(success) { notifications.push(nid, [touid], function(success) {
}); });
@ -570,13 +547,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
numSockets = userSockets[touid].length; numSockets = userSockets[touid].length;
for(var x=0; x<numSockets; ++x) { for(var x=0; x<numSockets; ++x) {
userSockets[touid][x].join(chatroom);
userSockets[touid][x].emit('chatMessage', {fromuid:uid, username:username, message: finalMessage, timestamp: Date.now()}); userSockets[touid][x].emit('chatMessage', {fromuid:uid, username:username, message: finalMessage, timestamp: Date.now()});
} }
chats[touid] = chats[touid] || [];
if(chats[touid].indexOf(chatroom) === -1)
chats[touid].push(chatroom);
} }
if(userSockets[uid]) { if(userSockets[uid]) {
@ -584,13 +556,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
numSockets = userSockets[uid].length; numSockets = userSockets[uid].length;
for(var x=0; x<numSockets; ++x) { for(var x=0; x<numSockets; ++x) {
userSockets[uid][x].join(chatroom);
userSockets[uid][x].emit('chatMessage', {fromuid:touid, username:username, message:'You : ' + msg, timestamp: Date.now()}); userSockets[uid][x].emit('chatMessage', {fromuid:touid, username:username, message:'You : ' + msg, timestamp: Date.now()});
} }
chats[uid] = chats[uid] || [];
if(chats[uid].indexOf(chatroom) === -1)
chats[uid].push(chatroom);
} }
}); });
}); });
@ -616,7 +583,6 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
if (uid > 0) { if (uid > 0) {
if (parseInt(data.tid) > 0) { if (parseInt(data.tid) > 0) {
topics.getTopicData(data.tid, function(topicData) { topics.getTopicData(data.tid, function(topicData) {
if (data.body) if (data.body)
topicData.body = data.body; topicData.body = data.body;
@ -639,9 +605,17 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
} }
}); });
} else if (parseInt(data.pid) > 0) { } else if (parseInt(data.pid) > 0) {
async.parallel([ async.parallel([
function(next) { function(next) {
posts.getPostField(data.pid, 'content', function(raw) { posts.getPostFields(data.pid, ['content', 'uploadedImages'], function(raw) {
try {
raw.uploadedImages = JSON.parse(raw.uploadedImages);
} catch(e) {
winston.err(e);
raw.uploadedImages = [];
}
next(null, raw); next(null, raw);
}); });
}, },
@ -654,7 +628,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('api:composer.push', { socket.emit('api:composer.push', {
title: results[1], title: results[1],
pid: data.pid, pid: data.pid,
body: results[0] body: results[0].content,
uploadedImages: results[0].uploadedImages
}); });
}); });
} }

Loading…
Cancel
Save