Merge branch 'master' into user_groups

v1.18.x
Julian Lam 12 years ago
commit 5e869a5e5c

@ -16,7 +16,11 @@ Please support NodeBB development! Check out our IndieGoGo campaign and like, sh
## 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

@ -101,69 +101,87 @@
@-webkit-keyframes scroll-2 /* Safari and Chrome */
{
0% {top: 0px;}
25% {top: -90px;}
50% {top: -180px;}
75% {top: -270px;}
0% {top: 0px;}
3% {top:-90px;}
25% {top: -90px;}
28% {top:-180px;}
50% {top: -180px;}
53% {top: -270px;};
75% {top: -270px;}
78% {top: -360px;}
100% {top: -360px;}
}
@keyframes scroll-2
{
0% {top: 0px;}
25% {top: -90px;}
50% {top: -180px;}
75% {top: -270px;}
0% {top: 0px;}
3% {top:-90px;}
25% {top: -90px;}
28% {top:-180px;}
50% {top: -180px;}
53% {top: -270px;};
75% {top: -270px;}
78% {top: -360px;}
100% {top: -360px;}
}
@-webkit-keyframes scroll-1 /* Safari and Chrome */
{
0% {top: 0px;}
33% {top: -90px;}
66% {top: -180px;}
100% {top: -270px;}
0% {top: 0px;}
3% {top:-90px;}
33% {top: -90px;}
36% {top: -180px;}
66% {top: -180px;}
69% {top: -270px;}
100% {top: -270px;}
}
@keyframes scroll-1
{
0% {top: 0px;}
33% {top: -90px;}
66% {top: -180px;}
100% {top: -270px;}
0% {top: 0px;}
3% {top:-90px;}
33% {top: -90px;}
36% {top: -180px;}
66% {top: -180px;}
69% {top: -270px;}
100% {top: -270px;}
}
@-webkit-keyframes scroll-0 /* Safari and Chrome */
{
0% {top: 0px;}
50% {top: -90px;}
100% {top: -180px;}
0% {top: 0px;}
3% {top:-90px;}
50% {top: -90px;}
53% {top: -180px;}
100% {top: -180px;}
}
@keyframes scroll-0
{
0% {top: 0px;}
50% {top: -90px;}
100% {top: -180px;}
0% {top: 0px;}
3% {top:-90px;}
50% {top: -90px;}
53% {top: -180px;}
100% {top: -180px;}
}
.category-slider-2:hover {
position:relative;
-webkit-animation: scroll-2 10s ease 0.5s infinite normal;
animation: scroll-2 10s ease 0.5s infinite normal;/* Safari and Chrome: */
-webkit-animation: scroll-2 10s ease-in 0.5s infinite normal;
animation: scroll-2 10s ease-in 0.5s infinite normal;/* Safari and Chrome: */
}
.category-slider-1:hover {
position:relative;
-webkit-animation: scroll-1 8s ease 0.5s infinite normal;
animation: scroll-1 8s ease 0.5s infinite normal;/* Safari and Chrome: */
-webkit-animation: scroll-1 8s ease-in 0.5s infinite normal;
animation: scroll-1 8s ease-in 0.5s infinite normal;/* Safari and Chrome: */
}
.category-slider-0:hover {
position:relative;
-webkit-animation: scroll-0 6s ease 0.5s infinite normal;
animation: scroll-0 6s ease 0.5s infinite normal;/* Safari and Chrome: */
-webkit-animation: scroll-0 6s ease-in 0.5s infinite normal;
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 {
height:200px;
resize:none;
@ -257,6 +253,11 @@ footer.footer {
/* 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
@media (max-width: 979px) {
@ -266,9 +267,11 @@ footer.footer {
}
}
.container > .navbar-header, .container > .navbar-collapse {
padding-right: 0;
margin-right: -11px;
@media (min-width: 760px) {
.container > .navbar-header, .container > .navbar-collapse {
padding-right: 0;
margin-right: -11px;
}
}
.badge {

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

@ -15,7 +15,7 @@ var socket,
API_URL = data.api_url;
config = data;
socket = io.connect(config.socket.address + (config.socket.port ? ':' + config.socket.port : ''));
socket = io.connect(config.socket.address);
var reconnecting = false;
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) {
data.posts[0].display_moderator_tools = 'none';
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data),

@ -6,7 +6,7 @@
$(document).ready(function() {
app.addCommasToNumbers();
var followBtn = $('#follow-btn');
var unfollowBtn = $('#unfollow-btn');
@ -18,6 +18,9 @@
followBtn.show();
unfollowBtn.hide();
}
} else {
followBtn.hide();
unfollowBtn.hide();
}
followBtn.on('click', function() {
@ -51,20 +54,22 @@
$('.user-recent-posts .topic-row').on('click', function() {
ajaxify.go($(this).attr('topic-url'));
});
var onlineStatus = $('.account-online-status');
socket.on('api:user.isOnline', function(online) {
if(online) {
function handleUserOnline(data) {
if(data.online) {
onlineStatus.find('span span').text('online');
onlineStatus.find('i').attr('class', 'icon-circle');
} else {
onlineStatus.find('span span').text('offline');
onlineStatus.find('i').attr('class', 'icon-circle-blank');
}
});
socket.emit('api:user.isOnline', theirid);
}
socket.on('api:user.isOnline', handleUserOnline);
socket.emit('api:user.isOnline', theirid, handleUserOnline);
});

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

@ -5,9 +5,9 @@
followingCount = templates.get('followingCount');
$(document).ready(function() {
if(parseInt(followingCount, 10) === 0) {
$('#no-following-notice').show();
$('#no-following-notice').removeClass('hide');
}
@ -18,7 +18,7 @@
$('.unfollow-btn').on('click',function() {
var unfollowBtn = $(this);
var followingUid = $(this).attr('followingUid');
socket.emit('api:user.unfollow', {uid: followingUid}, function(success) {
var username = unfollowBtn.attr('data-username');
if(success) {
@ -34,6 +34,6 @@
app.addCommasToNumbers();
});
}());

@ -44,6 +44,16 @@
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'),
isLoggedIn = data.uid > 0;
@ -162,25 +172,18 @@
socket.on('chatMessage', function(data) {
require(['chat'], function(chat) {
var chatModal = chat.createModalIfDoesntExist(data.username, data.fromuid, function(created, modal) {
if(!created)
chat.appendChatMessage(modal, data.message, data.timestamp);
});
var modal = null;
if(chat.modalExists(data.fromuid)) {
modal = chat.getModal(data.fromuid);
chat.appendChatMessage(modal, data.message, data.timestamp);
} else {
modal = chat.createModal(data.username, data.fromuid);
}
chatModal.show();
chat.bringModalToTop(chatModal);
chat.load(modal.attr('UUID'));
});
});
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) {
mobileMenu.init();
});

@ -10,9 +10,14 @@
if (password.value.length < 6) {
$('#error').hide();
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';
} 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 });
}
}, false);

@ -67,7 +67,8 @@
return false;
});
moveThreadModal.on('shown', function() {
moveThreadModal.on('shown.bs.modal', function() {
var loadingEl = document.getElementById('categories-loading');
if (loadingEl) {
socket.once('api:categories.get', function(data) {
@ -287,11 +288,7 @@
if(username === app.username || !app.username)
return;
require(['chat'], function(chat) {
var chatModal = chat.createModalIfDoesntExist(username, touid);
chatModal.modal();
chat.bringModalToTop(chatModal); // I don't think this is necessary
});
app.openChat(username, touid);
});
ajaxify.register_events([
@ -379,6 +376,18 @@
this.innerHTML = data.content;
$(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) {

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

@ -8,7 +8,7 @@ define(['taskbar'], function(taskbar) {
};
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>');
closeButton.on('click', function(e) {
@ -152,7 +152,7 @@ define(['taskbar'], function(taskbar) {
pid: threadData.pid,
title: threadData.title || '',
body: threadData.body || '',
images: []
images: threadData.uploadedImages
};
composer.load(uuid);
} else {

@ -19,8 +19,8 @@
<span class="label label-danger">banned</span>
</div>
<div id="user-actions">
<a id="follow-btn" href="#" class="btn hide">Follow</a>
<a id="unfollow-btn" href="#" class="btn hide">Unfollow</a>
<a id="follow-btn" href="#" class="btn btn-default">Follow</a>
<a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a>
</div>
</div>

@ -25,7 +25,7 @@
<span class='formatted-number'>{following.postcount}</span>
<i class='icon-pencil'></i>
</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>
<!-- END following -->
</div>

@ -18,7 +18,7 @@
</div><!-- /.modal-dialog -->
</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-content">
<div class="modal-header">

@ -53,29 +53,35 @@
<li>
<a href="/users">Users</a>
</li>
<!--Enable when /search form is completed <li class="visible-xs">
<a href="/search">Search</a>
</li>-->
<li>
<a href="/"></a>
</li>
</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">
<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>
<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>
</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>
<div id="pagination"></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="alert alert-success" id="success" style="display:none">
<button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Password Reset Sent</strong>
<p></p>
</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>
<strong>Invalid Email!</strong>
<p></p>
<strong>Invalid Email / Email does not exist!</strong>
</div>
<label for="email">Email Address</label><input type="text" placeholder="Enter Email Address" id="email" /><br />
<button class="btn btn-primary" id="reset" type="submit">Reset Password</button>
<form onsubmit="return false;">
<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>
<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="alert alert-success" id="success" style="display:none">
<button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Password Changed</strong>
<p>Password successfully reset, please <a href="/login">log in again</a>.</p>
</div>
<div class="alert" id="notice" style="display:none">
<div class="alert alert-warning" id="notice" style="display:none">
<strong></strong>
<p></p>
</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>
<p>The reset code received was incorrect. Please try again, or <a href="/reset">request a new reset code</a></p>
</div>
<div id="reset-form">
<label for="password">New Password</label><input type="password" placeholder="A new password" id="password" /><br />
<label for="repeat">... and again</label><input type="password" placeholder="The same password" id="repeat" /><br />
<button class="btn btn-primary" id="reset" type="submit" disabled>Reset Password</button>
</div>
<form onsubmit="return false;" id="reset-form">
<label for="password">New Password</label>
<input class="form-control input-lg" type="password" placeholder="A new password" id="password" /><br />
<label for="repeat">Confirm Password</label>
<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>
<input type="hidden" template-variable="reset_code" value="{reset_code}" />

@ -170,23 +170,28 @@
</div>
</div>
<div id="move_thread_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Move Thread</h3>
</div>
<div class="modal-body">
<p id="categories-loading"><i class="icon-spin icon-refresh"></i> Loading Categories</p>
<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 id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Chat" 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">&times;</button>
<h3>Move Thread</h3>
</div>
<div class="modal-body">
<p id="categories-loading"><i class="icon-spin icon-refresh"></i> Loading Categories</p>
<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 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>
<input type="hidden" template-variable="expose_tools" value="{expose_tools}" />

@ -1,7 +1,7 @@
<div>
<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-reputation'>Most Reputation</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,
category_slug = categoryData.slug,
disabled = categoryData.disabled || '0',
category_description = categoryData.description;
function getTopicIds(next) {
@ -32,6 +33,7 @@ var RDB = require('./redis.js'),
var categoryData = {
'category_name' : category_name,
'category_description': category_description,
'disabled': disabled,
'show_sidebar' : 'show',
'show_topic_button': 'inline-block',
'no_topics_message': 'hidden',

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

@ -18,17 +18,19 @@ var fs = require('fs'),
RDB.smembers('plugins:active', next);
},
function(plugins, next) {
async.each(plugins, function(plugin) {
// TODO: Update this check to also check node_modules
var pluginPath = path.join(__dirname, '../plugins/', plugin),
modulePath = path.join(__dirname, '../node_modules/', plugin);
if (fs.existsSync(pluginPath)) _self.loadPlugin(pluginPath, next);
else if (fs.existsSync(modulePath)) _self.loadPlugin(modulePath, next);
else {
if (global.env === 'development') winston.info('[plugins] Plugin \'' + plugin + '\' not found');
next(); // Ignore this plugin silently
}
}, next);
if (plugins && Array.isArray(plugins) && plugins.length > 0) {
async.each(plugins, function(plugin) {
// TODO: Update this check to also check node_modules
var pluginPath = path.join(__dirname, '../plugins/', plugin),
modulePath = path.join(__dirname, '../node_modules/', plugin);
if (fs.existsSync(pluginPath)) _self.loadPlugin(pluginPath, next);
else if (fs.existsSync(modulePath)) _self.loadPlugin(modulePath, next);
else {
if (global.env === 'development') winston.info('[plugins] Plugin \'' + plugin + '\' not found');
next(); // Ignore this plugin silently
}
}, next);
} else next();
}
], function(err) {
if (err) {

@ -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() {
posts.setPostField(pid, 'content', content);
posts.setPostField(pid, 'edited', Date.now());
@ -66,6 +66,11 @@ var RDB = require('./redis.js'),
});
async.parallel([
function(next) {
posts.uploadPostImages(pid, images, function(err, uploadedImages) {
next(err, uploadedImages);
});
},
function(next) {
posts.getPostField(pid, 'tid', function(tid) {
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);
}
], 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,
title: title,
content: results[1]
isMainPost: results[1].isMainPost,
content: results[2],
uploadedImages:results[0]
});
});
};
@ -160,7 +167,7 @@ var RDB = require('./redis.js'),
});
});
// Restore topic if it is the only post
// Restore topic if it is the only post
topics.getTopicField(postData.tid, 'postcount', function(err, count) {
if (count === '1') {
threadTools.restore(postData.tid, uid);
@ -179,6 +186,7 @@ var RDB = require('./redis.js'),
}
PostTools.toHTML = function(raw, callback) {
raw = raw || '';
plugins.fireHook('filter:post.parse', raw, function(parsed) {
var cheerio = require('cheerio');

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

@ -7,7 +7,8 @@ var utils = require('./../public/src/utils.js'),
bcrypt = require('bcrypt'),
notifications = require('./notifications.js'),
topics = require('./topics.js'),
async = require('async');
async = require('async'),
userSearch = require('reds').createSearch('nodebbusersearch');
(function(User) {
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:reputation', 0, uid);
userSearch.index(username, uid);
io.sockets.emit('user.latest', {userslug: userslug, username: username});
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) {
if(!username) {
callback([]);
return;
}
RDB.keys('username:*'+ username + '*:uid', function(err, keys) {
if(!err) {
if(keys && keys.length) {
RDB.mget(keys, function(err, uids) {
User.getDataForUsers(uids, function(userdata) {
callback(userdata);
});
});
} else {
callback([]);
}
} else {
userSearch.query(query = username).type('or').end(function(err, uids) {
if(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
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(express.bodyParser()); // Puts POST vars in request.body
app.use(express.cookieParser()); // If you want to parse cookies (res.cookies)
@ -295,6 +295,11 @@ var express = require('express'),
async.waterfall([
function(next) {
categories.getCategoryById(cid, 0, function(err, categoryData) {
if(categoryData) {
if(categoryData.disabled === '1')
return next(new Error('Category disabled'), null);
}
next(err, categoryData);
});
},
@ -403,10 +408,16 @@ var express = require('express'),
app.get('/reindex', function(req, res) {
topics.reIndexAll(function(err) {
if(err) {
res.json(err);
} else {
res.send('All topics reindexed');
return res.json(err);
}
user.reIndexAll(function(err) {
if(err) {
return res.json(err);
} else {
res.send('Topics and users reindexed');
}
});
});
});
});

@ -33,8 +33,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
(function(io) {
var users = {},
userSockets = {},
rooms = {},
chats = {};
rooms = {};
global.io = io;
@ -58,13 +57,6 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
user.getUserField(uid, 'username', function(err, username) {
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]);
}
}
});
}
});
@ -83,18 +75,6 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
delete users[sessionID];
if(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]);
}
}
});
}
}
@ -270,8 +250,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('api:user.get_online_users', returnData);
});
socket.on('api:user.isOnline', function(uid) {
socket.emit('api:user.isOnline', isUserOnline(uid));
socket.on('api:user.isOnline', function(uid, callback) {
callback({online:isUserOnline(uid), timestamp:Date.now()});
});
socket.on('api:user.changePassword', function(data, callback) {
@ -364,7 +344,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('event:alert', {
title: 'Thank you for posting',
message: 'You have successfully posted. Click here to view your post.',
type: 'warning',
type: 'success',
timeout: 2000
});
}
@ -417,7 +397,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('event:alert', {
title: 'Reply Successful',
message: 'You have successfully replied. Click here to view your reply.',
type: 'warning',
type: 'success',
timeout: 2000
});
@ -497,7 +477,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
posts.emitContentTooShortAlert(socket);
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) {
@ -549,15 +529,12 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
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) {
var finalMessage = username + ': ' + msg,
var finalMessage = username + ' : ' + msg,
notifText = 'New message from <strong>' + username + '</strong>';
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) {
});
@ -571,13 +548,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
numSockets = userSockets[touid].length;
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()});
}
chats[touid] = chats[touid] || [];
if(chats[touid].indexOf(chatroom) === -1)
chats[touid].push(chatroom);
}
if(userSockets[uid]) {
@ -585,13 +557,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
numSockets = userSockets[uid].length;
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()});
}
chats[uid] = chats[uid] || [];
if(chats[uid].indexOf(chatroom) === -1)
chats[uid].push(chatroom);
}
});
});
@ -617,7 +584,6 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
if (uid > 0) {
if (parseInt(data.tid) > 0) {
topics.getTopicData(data.tid, function(topicData) {
if (data.body)
topicData.body = data.body;
@ -640,9 +606,17 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
}
});
} else if (parseInt(data.pid) > 0) {
async.parallel([
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);
});
},
@ -655,7 +629,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.emit('api:composer.push', {
title: results[1],
pid: data.pid,
body: results[0]
body: results[0].content,
uploadedImages: results[0].uploadedImages
});
});
}

Loading…
Cancel
Save