fixed conflicts, added new language key for users/online

v1.18.x
psychobunny 12 years ago
commit 1fbc038e64

3
.gitignore vendored

@ -6,6 +6,7 @@ npm-debug.log
node_modules/
sftp-config.json
config.json
public/src/nodebb.min.js
public/config.json
public/css/*.css
public/themes/*
@ -15,4 +16,4 @@ public/themes/*
*.sublime-project
*.sublime-workspace
plugins/*
.project
.project

@ -1,9 +1,12 @@
Please support NodeBB development! Check out our IndieGoGo campaign and like, share, and follow us :)
[NodeBB Homepage](http://www.nodebb.org/ "NodeBB") # [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") # [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
# NodeBB
**NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8.
* [NodeBB Homepage](http://www.nodebb.org/ "NodeBB")
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
![NodeBB Main Category Listing](http://i.imgur.com/ZBrHqLr.png)
![NodeBB Topic Page](http://i.imgur.com/YSBA6Vr.png)

@ -24,8 +24,10 @@
nconf.argv().env();
var fs = require('fs'),
async = require('async'),
winston = require('winston'),
pkg = require('./package.json'),
path = require('path'),
meta;
// Runtime environment

@ -13,7 +13,7 @@
"socket.io": "~0.9.16",
"redis": "0.8.3",
"express": "3.2.0",
"express-namespace": "0.1.1",
"express-namespace": "~0.1.1",
"emailjs": "0.3.4",
"cookie": "0.0.6",
"connect-redis": "1.4.5",
@ -37,7 +37,9 @@
"nodebb-plugin-mentions": "~0.1.0",
"nodebb-plugin-markdown": "~0.1.0",
"rss": "~0.2.0",
"prompt": "~0.2.11"
"prompt": "~0.2.11",
"uglify-js": "~2.4.0",
"validator": "~1.5.1"
},
"bugs": {
"url": "https://github.com/designcreateplay/NodeBB/issues"

@ -2,6 +2,7 @@
"latest_users": "Latest Users",
"top_posters": "Top Posters",
"most_reputation": "Most Reputation",
"online": "Online",
"search": "Search",
"enter_username": "Enter a username to search",
"load_more": "Load More"

@ -62,9 +62,11 @@ var ajaxify = {};
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
if (quiet !== true) {
window.history.pushState({
"url": url
}, url, RELATIVE_PATH + "/" + url);
if (window.history && window.history.pushState) {
window.history.pushState({
"url": url
}, url, RELATIVE_PATH + "/" + url);
}
}
translator.load(tpl_url);
@ -105,26 +107,29 @@ var ajaxify = {};
// Enhancing all anchors to ajaxify...
$(document.body).on('click', 'a', function (e) {
function hrefEmpty(href) {
return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
}
if (hrefEmpty(this.href)) return;
var url = this.href.replace(rootUrl + '/', '');
if (this.target !== '') return;
if (this.protocol === 'javascript:') return;
if (!e.ctrlKey && e.which === 1) {
if (ajaxify.go(url)) {
if (this.host === window.location.host) {
var url = this.href.replace(rootUrl + '/', '');
if (ajaxify.go(url)) {
e.preventDefault();
}
} else {
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
e.preventDefault();
}
}
});
});
function exec_body_scripts(body_el) {
// modified from http://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
@ -156,7 +161,7 @@ var ajaxify = {};
var scripts = [],
script,
children_nodes = body_el.childNodes,
children_nodes = $(body_el).children(),
child,
i;

@ -7,7 +7,7 @@ var socket,
(function () {
var showWelcomeMessage = false;
function loadConfig() {
app.loadConfig = function() {
$.ajax({
url: RELATIVE_PATH + '/api/config',
@ -15,109 +15,114 @@ var socket,
API_URL = data.api_url;
config = data;
socket = io.connect(config.socket.address);
var reconnecting = false;
var reconnectTries = 0;
socket.on('event:connect', function (data) {
console.log('connected to nodebb socket: ', data);
app.username = data.username;
app.showLoginMessage();
});
if(socket) {
socket.disconnect();
socket.socket.connect();
} else {
socket = io.connect(config.socket.address);
var reconnecting = false;
var reconnectTries = 0;
socket.on('event:connect', function (data) {
app.username = data.username;
app.showLoginMessage();
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
});
socket.on('event:alert', function (data) {
app.alert(data);
});
socket.on('event:alert', function (data) {
app.alert(data);
});
socket.on('connect', function (data) {
if (reconnecting) {
setTimeout(function () {
app.alert({
alert_id: 'connection_alert',
title: 'Connected',
message: 'Connection successful.',
type: 'success',
timeout: 5000
socket.on('connect', function (data) {
if (reconnecting) {
setTimeout(function () {
app.alert({
alert_id: 'connection_alert',
title: 'Connected',
message: 'Connection successful.',
type: 'success',
timeout: 5000
});
}, 1000);
reconnecting = false;
reconnectTries = 0;
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
}, 1000);
reconnecting = false;
reconnectTries = 0;
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
}
});
}
});
socket.on('reconnecting', function (data) {
function showDisconnectModal() {
$('#disconnect-modal').modal({
backdrop: 'static',
show: true
});
socket.on('reconnecting', function (data) {
function showDisconnectModal() {
$('#disconnect-modal').modal({
backdrop: 'static',
show: true
});
$('#reload-button').on('click', function () {
$('#disconnect-modal').modal('hide');
window.location.reload();
});
}
$('#reload-button').on('click', function () {
$('#disconnect-modal').modal('hide');
window.location.reload();
});
}
reconnecting = true;
reconnectTries++;
reconnecting = true;
reconnectTries++;
if (reconnectTries > 4) {
showDisconnectModal();
return;
}
if (reconnectTries > 4) {
showDisconnectModal();
return;
}
app.alert({
alert_id: 'connection_alert',
title: 'Reconnecting',
message: 'You have disconnected from NodeBB, we will try to reconnect you. <br/><i class="icon-refresh icon-spin"></i>',
type: 'warning',
timeout: 5000
app.alert({
alert_id: 'connection_alert',
title: 'Reconnecting',
message: 'You have disconnected from NodeBB, we will try to reconnect you. <br/><i class="icon-refresh icon-spin"></i>',
type: 'warning',
timeout: 5000
});
});
});
socket.on('api:user.get_online_users', function (users) {
jQuery('a.username-field').each(function () {
if (this.processed === true)
return;
socket.on('api:user.get_online_users', function (users) {
jQuery('a.username-field').each(function () {
if (this.processed === true)
return;
var el = jQuery(this),
uid = el.parents('li').attr('data-uid');
var el = jQuery(this),
uid = el.parents('li').attr('data-uid');
if (uid && jQuery.inArray(uid, users) !== -1) {
el.find('i').remove();
el.prepend('<i class="icon-circle"></i>');
} else {
el.find('i').remove();
el.prepend('<i class="icon-circle-blank"></i>');
}
if (uid && jQuery.inArray(uid, users) !== -1) {
el.find('i').remove();
el.prepend('<i class="icon-circle"></i>');
} else {
el.find('i').remove();
el.prepend('<i class="icon-circle-blank"></i>');
}
el.processed = true;
});
jQuery('button .username-field').each(function () {
//DRY FAIL
if (this.processed === true)
return;
el.processed = true;
});
jQuery('button .username-field').each(function () {
//DRY FAIL
if (this.processed === true)
return;
var el = jQuery(this),
uid = el.parents('li').attr('data-uid');
var el = jQuery(this),
uid = el.parents('li').attr('data-uid');
if (uid && jQuery.inArray(uid, users) !== -1) {
el.parent().addClass('btn-success');
} else {
el.parent().addClass('btn-danger');
}
if (uid && jQuery.inArray(uid, users) !== -1) {
el.parent().addClass('btn-success');
} else {
el.parent().addClass('btn-danger');
}
el.processed = true;
el.processed = true;
});
});
});
app.enter_room('global');
app.enter_room('global');
}
},
async: false
});
@ -275,7 +280,7 @@ var socket,
if (active) {
jQuery('#main-nav li a').each(function () {
var href = this.getAttribute('href');
if (active == "sort-posts" || active == "sort-reputation" || active == "search" || active == "latest")
if (active == "sort-posts" || active == "sort-reputation" || active == "search" || active == "latest" || active == "online")
active = 'users';
if (href && href.match(active)) {
jQuery(this.parentNode).addClass('active');
@ -286,6 +291,7 @@ var socket,
$('span.timeago').timeago();
setTimeout(function () {
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
}, 100);
@ -402,7 +408,8 @@ var socket,
function animateScroll() {
$('body,html').animate({
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height()
});
}, 400);
//$('body,html').scrollTop(scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height());
}
if (!scrollTo.length && tid) {
@ -427,19 +434,16 @@ var socket,
}
jQuery('document').ready(function () {
translator.load('global');
$('#search-form').on('submit', function () {
var input = $(this).find('input');
ajaxify.go("search/" + input.val(), null, "search");
input.val('');
return false;
})
});
});
showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
loadConfig();
app.loadConfig();
}());

@ -27,6 +27,7 @@ $(document).ready(function() {
},
uploadProgress: function(event, position, total, percent) {
console.log(percent);
$('#upload-progress-bar').css('width', percent + '%');
},
@ -60,25 +61,25 @@ $(document).ready(function() {
});
function hideAlerts() {
$('#alert-status').hide();
$('#alert-success').hide();
$('#alert-error').hide();
$('#upload-progress-box').hide();
$('#alert-status').addClass('hide');
$('#alert-success').addClass('hide');
$('#alert-error').addClass('hide');
$('#upload-progress-box').addClass('hide');
}
function status(message) {
hideAlerts();
$('#alert-status').text(message).show();
$('#alert-status').text(message).removeClass('hide');
}
function success(message) {
hideAlerts();
$('#alert-success').text(message).show();
$('#alert-success').text(message).removeClass('hide');
}
function error(message) {
hideAlerts();
$('#alert-error').text(message).show();
$('#alert-error').text(message).removeClass('hide');
}
function changeUserPicture(type) {

@ -26,7 +26,6 @@
fields: ['username', 'picture', 'userslug']
});
socket.on('api:updateHeader', function(data) {
jQuery('#search-button').on('click', function() {
jQuery('#search-fields').removeClass('hide').show();
jQuery(this).hide();
@ -59,6 +58,7 @@
if (data['username'])
userLabel.find('span').html(data['username']);
} else {
rightMenu.empty();
var userli = $('<li> \
<a id="user_label" href="/user/' + data['userslug'] + '"> \
<img src="' + data['picture'] + '"/> \
@ -67,7 +67,16 @@
</li>');
rightMenu.append(userli);
var logoutli = $('<li><a href="' + RELATIVE_PATH + '/logout">Log out</a></li>');
var logoutli = $('<li><a href="#">Log out</a></li>');
logoutli.on('click', function() {
var csrf_token = $('#csrf_token').val();
$.post(RELATIVE_PATH + '/logout', {
_csrf: csrf_token
}, function() {
window.location = RELATIVE_PATH + '/';
});
});
rightMenu.append(logoutli);
}
} else {
@ -86,6 +95,11 @@
right_menu.appendChild(registerEl);
right_menu.appendChild(loginEl);
}
$('#main-nav a,#right-menu a').off('click').on('click', function() {
if($('.navbar .navbar-collapse').hasClass('in'))
$('.navbar-header button').click();
});
});
// Notifications dropdown

@ -32,7 +32,12 @@
$('#login-error-notify').show();
} else {
$('#login-error-notify').hide();
window.location.replace(RELATIVE_PATH + "/?loggedin");
//window.location.replace(RELATIVE_PATH + "/?loggedin");
history.go(-1);
//setTimeout(function(){
app.loadConfig();
//}, 500);
//socket.emit('api:updateHeader');
}
},
error: function(data, textStatus, jqXHR) {

@ -12,6 +12,13 @@
$('#search-form input').val(searchQuery);
$('#mobile-search-form').off('submit').on('submit', function() {
var input = $(this).find('input');
ajaxify.go("search/" + input.val(), null, "search");
input.val('');
return false;
});
});
})();

@ -215,18 +215,29 @@
}, false);
}
enableInfiniteLoading();
var bookmark = localStorage.getItem('topic:' + tid + ':bookmark');
if(bookmark) {
app.scrollToPost(parseInt(bookmark, 10));
}
$('#post-container').on('click', '.deleted', function(ev) {
$(this).toggleClass('deleted-expanded');
});
});
function enableInfiniteLoading() {
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
app.loadMorePosts(tid);
console.log('window scrolling');
}
});
$('#post-container').on('click', '.deleted', function(ev) {
$(this).toggleClass('deleted-expanded');
});
});
}
var reply_fn = function() {
var selectionText = '',
@ -659,6 +670,7 @@
var scrollBottom = scrollTop + windowHeight;
if (scrollTop < 50 && postcount > 1) {
localStorage.removeItem("topic:" + tid + ":bookmark");
postAuthorImage.src = (jQuery('.main-post .avatar img').attr('src'));
mobileAuthorOverlay.innerHTML = 'Posted by ' + jQuery('.main-post').attr('data-username') + ', ' + jQuery('.main-post').find('.relativeTimeAgo').html();
pagination.innerHTML = '0 out of ' + postcount;
@ -666,7 +678,7 @@
}
var count = 0;
var count = 0, smallestNonNegative = 0;
jQuery('.sub-posts').each(function() {
count++;
@ -682,6 +694,11 @@
if (inView) {
if(elTop - scrollTop > smallestNonNegative) {
localStorage.setItem("topic:" + tid + ":bookmark", el.attr('data-pid'));
smallestNonNegative = Number.MAX_VALUE;
}
pagination.innerHTML = this.postnumber + ' out of ' + postcount;
postAuthorImage.src = (jQuery(this).find('.profile-image-block img').attr('src'));
mobileAuthorOverlay.innerHTML = 'Posted by ' + jQuery(this).attr('data-username') + ', ' + jQuery(this).find('.relativeTimeAgo').html();

@ -74,7 +74,12 @@
});
socket.on('api:user.isOnline', function(data) {
if(active == 'online' && !loadingMoreUsers) {
$('#users-container').empty();
startLoading('users:online', 0);
}
});
function onUsersLoaded(users) {
var html = templates.prepare(templates['users'].blocks['users']).parse({
@ -91,24 +96,32 @@
set = 'users:postcount';
} else if (active === 'sort-reputation') {
set = 'users:reputation';
} else if (active === 'online') {
set = 'users:online';
}
if (set) {
loadingMoreUsers = true;
socket.emit('api:users.loadMore', {
set: set,
after: $('#users-container').children().length
}, function(data) {
if (data.users.length) {
onUsersLoaded(data.users);
} else {
$('#load-more-users-btn').addClass('disabled');
}
loadingMoreUsers = false;
});
startLoading(set, $('#users-container').children().length);
}
}
function startLoading(set, after) {
loadingMoreUsers = true;
socket.emit('api:users.loadMore', {
set: set,
after: after
}, function(data) {
if (data.users.length) {
onUsersLoaded(data.users);
$('#load-more-users-btn').removeClass('disabled');
} else {
$('#load-more-users-btn').addClass('disabled');
}
loadingMoreUsers = false;
});
}
$('#load-more-users-btn').on('click', loadMoreUsers);
$(window).off('scroll').on('scroll', function() {

@ -180,7 +180,6 @@
else
template_data['relative_path'] = RELATIVE_PATH;
translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
document.getElementById('content').innerHTML = translatedTemplate;
});
@ -233,7 +232,7 @@
}
function makeRegex(block) {
return new RegExp("<!-- BEGIN " + block + " -->[^]*<!-- END " + block + " -->", 'g');
return new RegExp("<!-- BEGIN " + block + " -->[\\s\\S]*<!-- END " + block + " -->", 'g');
}
function getBlock(regex, block, template) {
@ -302,7 +301,7 @@
}
if (namespace) {
var regex = new RegExp("{" + namespace + "[^]*?}", 'g');
var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
template = template.replace(regex, '');
}

@ -48,13 +48,15 @@
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
</form>
<div id="upload-progress-box" class="progress progress-striped active hide">
<div id="upload-progress-bar" class="bar" style="width: 0%;"></div>
<div id="upload-progress-box" class="progress progress-striped">
<div id="upload-progress-bar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0">
<span class="sr-only"> success</span>
</div>
</div>
<div id="alert-status" class="alert hide"></div>
<div id="alert-status" class="alert alert-info hide"></div>
<div id="alert-success" class="alert alert-success hide"></div>
<div id="alert-error" class="alert alert-error hide"></div>
<div id="alert-error" class="alert alert-danger hide"></div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>

@ -15,7 +15,7 @@
<div class="alert alert-warning">
<p>
<strong>Interesed in writing plugins for NodeBB?</strong>
<strong>Interested in writing plugins for NodeBB?</strong>
</p>
<p>
Full documentation regarding plugin authoring can be found in the <a target="_blank" href="https://github.com/designcreateplay/NodeBB/wiki/Writing-Plugins-for-NodeBB">NodeBB Wiki</a>.

@ -9,7 +9,9 @@
<label>Site Description</label>
<input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br />
<label>Imgur Client ID</label>
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" />
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
<label>Maximum User Image Size</label>
<input type="text" class="form-control" placeholder="Maximum size of uploaded user images in kilobytes" data-field="maximumProfileImageSize" />
</form>
</div>

@ -28,29 +28,35 @@
<div class="{topic_row_size}">
<ul id="topics-container">
<!-- BEGIN topics -->
<a href="../../topic/{topics.slug}"><li class="category-item {topics.deleted-class}">
<div class="row">
<div class="col-md-12 topic-row">
<div class="latest-post visible-lg visible-md">
<div class="pull-right">
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
<p>{topics.teaser_text}</p>
<p class="meta">
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
</p>
<li class="category-item {topics.deleted-class}">
<div class="row">
<div class="col-md-12 topic-row">
<div class="latest-post visible-lg visible-md">
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
<div class="pull-right">
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
<p>{topics.teaser_text}</p>
<p class="meta">
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
</p>
</div>
</a>
</div>
</div>
<div>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
<small>
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
<strong>{topics.username}</strong>.
</small>
<a href="../../topic/{topics.slug}">
<div>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
<small>
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
<strong>{topics.username}</strong>.
</small>
</div>
</a>
</div>
</div>
</div>
</li></a>
</li>
<!-- END topics -->
</ul>
</div>

@ -1,40 +1,40 @@
{
"custom_mapping": {
"admin/testing/categories[^]*": "admin/testing/categories",
"admin/topics[^]*": "admin/topics",
"admin/categories[^]*": "admin/categories",
"admin/users[^]*": "admin/users",
"admin/redis[^]*": "admin/redis",
"admin/index[^]*": "admin/index",
"admin/themes[^]*": "admin/themes",
"admin/plugins[^]*": "admin/plugins",
"^admin/settings[^]*": "admin/settings",
"admin/twitter[^]*": "admin/twitter",
"admin/facebook[^]*": "admin/facebook",
"admin/gplus[^]*": "admin/gplus",
"admin/testing/categories.*": "admin/testing/categories",
"admin/topics.*": "admin/topics",
"admin/categories.*": "admin/categories",
"admin/users.*": "admin/users",
"admin/redis.*": "admin/redis",
"admin/index.*": "admin/index",
"admin/themes.*": "admin/themes",
"admin/plugins.*": "admin/plugins",
"^admin/settings.*": "admin/settings",
"admin/twitter.*": "admin/twitter",
"admin/facebook.*": "admin/facebook",
"admin/gplus.*": "admin/gplus",
"admin/motd/?$": "admin/motd",
"admin/groups/?$": "admin/groups",
"install/?$": "install/mail",
"install/mail/?": "install/mail",
"install/social/?": "install/social",
"install/privileges/?": "install/privileges",
"users/sort-posts": "users",
"users/latest": "users",
"users/sort-reputation": "users",
"users/search": "users",
"user[^]*edit": "accountedit",
"user[^]*following": "following",
"user[^]*followers": "followers",
"user[^]*settings": "accountsettings",
"user[^]*favourites": "favourites",
"user/[^]*": "account",
"users/sort-posts": "users",
"users/latest": "users",
"users/sort-reputation": "users",
"users/search": "users",
"user.*edit": "accountedit",
"user.*following": "following",
"user.*followers": "followers",
"user.*settings": "accountsettings",
"user.*favourites": "favourites",
"user/.*": "account",
"recent": "recent",
"unread": "unread",
"popular": "category",
"active": "category",
"search": "search",
"reset/[^]*": "reset_code",
"reset/.*": "reset_code",
"reset": "reset"
},

@ -8,28 +8,18 @@
<script>
var RELATIVE_PATH = "{relative_path}";
</script>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="{relative_path}/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="{relative_path}/vendor/jquery/js/jquery.timeago.js"></script>
<script src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="{relative_path}/socket.io/socket.io.js"></script>
<script src="{relative_path}/src/app.js"></script>
<script src="{relative_path}/vendor/requirejs/require.js"></script>
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
<!-- BEGIN clientScripts -->
<script src="{relative_path}/{clientScripts.script}"></script>
<!-- END clientScripts -->
<script>
require.config({
baseUrl: "{relative_path}/src/modules",
waitSeconds: 3
});
</script>
<script src="{relative_path}/src/templates.js"></script>
<script src="{relative_path}/src/ajaxify.js"></script>
<script src="{relative_path}/src/translator.js"></script>
<script src="{relative_path}/src/jquery.form.js"></script>
<script src="{relative_path}/src/utils.js"></script>
<link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
</head>
<body>
@ -37,12 +27,14 @@
<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>
<a class="navbar-brand" href="/">{title}</a>
</div>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/">
<h1 class="navbar-brand forum-title">{title}</h1>
</a>
</div>
<div class="navbar-collapse collapse navbar-ex1-collapse">
<ul id="main-nav" class="nav navbar-nav">
@ -58,6 +50,9 @@
<li class="visible-xs">
<a href="/search">[[global:header.search]]</a>
</li>
<li class="visible-xs">
<a href="/search">Search</a>
</li>
<li>
<a href="/"></a>
</li>

@ -6,11 +6,12 @@
<ul class="topics">
<!-- BEGIN topics -->
<li>
<span class="timestamp">{topics.teaser_timestamp}</span>
<a href="../../topic/{topics.slug}">{topics.title} ({topics.postcount})</a>
<div class="teaser">
<img class="img-thumbnail" src="{topics.teaser_userpicture}" />
<p>
{topics.teaser_text} &mdash; {topics.teaser_timestamp} ago
{topics.teaser_text}
</p>
<div class="clear"></div>
</div>

@ -3,8 +3,18 @@
<li class="active">Search</li>
</ol>
<form id="mobile-search-form" class="navbar-form navbar-right visible-xs" role="search" method="GET" action="">
<div class="" id="search-fields">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" name="query" value="">
</div>
<button type="submit" class="btn btn-default hide">Search</button>
</div>
</form>
<div class="category search">
<div class="">
<div class="{show_results}">
<ul id="topics-container" data-search-query="{search_query}">
<h3>Topics</h3>
<div class="alert alert-info {show_no_topics}">No topics found!</div>

@ -3,6 +3,7 @@
<li class=''><a href='/users/latest'>[[users:latest_users]]</a></li>
<li class=''><a href='/users/sort-posts'>[[users:top_posters]]</a></li>
<li class=''><a href='/users/sort-reputation'>[[users:most_reputation]]</a></li>
<li class=''><a href='/users/online'>[[users:online]]</a></li>
<li class=''><a href='/users/search'>[[users:search]]</a></li>
</ul>

@ -7,7 +7,7 @@
padding-right: 30px;
}
a {
ul {
li {
list-style: none;
padding-bottom: 10px;
@ -105,6 +105,8 @@
width: 70%;
margin-left: 10px;
overflow: hidden;
max-height: 33px;
margin-bottom: 0px;
}
}
}

@ -22,6 +22,12 @@
}
}
.forum-title {
padding-top: 15px;
padding-bottom: 15px;
margin: 0px;
}
.pagination-block {
position: absolute;
background: rgb(34, 34, 34);

@ -32,6 +32,13 @@ noscript {
.default;
}
.timestamp {
float: right;
color: #999;
font-style: italic;
font-size: 12px;
}
.teaser {
margin-left: 16px;
margin-top: 8px;

File diff suppressed because it is too large Load Diff

@ -113,6 +113,9 @@ var async = require('async'),
}, {
field: 'imgurClientID',
value: ''
}, {
field: 'maximumProfileImageSize',
value: 256
}];
async.each(defaults, function (configObj, next) {

@ -6,19 +6,21 @@ var utils = require('./../public/src/utils.js'),
winston = require('winston'),
nconf = require('nconf');
(function(Meta) {
(function (Meta) {
Meta.configs = {
init: function(callback) {
Meta.configs.list(function(err, config) {
init: function (callback) {
Meta.configs.list(function (err, config) {
if (!err) {
Meta.config = config;
callback();
} else winston.error(err);
} else {
winston.error(err);
}
});
},
list: function(callback) {
RDB.hgetall('config', function(err, config) {
list: function (callback) {
RDB.hgetall('config', function (err, config) {
if (!err) {
config = config || {};
config.status = 'ok';
@ -28,52 +30,63 @@ var utils = require('./../public/src/utils.js'),
}
});
},
get: function(field, callback) {
get: function (field, callback) {
RDB.hget('config', field, callback);
},
getFields: function(fields, callback) {
getFields: function (fields, callback) {
RDB.hmgetObject('config', fields, callback);
},
set: function(field, value, callback) {
RDB.hset('config', field, value, function(err, res) {
if (callback)
set: function (field, value, callback) {
RDB.hset('config', field, value, function (err, res) {
if (callback) {
callback(err, res);
}
});
},
setOnEmpty: function(field, value, callback) {
this.get(field, function(err, curValue) {
if (!curValue) Meta.configs.set(field, value, callback);
else callback();
setOnEmpty: function (field, value, callback) {
this.get(field, function (err, curValue) {
if (!curValue) {
Meta.configs.set(field, value, callback);
} else {
callback();
}
});
},
remove: function(field) {
remove: function (field) {
RDB.hdel('config', field);
}
}
};
Meta.themes = {
get: function(callback) {
get: function (callback) {
var themePath = path.join(__dirname, '../node_modules');
fs.readdir(themePath, function(err, files) {
async.filter(files, function(file, next) {
fs.stat(path.join(themePath, file), function(err, fileStat) {
if (err) next(false);
fs.readdir(themePath, function (err, files) {
async.filter(files, function (file, next) {
fs.stat(path.join(themePath, file), function (err, fileStat) {
if (err) {
next(false);
}
next((fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-'));
});
}, function(themes) {
async.map(themes, function(theme, next) {
}, function (themes) {
async.map(themes, function (theme, next) {
var config = path.join(themePath, theme, 'theme.json');
if (fs.existsSync(config)) {
fs.readFile(config, function(err, file) {
fs.readFile(config, function (err, file) {
var configObj = JSON.parse(file.toString());
if (!configObj.screenshot) configObj.screenshot = nconf.get('relative_path') + '/images/themes/default.png';
if (!configObj.screenshot) {
configObj.screenshot = nconf.get('relative_path') + '/images/themes/default.png';
}
next(err, configObj);
});
} else next();
}, function(err, themes) {
themes = themes.filter(function(theme) {
} else {
next();
}
}, function (err, themes) {
themes = themes.filter(function (theme) {
return (theme !== undefined);
});
callback(null, themes);
@ -81,30 +94,33 @@ var utils = require('./../public/src/utils.js'),
});
});
}
}
};
Meta.title = {
build: function(urlFragment, current_user, callback) {
build: function (urlFragment, current_user, callback) {
var self = this,
user = require('./user');
async.parallel({
title: function(next) {
title: function (next) {
self.parseFragment(urlFragment, next);
},
notifCount: function(next) {
notifCount: function (next) {
user.notifications.getUnreadCount(current_user, next);
}
}, function(err, values) {
}, function (err, values) {
var title;
if (err) title = Meta.config.title || 'NodeBB';
else title = (values.title ? values.title + ' | ' : '') + (Meta.config.title || 'NodeBB');
if (err) {
title = Meta.config.title || 'NodeBB';
} else {
title = (values.title ? values.title + ' | ' : '') + (Meta.config.title || 'NodeBB');
}
callback(null, title, values.notifCount);
});
},
parseFragment: function(urlFragment, callback) {
parseFragment: function (urlFragment, callback) {
if (urlFragment === '') {
callback(null, 'Index');
} else if (urlFragment === 'recent') {
@ -116,18 +132,95 @@ var utils = require('./../public/src/utils.js'),
} else if (/^category\/\d+\/?/.test(urlFragment)) {
var cid = urlFragment.match(/category\/(\d+)/)[1];
require('./categories').getCategoryField(cid, 'name', function(err, name) {
require('./categories').getCategoryField(cid, 'name', function (err, name) {
callback(null, name);
});
} else if (/^topic\/\d+\/?/.test(urlFragment)) {
var tid = urlFragment.match(/topic\/(\d+)/)[1];
require('./topics').getTopicField(tid, 'title', function(err, title) {
require('./topics').getTopicField(tid, 'title', function (err, title) {
callback(null, title);
});
} else callback(null);
} else {
callback(null);
}
}
}
};
Meta.js = {
scripts: [
'vendor/jquery/js/jquery.js',
'vendor/jquery/js/jquery-ui-1.10.3.custom.min.js',
'vendor/jquery/js/jquery.timeago.js',
'vendor/bootstrap/js/bootstrap.min.js',
'src/app.js',
'vendor/requirejs/require.js',
'vendor/bootbox/bootbox.min.js',
'src/templates.js',
'src/ajaxify.js',
'src/translator.js',
'src/jquery.form.js',
'src/utils.js'
],
minFile: path.join(__dirname, '..', 'public/src/nodebb.min.js'),
get: function (callback) {
var mtime,
jsPaths = this.scripts.map(function (jsPath) {
return path.join(__dirname, '..', '/public', jsPath);
});
if (process.env.NODE_ENV !== 'development') {
async.parallel({
mtime: function (next) {
async.map(jsPaths, fs.stat, function (err, stats) {
async.reduce(stats, 0, function (memo, item, callback) {
mtime = +new Date(item.mtime);
callback(null, mtime > memo ? mtime : memo);
}, next);
});
},
minFile: function (next) {
if (!fs.existsSync(Meta.js.minFile)) {
winston.warn('No minified client-side library found');
return next(null, 0);
}
fs.stat(Meta.js.minFile, function (err, stat) {
next(err, +new Date(stat.mtime));
});
}
}, function (err, results) {
if (results.minFile > results.mtime) {
winston.info('No changes to client-side libraries -- skipping minification');
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
} else {
Meta.js.minify(function () {
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
});
}
});
} else {
callback(null, this.scripts);
}
},
minify: function (callback) {
var uglifyjs = require('uglify-js'),
jsPaths = this.scripts.map(function (jsPath) {
return path.join(__dirname, '..', '/public', jsPath);
}),
minified;
winston.info('Minifying client-side libraries');
minified = uglifyjs.minify(jsPaths);
fs.writeFile(Meta.js.minFile, minified.code, function (err) {
if (!err) {
winston.info('Minified client-side libraries');
callback();
} else {
winston.error('Problem minifying client-side libraries, exiting.');
process.exit();
}
});
}
};
}(exports));

@ -137,7 +137,7 @@ var fs = require('fs'),
hookList = this.loadedHooks[hook];
if (hookList && Array.isArray(hookList)) {
if (global.env === 'development') winston.info('[plugins] Firing hook: \'' + hook + '\'');
//if (global.env === 'development') winston.info('[plugins] Firing hook: \'' + hook + '\'');
var hookType = hook.split(':')[0];
switch (hookType) {
case 'filter':

@ -87,7 +87,7 @@ var RDB = require('./redis.js'),
});
},
function(next) {
PostTools.toHTML(content, next);
PostTools.parse(content, next);
}
], function(err, results) {
io.sockets.in('topic_' + results[0].tid).emit('event:post_edited', {
@ -128,7 +128,7 @@ var RDB = require('./redis.js'),
});
// Delete the thread if it is the last undeleted post
threadTools.get_latest_undeleted_pid(postData.tid, function(err, pid) {
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
if (err && err.message === 'no-undeleted-pids-found') {
threadTools.delete(postData.tid, -1, function(err) {
if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
@ -164,7 +164,7 @@ var RDB = require('./redis.js'),
pid: pid
});
threadTools.get_latest_undeleted_pid(postData.tid, function(err, pid) {
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
posts.getPostField(pid, 'timestamp', function(timestamp) {
topics.updateTimestamp(postData.tid, timestamp);
});
@ -190,28 +190,11 @@ var RDB = require('./redis.js'),
});
}
PostTools.toHTML = function(raw, callback) {
PostTools.parse = function(raw, callback) {
raw = raw || '';
plugins.fireHook('filter:post.parse', raw, function(parsed) {
var cheerio = require('cheerio');
if (parsed && parsed.length > 0) {
var parsedContentDOM = cheerio.load(parsed);
var domain = nconf.get('url');
parsedContentDOM('a').each(function() {
this.attr('rel', 'nofollow');
var href = this.attr('href');
if (href && !href.match(domain) && !utils.isRelativeUrl(href)) {
this.attr('href', domain + 'outgoing?url=' + encodeURIComponent(href));
}
});
callback(null, parsedContentDOM.html());
} else {
callback(null, '<p></p>');
}
plugins.fireHook('filter:post.parse', raw, function(parsed) {
callback(null, parsed);
});
}

@ -36,7 +36,7 @@ var RDB = require('./redis.js'),
user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(err, userData) {
if (err) return callback();
postTools.toHTML(userData.signature, function(err, signature) {
postTools.parse(userData.signature, function(err, signature) {
post.username = userData.username || 'anonymous';
post.userslug = userData.userslug || '';
post.user_rep = userData.reputation || 0;
@ -70,7 +70,7 @@ var RDB = require('./redis.js'),
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) {
if (postData.deleted === '1') return callback(null);
else {
postData.relativeTime = new Date(parseInt(postData.timestamp, 10)).toISOString();
postData.relativeTime = new Date(parseInt(postData.timestamp || 0, 10)).toISOString();
next(null, postData);
}
});
@ -91,7 +91,7 @@ var RDB = require('./redis.js'),
},
function(postData, next) {
if (postData.content) {
postTools.toHTML(postData.content, function(err, content) {
postTools.parse(postData.content, function(err, content) {
if (!err) postData.content = utils.strip_tags(content);
next(err, postData);
});
@ -164,7 +164,7 @@ var RDB = require('./redis.js'),
postData['edited-class'] = postData.editor !== '' ? '' : 'none';
postData['relativeEditTime'] = postData.edited !== '0' ? (new Date(parseInt(postData.edited,10)).toISOString()) : '';
postTools.toHTML(postData.content, function(err, content) {
postTools.parse(postData.content, function(err, content) {
postData.content = content;
posts.push(postData);
callback(null);
@ -321,7 +321,7 @@ var RDB = require('./redis.js'),
async.parallel({
content: function(next) {
plugins.fireHook('filter:post.get', postData, function(postData) {
postTools.toHTML(postData.content, function(err, content) {
postTools.parse(postData.content, function(err, content) {
next(null, content);
});
});

@ -8,24 +8,24 @@ var user = require('./../user.js'),
winston = require('winston'),
nconf = require('nconf');
(function(Admin) {
Admin.isAdmin = function(req, res, next) {
user.isAdministrator((req.user && req.user.uid) ? req.user.uid : 0, function(isAdmin) {
(function (Admin) {
Admin.isAdmin = function (req, res, next) {
user.isAdministrator((req.user && req.user.uid) ? req.user.uid : 0, function (isAdmin) {
if (!isAdmin) res.redirect('/403');
else next();
});
}
Admin.build_header = function(res) {
Admin.build_header = function (res) {
return templates['admin/header'].parse({
csrf: res.locals.csrf_token,
relative_path: nconf.get('relative_path')
});
}
Admin.create_routes = function(app) {
Admin.create_routes = function (app) {
(function() {
(function () {
var routes = [
'categories/active', 'categories/disabled', 'users', 'topics', 'settings', 'themes',
'twitter', 'facebook', 'gplus', 'redis', 'motd', 'groups',
@ -34,8 +34,8 @@ var user = require('./../user.js'),
];
for (var i = 0, ii = routes.length; i < ii; i++) {
(function(route) {
app.get('/admin/' + route, Admin.isAdmin, function(req, res) {
(function (route) {
app.get('/admin/' + route, Admin.isAdmin, function (req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/' + route) + templates['admin/footer']);
});
}(routes[i]));
@ -44,8 +44,8 @@ var user = require('./../user.js'),
var unit_tests = ['categories'];
for (var i = 0, ii = unit_tests.length; i < ii; i++) {
(function(route) {
app.get('/admin/testing/' + route, Admin.isAdmin, function(req, res) {
(function (route) {
app.get('/admin/testing/' + route, Admin.isAdmin, function (req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/testing/' + route) + templates['admin/footer']);
});
}(unit_tests[i]));
@ -53,174 +53,178 @@ var user = require('./../user.js'),
}());
app.get('/admin', Admin.isAdmin, function(req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']);
});
app.get('/admin/index', Admin.isAdmin, function(req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']);
});
app.get('/api/admin/index', function(req, res) {
res.json({
version: pkg.version
app.namespace('/admin', function () {
app.get('/', Admin.isAdmin, function (req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']);
});
});
app.get('/api/admin/users/search', function(req, res) {
res.json({
search_display: 'block',
loadmore_display: 'none',
users: []
app.get('/index', Admin.isAdmin, function (req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']);
});
});
app.get('/api/admin/users/latest', function(req, res) {
user.getUsers('users:joindate', 0, 49, function(err, data) {
app.namespace('/api/admin', function () {
app.get('/index', function (req, res) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data,
yourid: req.user.uid
version: pkg.version
});
});
});
app.get('/api/admin/users/sort-posts', function(req, res) {
user.getUsers('users:postcount', 0, 49, function(err, data) {
app.get('/users/search', function (req, res) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data,
yourid: req.user.uid
search_display: 'block',
loadmore_display: 'none',
users: []
});
});
});
app.get('/api/admin/users/sort-reputation', function(req, res) {
user.getUsers('users:reputation', 0, 49, function(err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data,
yourid: req.user.uid
app.get('/users/latest', function (req, res) {
user.getUsers('users:joindate', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data,
yourid: req.user.uid
});
});
});
});
app.get('/api/admin/users', function(req, res) {
user.getUsers('users:joindate', 0, 49, function(err, data) {
res.json({
search_display: 'none',
users: data,
yourid: req.user.uid
app.get('/users/sort-posts', function (req, res) {
user.getUsers('users:postcount', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data,
yourid: req.user.uid
});
});
});
});
app.get('/api/admin/categories', function(req, res) {
categories.getAllCategories(function(data) {
res.json(data);
app.get('/users/sort-reputation', function (req, res) {
user.getUsers('users:reputation', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data,
yourid: req.user.uid
});
});
});
});
app.get('/api/admin/categories/active', function(req, res) {
categories.getAllCategories(function(data) {
data.categories = data.categories.filter(function(category) {
return (!category.disabled || category.disabled === "0");
app.get('/users', function (req, res) {
user.getUsers('users:joindate', 0, 49, function (err, data) {
res.json({
search_display: 'none',
users: data,
yourid: req.user.uid
});
});
res.json(data);
});
});
app.get('/api/admin/categories/disabled', function(req, res) {
categories.getAllCategories(function(data) {
data.categories = data.categories.filter(function(category) {
return category.disabled === "1";
app.get('/categories', function (req, res) {
categories.getAllCategories(function (data) {
res.json(data);
});
res.json(data);
});
});
app.get('/api/admin/topics', function(req, res) {
topics.getAllTopics(10, null, function(topics) {
res.json({
topics: topics
app.get('/categories/active', function (req, res) {
categories.getAllCategories(function (data) {
data.categories = data.categories.filter(function (category) {
return (!category.disabled || category.disabled === "0");
});
res.json(data);
});
});
});
app.get('/api/admin/redis', function(req, res) {
RDB.info(function(err, data) {
data = data.split("\r\n");
var finalData = {};
app.get('/categories/disabled', function (req, res) {
categories.getAllCategories(function (data) {
data.categories = data.categories.filter(function (category) {
return category.disabled === "1";
});
res.json(data);
});
});
for (var i in data) {
app.get('/topics', function (req, res) {
topics.getAllTopics(10, null, function (topics) {
res.json({
topics: topics
});
});
});
if (data[i].indexOf(':') == -1 || !data[i])
continue;
app.get('/redis', function (req, res) {
RDB.info(function (err, data) {
data = data.split("\r\n");
var finalData = {};
try {
data[i] = data[i].replace(/:/, "\":\"");
var json = "{\"" + data[i] + "\"}";
for (var i in data) {
var jsonObject = JSON.parse(json);
for (var key in jsonObject) {
finalData[key] = jsonObject[key];
if (data[i].indexOf(':') == -1 || !data[i])
continue;
try {
data[i] = data[i].replace(/:/, "\":\"");
var json = "{\"" + data[i] + "\"}";
var jsonObject = JSON.parse(json);
for (var key in jsonObject) {
finalData[key] = jsonObject[key];
}
} catch (err) {
winston.warn('can\'t parse redis status variable, ignoring', i, data[i], err);
}
} catch (err) {
winston.warn('can\'t parse redis status variable, ignoring', i, data[i], err);
}
}
res.json(finalData);
res.json(finalData);
});
});
});
app.get('/api/admin/plugins', function(req, res) {
plugins.showInstalled(function(err, plugins) {
if (err || !Array.isArray(plugins)) plugins = [];
app.get('/plugins', function (req, res) {
plugins.showInstalled(function (err, plugins) {
if (err || !Array.isArray(plugins)) plugins = [];
res.json(200, {
plugins: plugins
res.json(200, {
plugins: plugins
});
});
});
});
app.get('/api/admin/settings', function(req, res) {
res.json(200, {});
});
app.get('/settings', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/motd', function(req, res) {
res.json(200, {});
});
app.get('/motd', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/themes', function(req, res) {
res.json(200, {});
});
app.get('/themes', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/twitter', function(req, res) {
res.json(200, {});
});
app.get('/twitter', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/facebook', function(req, res) {
res.json(200, {});
});
app.get('/facebook', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/gplus', function(req, res) {
res.json(200, {});
});
app.get('/gplus', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/testing/categories', function(req, res) {
res.json(200, {});
});
app.get('/testing/categories', function (req, res) {
res.json(200, {});
});
app.get('/api/admin/groups', function(req, res) {
Groups.list({
expand: true
}, function(err, groups) {
res.json(200, {
groups: groups
app.get('/groups', function (req, res) {
Groups.list({
expand: true
}, function (err, groups) {
res.json(200, {
groups: groups
});
});
});
});

@ -9,255 +9,259 @@ var user = require('./../user.js'),
nconf = require('nconf');
(function(Api) {
Api.create_routes = function(app) {
app.get('/api/get_templates_listing', function(req, res) {
utils.walk(path.join(__dirname, '../../', 'public/templates'), function(err, data) {
res.json(data);
(function (Api) {
Api.create_routes = function (app) {
app.namespace('/api', function () {
app.get('/get_templates_listing', function (req, res) {
utils.walk(path.join(__dirname, '../../', 'public/templates'), function (err, data) {
res.json(data);
});
});
});
app.get('/api/config', function(req, res, next) {
var config = require('../../public/config.json');
app.get('/config', function (req, res, next) {
var config = require('../../public/config.json');
config['postDelay'] = meta.config['postDelay'];
config['minimumTitleLength'] = meta.config['minimumTitleLength'];
config['minimumPostLength'] = meta.config['minimumPostLength'];
config['imgurClientIDSet'] = !! meta.config['imgurClientID'];
config['minimumUsernameLength'] = meta.config['minimumUsernameLength'];
config['maximumUsernameLength'] = meta.config['maximumUsernameLength'];
config['minimumPasswordLength'] = meta.config['minimumPasswordLength'];
config.postDelay = meta.config.postDelay;
config.minimumTitleLength = meta.config.minimumTitleLength;
config.minimumPostLength = meta.config.minimumPostLength;
config.imgurClientIDSet = !! meta.config.imgurClientID;
config.minimumUsernameLength = meta.config.minimumUsernameLength;
config.maximumUsernameLength = meta.config.maximumUsernameLength;
config.minimumPasswordLength = meta.config.minimumPasswordLength;
res.json(200, config);
});
res.json(200, config);
});
app.get('/api/home', function(req, res) {
var uid = (req.user) ? req.user.uid : 0;
categories.getAllCategories(function(data) {
data.categories = data.categories.filter(function(category) {
return (!category.disabled || category.disabled === "0");
});
app.get('/home', function (req, res) {
var uid = (req.user) ? req.user.uid : 0;
categories.getAllCategories(function (data) {
data.categories = data.categories.filter(function (category) {
return (!category.disabled || category.disabled === "0");
});
function iterator(category, callback) {
categories.getRecentReplies(category.cid, 2, function(posts) {
category["posts"] = posts;
category["post_count"] = posts.length > 2 ? 2 : posts.length;
callback(null);
function iterator(category, callback) {
categories.getRecentReplies(category.cid, 2, function (posts) {
category.posts = posts;
category.post_count = posts.length > 2 ? 2 : posts.length;
callback(null);
});
}
require('async').each(data.categories, iterator, function (err) {
data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : 'none';
data.motd = require('marked')(meta.config.motd || "# NodeBB <span>v " + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future.\n\n<div class='btn-group'><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"icon-comment\"></i><span class='hidden-mobile'>&nbsp;Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"icon-github-alt\"></i><span class='hidden-mobile'>&nbsp;Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"icon-twitter\"></i><span class='hidden-mobile'>&nbsp;@dcplabs</span></a></div>");
res.json(data);
});
}
require('async').each(data.categories, iterator, function(err) {
data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : 'none';
data.motd = require('marked')(meta.config.motd || "# NodeBB <span>v " + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future.\n\n<div class='btn-group'><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"icon-comment\"></i><span class='hidden-mobile'>&nbsp;Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"icon-github-alt\"></i><span class='hidden-mobile'>&nbsp;Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"icon-twitter\"></i><span class='hidden-mobile'>&nbsp;@dcplabs</span></a></div>");
res.json(data);
});
}, uid);
});
}, uid);
});
app.get('/login', function (req, res) {
var data = {},
login_strategies = auth.get_login_strategies(),
num_strategies = login_strategies.length;
app.get('/api/login', function(req, res) {
var data = {},
login_strategies = auth.get_login_strategies(),
num_strategies = login_strategies.length;
if (num_strategies == 0) {
data = {
'login_window:spansize': 'col-md-12',
'alternate_logins:display': 'none'
};
} else {
data = {
'login_window:spansize': 'col-md-6',
'alternate_logins:display': 'block'
}
for (var i = 0, ii = num_strategies; i < ii; i++) {
data[login_strategies[i] + ':display'] = 'active';
if (num_strategies == 0) {
data = {
'login_window:spansize': 'col-md-12',
'alternate_logins:display': 'none'
};
} else {
data = {
'login_window:spansize': 'col-md-6',
'alternate_logins:display': 'block'
}
for (var i = 0, ii = num_strategies; i < ii; i++) {
data[login_strategies[i] + ':display'] = 'active';
}
}
}
data.token = res.locals.csrf_token;
data.token = res.locals.csrf_token;
res.json(data);
});
res.json(data);
});
app.get('/api/register', function(req, res) {
var data = {},
login_strategies = auth.get_login_strategies(),
num_strategies = login_strategies.length;
if (num_strategies == 0) {
data = {
'register_window:spansize': 'col-md-12',
'alternate_logins:display': 'none'
};
} else {
data = {
'register_window:spansize': 'col-md-6',
'alternate_logins:display': 'block'
}
for (var i = 0, ii = num_strategies; i < ii; i++) {
data[login_strategies[i] + ':display'] = 'active';
app.get('/register', function (req, res) {
var data = {},
login_strategies = auth.get_login_strategies(),
num_strategies = login_strategies.length;
if (num_strategies == 0) {
data = {
'register_window:spansize': 'col-md-12',
'alternate_logins:display': 'none'
};
} else {
data = {
'register_window:spansize': 'col-md-6',
'alternate_logins:display': 'block'
}
for (var i = 0, ii = num_strategies; i < ii; i++) {
data[login_strategies[i] + ':display'] = 'active';
}
}
}
data.token = res.locals.csrf_token;
data.minimumUsernameLength = meta.config['minimumUsernameLength'];
data.maximumUsernameLength = meta.config['maximumUsernameLength'];
data.minimumPasswordLength = meta.config['minimumPasswordLength'];
res.json(data);
});
data.token = res.locals.csrf_token;
data.minimumUsernameLength = meta.config['minimumUsernameLength'];
data.maximumUsernameLength = meta.config['maximumUsernameLength'];
data.minimumPasswordLength = meta.config['minimumPasswordLength'];
res.json(data);
});
app.get('/api/topic/:id/:slug?', function(req, res, next) {
var uid = (req.user) ? req.user.uid : 0;
topics.getTopicWithPosts(req.params.id, uid, 0, 10, function(err, data) {
if (!err) {
if (data.deleted === '1' && data.expose_tools === 0) {
return res.json(404, {});
}
res.json(data);
} else next();
app.get('/topic/:id/:slug?', function (req, res, next) {
var uid = (req.user) ? req.user.uid : 0;
topics.getTopicWithPosts(req.params.id, uid, 0, 10, function (err, data) {
if (!err) {
if (data.deleted === '1' && data.expose_tools === 0) {
return res.json(404, {});
}
res.json(data);
} else next();
});
});
app.get('/category/:id/:slug?', function (req, res, next) {
var uid = (req.user) ? req.user.uid : 0;
categories.getCategoryById(req.params.id, uid, function (err, data) {
if (!err)
res.json(data);
else
next();
}, req.params.id, uid);
});
});
app.get('/api/category/:id/:slug?', function(req, res, next) {
var uid = (req.user) ? req.user.uid : 0;
categories.getCategoryById(req.params.id, uid, function(err, data) {
if (!err)
app.get('/recent', function (req, res) {
var uid = (req.user) ? req.user.uid : 0;
topics.getLatestTopics(uid, 0, 19, function (data) {
res.json(data);
else
next();
}, req.params.id, uid);
});
});
});
app.get('/api/recent', function(req, res) {
var uid = (req.user) ? req.user.uid : 0;
topics.getLatestTopics(uid, 0, 19, function(data) {
res.json(data);
app.get('/unread', function (req, res) {
var uid = (req.user) ? req.user.uid : 0;
topics.getUnreadTopics(uid, 0, 19, function (data) {
res.json(data);
});
});
});
app.get('/api/unread', function(req, res) {
var uid = (req.user) ? req.user.uid : 0;
topics.getUnreadTopics(uid, 0, 19, function(data) {
res.json(data);
app.get('/unread/total', function (req, res) {
var uid = (req.user) ? req.user.uid : 0;
topics.getTotalUnread(uid, function (data) {
res.json(data);
});
});
});
app.get('/api/unread/total', function(req, res) {
var uid = (req.user) ? req.user.uid : 0;
topics.getTotalUnread(uid, function(data) {
res.json(data);
app.get('/confirm/:id', function (req, res) {
user.email.confirm(req.params.id, function (data) {
if (data.status === 'ok') {
res.json({
'alert-class': 'alert-success',
title: 'Email Confirmed',
text: 'Thank you for vaidating your email. Your account is now fully activated.'
});
} else {
res.json({
'alert-class': 'alert-error',
title: 'An error occurred...',
text: 'There was a problem validating your email address. Perhaps the code was invalid or has expired.'
});
}
});
});
});
app.get('/api/confirm/:id', function(req, res) {
user.email.confirm(req.params.id, function(data) {
if (data.status === 'ok') {
app.get('/outgoing', function (req, res) {
var url = req.query.url;
if (url) {
res.json({
'alert-class': 'alert-success',
title: 'Email Confirmed',
text: 'Thank you for vaidating your email. Your account is now fully activated.'
url: url,
home: nconf.get('url')
});
} else {
res.json({
'alert-class': 'alert-error',
title: 'An error occurred...',
text: 'There was a problem validating your email address. Perhaps the code was invalid or has expired.'
});
res.status(404);
res.redirect(nconf.get('relative_path') + '/404');
}
});
});
app.get('/api/outgoing', function(req, res) {
var url = req.query.url;
if (url) {
res.json({
url: url,
home: nconf.get('url')
app.get('/search', function (req, res) {
return res.json({
show_no_topics: 'hide',
show_no_posts: 'hide',
show_results: 'hide',
search_query: '',
posts: [],
topics: []
});
} else {
res.status(404);
res.redirect(nconf.get('relative_path') + '/404');
}
});
app.get('/api/search', function(req, res) {
return res.json({
show_no_topics: 'hide',
show_no_posts: 'hide',
search_query: '',
posts: [],
topics: []
});
});
app.get('/api/search/:term', function(req, res, next) {
app.get('/search/:term', function (req, res, next) {
var reds = require('reds');
var postSearch = reds.createSearch('nodebbpostsearch');
var topicSearch = reds.createSearch('nodebbtopicsearch');
var reds = require('reds');
var postSearch = reds.createSearch('nodebbpostsearch');
var topicSearch = reds.createSearch('nodebbtopicsearch');
function search(searchObj, callback) {
searchObj
.query(query = req.params.term).type('or')
.end(callback);
}
function search(searchObj, callback) {
searchObj
.query(query = req.params.term).type('or')
.end(callback);
}
function searchPosts(callback) {
search(postSearch, function(err, pids) {
if (err)
return callback(err, null);
function searchPosts(callback) {
search(postSearch, function (err, pids) {
if (err)
return callback(err, null);
posts.getPostSummaryByPids(pids, function (err, posts) {
if (err)
return callback(err, null);
callback(null, posts);
});
})
}
posts.getPostSummaryByPids(pids, function(err, posts) {
function searchTopics(callback) {
search(topicSearch, function (err, tids) {
if (err)
return callback(err, null);
callback(null, posts);
topics.getTopicsByTids(tids, 0, function (topics) {
callback(null, topics);
}, 0);
});
})
}
}
function searchTopics(callback) {
search(topicSearch, function(err, tids) {
async.parallel([searchPosts, searchTopics], function (err, results) {
if (err)
return callback(err, null);
topics.getTopicsByTids(tids, 0, function(topics) {
callback(null, topics);
}, 0);
return next();
return res.json({
show_no_topics: results[1].length ? 'hide' : '',
show_no_posts: results[0].length ? 'hide' : '',
show_results: '',
search_query: req.params.term,
posts: results[0],
topics: results[1]
});
});
}
});
async.parallel([searchPosts, searchTopics], function(err, results) {
if (err)
return next();
app.get('/reset', function (req, res) {
res.json({});
});
return res.json({
show_no_topics: results[1].length ? 'hide' : '',
show_no_posts: results[0].length ? 'hide' : '',
search_query: req.params.term,
posts: results[0],
topics: results[1]
app.get('/reset/:code', function (req, res) {
res.json({
reset_code: req.params.code
});
});
});
app.get('/api/reset', function(req, res) {
res.json({});
});
app.get('/api/reset/:code', function(req, res) {
res.json({
reset_code: req.params.code
app.get('/404', function (req, res) {
res.json({});
});
});
app.get('/api/404', function(req, res) {
res.json({});
});
app.get('/api/403', function(req, res) {
res.json({});
app.get('/403', function (req, res) {
res.json({});
});
});
}
}(exports));
}(exports));

@ -90,19 +90,14 @@
}
Auth.create_routes = function(app) {
app.get('/logout', function(req, res) {
app.post('/logout', function(req, res) {
if (req.user && req.user.uid > 0) {
winston.info('[Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')');
req.logout();
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + templates['logout'] + templates['footer']);
});
} else res.redirect('/');
}
res.send(200)
});
if (login_strategies.indexOf('twitter') !== -1) {

@ -5,17 +5,18 @@ var user = require('./../user.js'),
utils = require('./../../public/src/utils.js'),
path = require('path'),
winston = require('winston'),
nconf = require('nconf');
nconf = require('nconf'),
meta = require('./../meta');
(function(User) {
User.create_routes = function(app) {
(function (User) {
User.create_routes = function (app) {
app.get('/uid/:uid', function(req, res) {
app.get('/uid/:uid', function (req, res) {
if (!req.params.uid)
return res.redirect('/404');
user.getUserData(req.params.uid, function(err, data) {
user.getUserData(req.params.uid, function (err, data) {
if (data) {
res.send(data);
} else {
@ -24,148 +25,162 @@ var user = require('./../user.js'),
});
}
});
});
app.get('/users', function(req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route("users", "users") + templates['footer']);
app.namespace('/users', function () {
app.get('', function (req, res) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route("users", "users") + templates['footer']);
});
});
});
app.get('/users/latest', function(req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route("users/latest", "users") + templates['footer']);
app.get('/latest', function (req, res) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route("users/latest", "users") + templates['footer']);
});
});
});
app.get('/users/sort-posts', function(req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route("users/sort-posts", "users") + templates['footer']);
app.get('/sort-posts', function (req, res) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route("users/sort-posts", "users") + templates['footer']);
});
});
});
app.get('/users/sort-reputation', function(req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route("users/sort-reputation", "users") + templates['footer']);
app.get('/sort-reputation', function (req, res) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route("users/sort-reputation", "users") + templates['footer']);
});
});
});
app.get('/users/search', function(req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route("users/search", "users") + templates['footer']);
app.get('/online', function (req, res) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route("users/online", "users") + templates['footer']);
});
});
});
app.get('/user/:userslug', function(req, res, next) {
if (!req.params.userslug) {
next();
return;
}
user.get_uid_by_userslug(req.params.userslug, function(err, uid) {
if (!uid) {
return next();
}
app.get('/search', function (req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route('user/' + req.params.userslug, 'account') + templates['footer']);
}, function (err, header) {
res.send(header + app.create_route("users/search", "users") + templates['footer']);
});
});
});
app.get('/user/:userslug/edit', function(req, res) {
app.namespace('/user', function () {
app.get('/:userslug', function (req, res, next) {
if (!req.user)
return res.redirect('/403');
if (!req.params.userslug) {
next();
return;
}
user.get_uid_by_userslug(req.params.userslug, function (err, uid) {
if (!uid) {
return next();
}
user.getUserField(req.user.uid, 'userslug', function(err, userslug) {
if (req.params.userslug && userslug === req.params.userslug) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/edit', 'accountedit') + templates['footer']);
}, function (err, header) {
res.send(header + app.create_route('user/' + req.params.userslug, 'account') + templates['footer']);
});
} else {
return res.redirect('/404');
}
});
});
});
app.get('/user/:userslug/settings', function(req, res) {
app.get('/:userslug/edit', function (req, res) {
if (!req.user)
return res.redirect('/403');
if (!req.user)
return res.redirect('/403');
user.getUserField(req.user.uid, 'userslug', function(err, userslug) {
if (req.params.userslug && userslug === req.params.userslug) {
app.build_header({
req: req,
res: res
}, function(err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/settings', 'accountsettings') + templates['footer']);
})
} else {
return res.redirect('/404');
}
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
if (req.params.userslug && userslug === req.params.userslug) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/edit', 'accountedit') + templates['footer']);
});
} else {
return res.redirect('/404');
}
});
});
});
app.post('/user/uploadpicture', function(req, res) {
if (!req.user)
return res.redirect('/403');
app.get('/:userslug/settings', function (req, res) {
if (req.files.userPhoto.size > 262144) {
res.send({
error: 'Images must be smaller than 256kb!'
if (!req.user)
return res.redirect('/403');
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
if (req.params.userslug && userslug === req.params.userslug) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/settings', 'accountsettings') + templates['footer']);
})
} else {
return res.redirect('/404');
}
});
return;
}
});
var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
app.post('/uploadpicture', function (req, res) {
if (!req.user)
return res.redirect('/403');
if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) {
res.send({
error: 'Allowed image types are png, jpg and gif!'
});
return;
}
var uploadSize = meta.config.maximumProfileImageSize || 256;
user.getUserField(req.user.uid, 'uploadedpicture', function(err, oldpicture) {
if (!oldpicture) {
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
if (req.files.userPhoto.size > uploadSize * 1024) {
res.send({
error: 'Images must be smaller than ' + uploadSize + ' kb!'
});
return;
}
var absolutePath = path.join(process.cwd(), nconf.get('upload_path'), path.basename(oldpicture));
var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
fs.unlink(absolutePath, function(err) {
if (err) {
winston.err(err);
if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) {
res.send({
error: 'Allowed image types are png, jpg and gif!'
});
return;
}
user.getUserField(req.user.uid, 'uploadedpicture', function (err, oldpicture) {
if (!oldpicture) {
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
return;
}
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
var absolutePath = path.join(process.cwd(), nconf.get('upload_path'), path.basename(oldpicture));
fs.unlink(absolutePath, function (err) {
if (err) {
winston.err(err);
}
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
});
});
});
});
@ -186,7 +201,7 @@ var user = require('./../user.js'),
var is = fs.createReadStream(tempPath);
var os = fs.createWriteStream(uploadPath);
is.on('end', function() {
is.on('end', function () {
fs.unlinkSync(tempPath);
var imageUrl = nconf.get('upload_url') + filename;
@ -199,7 +214,7 @@ var user = require('./../user.js'),
dstPath: uploadPath,
width: 128,
height: 128
}, function(err, stdout, stderr) {
}, function (err, stdout, stderr) {
if (err) {
winston.err(err);
}
@ -210,7 +225,7 @@ var user = require('./../user.js'),
});
});
os.on('error', function(err) {
os.on('error', function (err) {
fs.unlinkSync(tempPath);
winston.err(err);
});
@ -218,12 +233,12 @@ var user = require('./../user.js'),
is.pipe(os);
}
app.get('/user/:userslug/following', function(req, res) {
app.get('/user/:userslug/following', function (req, res) {
if (!req.user)
return res.redirect('/403');
user.get_uid_by_userslug(req.params.userslug, function(err, uid) {
user.get_uid_by_userslug(req.params.userslug, function (err, uid) {
if (!uid) {
res.redirect('/404');
return;
@ -232,18 +247,18 @@ var user = require('./../user.js'),
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/following', 'following') + templates['footer']);
});
});
});
app.get('/user/:userslug/followers', function(req, res) {
app.get('/user/:userslug/followers', function (req, res) {
if (!req.user)
return res.redirect('/403');
user.get_uid_by_userslug(req.params.userslug, function(err, uid) {
user.get_uid_by_userslug(req.params.userslug, function (err, uid) {
if (!uid) {
res.redirect('/404');
return;
@ -251,18 +266,18 @@ var user = require('./../user.js'),
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/followers', 'followers') + templates['footer']);
});
});
});
app.get('/user/:userslug/favourites', function(req, res) {
app.get('/user/:userslug/favourites', function (req, res) {
if (!req.user)
return res.redirect('/403');
user.get_uid_by_userslug(req.params.userslug, function(err, uid) {
user.get_uid_by_userslug(req.params.userslug, function (err, uid) {
if (!uid) {
res.redirect('/404');
return;
@ -270,18 +285,18 @@ var user = require('./../user.js'),
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + app.create_route('user/' + req.params.userslug + '/favourites', 'favourites') + templates['footer']);
});
});
});
app.get('/api/user/:userslug/following', function(req, res) {
app.get('/api/user/:userslug/following', function (req, res) {
var callerUID = req.user ? req.user.uid : '0';
getUserDataByUserSlug(req.params.userslug, callerUID, function(userData) {
getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) {
if (userData) {
user.getFollowing(userData.uid, function(followingData) {
user.getFollowing(userData.uid, function (followingData) {
userData.following = followingData;
userData.followingCount = followingData.length;
res.json(userData);
@ -295,12 +310,12 @@ var user = require('./../user.js'),
});
});
app.get('/api/user/:userslug/followers', function(req, res) {
app.get('/api/user/:userslug/followers', function (req, res) {
var callerUID = req.user ? req.user.uid : '0';
getUserDataByUserSlug(req.params.userslug, callerUID, function(userData) {
getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) {
if (userData) {
user.getFollowers(userData.uid, function(followersData) {
user.getFollowers(userData.uid, function (followersData) {
userData.followers = followersData;
userData.followersCount = followersData.length;
res.json(userData);
@ -313,18 +328,18 @@ var user = require('./../user.js'),
});
});
app.get('/api/user/:userslug/edit', function(req, res) {
app.get('/api/user/:userslug/edit', function (req, res) {
var callerUID = req.user ? req.user.uid : '0';
getUserDataByUserSlug(req.params.userslug, callerUID, function(userData) {
getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) {
res.json(userData);
});
});
app.get('/api/user/:userslug/settings', function(req, res, next) {
app.get('/api/user/:userslug/settings', function (req, res, next) {
var callerUID = req.user ? req.user.uid : '0';
user.get_uid_by_userslug(req.params.userslug, function(err, uid) {
user.get_uid_by_userslug(req.params.userslug, function (err, uid) {
if (!uid) {
res.json(404, {
error: 'User not found!'
@ -339,8 +354,8 @@ var user = require('./../user.js'),
return;
}
user.getUserFields(uid, ['username', 'userslug', 'showemail'], function(err, userData) {
user.getUserFields(uid, ['username', 'userslug', 'showemail'], function (err, userData) {
if (err)
return next(err);
@ -359,10 +374,10 @@ var user = require('./../user.js'),
});
});
app.get('/api/user/:userslug/favourites', function(req, res, next) {
app.get('/api/user/:userslug/favourites', function (req, res, next) {
var callerUID = req.user ? req.user.uid : '0';
user.get_uid_by_userslug(req.params.userslug, function(err, uid) {
user.get_uid_by_userslug(req.params.userslug, function (err, uid) {
if (!uid) {
res.json(404, {
error: 'User not found!'
@ -377,12 +392,12 @@ var user = require('./../user.js'),
return;
}
user.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
user.getUserFields(uid, ['username', 'userslug'], function (err, userData) {
if (err)
return next(err);
if (userData) {
posts.getFavourites(uid, function(err, posts) {
posts.getFavourites(uid, function (err, posts) {
if (err)
return next(err);
userData.posts = posts;
@ -398,15 +413,15 @@ var user = require('./../user.js'),
});
});
app.get('/api/user/:userslug', function(req, res) {
app.get('/api/user/:userslug', function (req, res) {
var callerUID = req.user ? req.user.uid : '0';
getUserDataByUserSlug(req.params.userslug, callerUID, function(userData) {
getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) {
if (userData) {
user.isFollowing(callerUID, userData.theirid, function(isFollowing) {
posts.getPostsByUid(userData.theirid, 0, 9, function(posts) {
user.isFollowing(callerUID, userData.theirid, function (isFollowing) {
posts.getPostsByUid(userData.theirid, 0, 9, function (posts) {
userData.posts = posts.filter(function(p) {
userData.posts = posts.filter(function (p) {
return p.deleted !== "1";
});
userData.isFollowing = isFollowing;
@ -415,7 +430,7 @@ var user = require('./../user.js'),
if (callerUID !== userData.uid)
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
postTools.toHTML(userData.signature, function(err, signature) {
postTools.parse(userData.signature, function (err, signature) {
userData.signature = signature;
res.json(userData);
});
@ -433,11 +448,12 @@ var user = require('./../user.js'),
app.get('/api/users/sort-posts', getUsersSortedByPosts);
app.get('/api/users/sort-reputation', getUsersSortedByReputation);
app.get('/api/users/latest', getUsersSortedByJoinDate);
app.get('/api/users/online', getOnlineUsers);
app.get('/api/users/search', getUsersForSearch);
function getUsersSortedByJoinDate(req, res) {
user.getUsers('users:joindate', 0, 49, function(err, data) {
user.getUsers('users:joindate', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
@ -447,7 +463,7 @@ var user = require('./../user.js'),
}
function getUsersSortedByPosts(req, res) {
user.getUsers('users:postcount', 0, 49, function(err, data) {
user.getUsers('users:postcount', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
@ -457,7 +473,17 @@ var user = require('./../user.js'),
}
function getUsersSortedByReputation(req, res) {
user.getUsers('users:reputation', 0, 49, function(err, data) {
user.getUsers('users:reputation', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data
});
});
}
function getOnlineUsers(req, res) {
user.getUsers('users:online', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
@ -475,14 +501,14 @@ var user = require('./../user.js'),
}
function getUserDataByUserSlug(userslug, callerUID, callback) {
user.get_uid_by_userslug(userslug, function(err, uid) {
user.get_uid_by_userslug(userslug, function (err, uid) {
if (uid === null) {
callback(null);
return;
}
user.getUserData(uid, function(err, data) {
user.getUserData(uid, function (err, data) {
if (data) {
data.joindate = new Date(parseInt(data.joindate, 10)).toISOString();
@ -510,8 +536,8 @@ var user = require('./../user.js'),
data.yourid = callerUID;
data.theirid = uid;
user.getFollowingCount(uid, function(followingCount) {
user.getFollowerCount(uid, function(followerCount) {
user.getFollowingCount(uid, function (followingCount) {
user.getFollowerCount(uid, function (followerCount) {
data.followingCount = followingCount;
data.followerCount = followerCount;
callback(data);

@ -297,22 +297,20 @@ var RDB = require('./redis.js'),
});
}
ThreadTools.get_latest_undeleted_pid = function(tid, callback) {
posts.getPostsByTid(tid, 0, -1, function(posts) {
var numPosts = posts.length;
if (!numPosts)
return callback(new Error('no-undeleted-pids-found'));
while (numPosts--) {
if (posts[numPosts].deleted !== '1') {
callback(null, posts[numPosts].pid);
return;
}
}
callback(new Error('no-undeleted-pids-found'));
ThreadTools.getLatestUndeletedPid = function(tid, callback) {
RDB.lrange('tid:' + tid + ':posts', 0, -1, function(err, pids) {
if (pids.length === 0) return callback(new Error('no-undeleted-pids-found'));
pids.reverse();
async.detectSeries(pids, function(pid, next) {
RDB.hget('post:' + pid, 'deleted', function(err, deleted) {
if (deleted === '0') next(true);
else next(false);
});
}, function(pid) {
if (pid) callback(null, pid);
else callback(new Error('no-undeleted-pids-found'));
});
});
}
}(exports));

@ -12,7 +12,8 @@ schema = require('./schema.js'),
feed = require('./feed.js'),
favourites = require('./favourites.js'),
reds = require('reds'),
topicSearch = reds.createSearch('nodebbtopicsearch');
topicSearch = reds.createSearch('nodebbtopicsearch'),
validator = require('validator');
(function(Topics) {
@ -312,6 +313,7 @@ schema = require('./schema.js'),
topicData.teaser_text = topicInfo.teaserInfo.text || '',
topicData.teaser_username = topicInfo.teaserInfo.username || '';
topicData.teaser_userpicture = topicInfo.teaserInfo.picture || '';
topicData.teaser_pid = topicInfo.teaserInfo.pid;
topicData.teaser_timestamp = topicInfo.teaserInfo.timestamp ? (new Date(parseInt(topicInfo.teaserInfo.timestamp, 10)).toISOString()) : '';
@ -572,9 +574,9 @@ schema = require('./schema.js'),
}
Topics.getTeaser = function(tid, callback) {
threadTools.get_latest_undeleted_pid(tid, function(err, pid) {
threadTools.getLatestUndeletedPid(tid, function(err, pid) {
if (!err) {
posts.getPostFields(pid, ['content', 'uid', 'timestamp'], function(postData) {
posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(postData) {
user.getUserFields(postData.uid, ['username', 'picture'], function(err, userData) {
if (err)
@ -583,6 +585,7 @@ schema = require('./schema.js'),
var stripped = postData.content,
timestamp = postData.timestamp,
returnObj = {
"pid": postData.pid,
"username": userData.username,
"picture": userData.picture,
"timestamp": timestamp
@ -590,7 +593,7 @@ schema = require('./schema.js'),
if (postData.content) {
stripped = postData.content.replace(/>.+\n\n/, '');
postTools.toHTML(stripped, function(err, stripped) {
postTools.parse(stripped, function(err, stripped) {
returnObj.text = utils.strip_tags(stripped);
callback(null, returnObj);
});
@ -655,7 +658,7 @@ schema = require('./schema.js'),
var slug = tid + '/' + utils.slugify(title);
var timestamp = Date.now();
title = validator.sanitize(title).escape();
RDB.hmset('topic:' + tid, {
'tid': tid,
'uid': uid,

@ -24,14 +24,24 @@ var express = require('express'),
plugins = require('./plugins'),
nconf = require('nconf');
(function(app) {
var templates = null;
(function (app) {
var templates = null,
clientScripts;
// Minify client-side libraries
meta.js.get(function (err, scripts) {
clientScripts = scripts.map(function (script) {
return script = {
script: script
}
});
});
/**
* `options` object requires: req, res
* accepts: metaTags
*/
app.build_header = function(options, callback) {
app.build_header = function (options, callback) {
var defaultMetaTags = [{
name: 'viewport',
content: 'width=device-width, initial-scale=1.0'
@ -44,33 +54,35 @@ var express = require('express'),
}, {
property: 'og:site_name',
content: meta.config.title || 'NodeBB'
}, ],
}],
metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])),
templateValues = {
cssSrc: meta.config['theme:src'] || nconf.get('relative_path') + '/vendor/bootstrap/css/bootstrap.min.css',
title: meta.config['title'] || 'NodeBB',
browserTitle: meta.config['title'] || 'NodeBB',
title: meta.config.title || 'NodeBB',
browserTitle: meta.config.title || 'NodeBB',
csrf: options.res.locals.csrf_token,
relative_path: nconf.get('relative_path'),
meta_tags: metaString
meta_tags: metaString,
clientScripts: clientScripts
};
translator.translate(templates['header'].parse(templateValues), function(template) {
translator.translate(templates.header.parse(templateValues), function(template) {
callback(null, template);
});
};
// Middlewares
app.use(express.compress());
app.use(express.favicon(path.join(__dirname, '../', 'public', 'favicon.ico')));
app.use(require('less-middleware')({
src: path.join(__dirname, '../', 'public'),
prefix: nconf.get('relative_path')
prefix: nconf.get('relative_path'),
yuicompress: true
}));
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)
app.use(express.compress());
app.use(express.session({
store: new RedisStore({
client: RDB,
@ -83,16 +95,16 @@ var express = require('express'),
}
}));
app.use(express.csrf());
app.use(function(req, res, next) {
app.use(function (req, res, next) {
res.locals.csrf_token = req.session._csrf;
next();
});
// Static Directories for NodeBB Plugins
app.configure(function() {
app.configure(function () {
var tailMiddlewares = [];
plugins.ready(function() {
plugins.ready(function () {
// Remove some middlewares until the router is gone
// This is not recommended behaviour: http://stackoverflow.com/a/13691542/122353
// Also: https://www.exratione.com/2013/03/nodejs-abusing-express-3-to-enable-late-addition-of-middleware/
@ -111,7 +123,7 @@ var express = require('express'),
});
});
module.exports.init = function() {
module.exports.init = function () {
templates = global.templates;
// translate all static templates served by webserver here. ex. footer, logout
@ -127,7 +139,7 @@ var express = require('express'),
auth.initialize(app);
app.use(function(req, res, next) {
app.use(function (req, res, next) {
nconf.set('https', req.secure);
@ -136,7 +148,7 @@ var express = require('express'),
app.use(app.router);
app.use(function(req, res, next) {
app.use(function (req, res, next) {
res.status(404);
// respond with html page
@ -158,7 +170,7 @@ var express = require('express'),
res.type('txt').send('Not found');
});
app.use(function(err, req, res, next) {
app.use(function (err, req, res, next) {
// we may use properties of the error object
// here and next(err) appropriately, or if
@ -173,12 +185,12 @@ var express = require('express'),
});
app.create_route = function(url, tpl) { // to remove
app.create_route = function (url, tpl) { // to remove
return '<script>templates.ready(function(){ajaxify.go("' + url + '", null, "' + tpl + '");});</script>';
};
app.namespace(nconf.get('relative_path'), function() {
app.namespace(nconf.get('relative_path'), function () {
auth.create_routes(app);
admin.create_routes(app);
@ -188,16 +200,16 @@ var express = require('express'),
// Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section)
(function() {
(function () {
var routes = ['login', 'register', 'account', 'recent', 'unread', 'popular', 'active', '403', '404'];
for (var i = 0, ii = routes.length; i < ii; i++) {
(function(route) {
(function (route) {
app.get('/' + route, function(req, res) {
app.get('/' + route, function (req, res) {
if ((route === 'login' || route === 'register') && (req.user && req.user.uid > 0)) {
user.getUserField(req.user.uid, 'userslug', function(err, userslug) {
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
res.redirect('/user/' + userslug);
});
return;
@ -206,7 +218,7 @@ var express = require('express'),
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + app.create_route(route) + templates['footer']);
});
});
@ -215,9 +227,9 @@ var express = require('express'),
}());
app.get('/', function(req, res) {
app.get('/', function (req, res) {
async.parallel({
"header": function(next) {
"header": function (next) {
app.build_header({
req: req,
res: res,
@ -236,12 +248,17 @@ var express = require('express'),
}]
}, next);
},
"categories": function(next) {
categories.getAllCategories(function(returnData) {
"categories": function (next) {
categories.getAllCategories(function (returnData) {
returnData.categories = returnData.categories.filter(function (category) {
if (category.disabled !== '1') return true;
else return false;
});
next(null, returnData);
}, 0);
}
}, function(err, data) {
}, function (err, data) {
res.send(
data.header +
'\n\t<noscript>\n' + templates['noscript/header'] + templates['noscript/home'].parse(data.categories) + '\n\t</noscript>' +
@ -252,14 +269,14 @@ var express = require('express'),
});
app.get('/topic/:topic_id/:slug?', function(req, res) {
app.get('/topic/:topic_id/:slug?', function (req, res) {
var tid = req.params.topic_id;
if (tid.match(/^\d+\.rss$/)) {
tid = tid.slice(0, -4);
var rssPath = path.join(__dirname, '../', 'feeds/topics', tid + '.rss'),
loadFeed = function() {
fs.readFile(rssPath, function(err, data) {
loadFeed = function () {
fs.readFile(rssPath, function (err, data) {
if (err) res.type('text').send(404, "Unable to locate an rss feed at this location.");
else res.type('xml').set('Content-Length', data.length).send(data);
});
@ -267,7 +284,7 @@ var express = require('express'),
};
if (!fs.existsSync(rssPath)) {
feed.updateTopic(tid, function(err) {
feed.updateTopic(tid, function (err) {
if (err) res.redirect('/404');
else loadFeed();
});
@ -277,8 +294,8 @@ var express = require('express'),
}
async.waterfall([
function(next) {
topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), 0, -1, function(err, topicData) {
function (next) {
topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), 0, -1, function (err, topicData) {
if (topicData) {
if (topicData.deleted === '1' && topicData.expose_tools === 0)
return next(new Error('Topic deleted'), null);
@ -287,7 +304,7 @@ var express = require('express'),
next(err, topicData);
});
},
function(topicData, next) {
function (topicData, next) {
var lastMod = 0,
timestamp;
@ -324,14 +341,14 @@ var express = require('express'),
property: 'article:section',
content: topicData.category_name
}]
}, function(err, header) {
}, function (err, header) {
next(err, {
header: header,
topics: topicData
});
});
},
], function(err, data) {
], function (err, data) {
if (err) return res.redirect('404');
var topic_url = tid + (req.params.slug ? '/' + req.params.slug : '');
@ -344,14 +361,14 @@ var express = require('express'),
});
});
app.get('/category/:category_id/:slug?', function(req, res) {
app.get('/category/:category_id/:slug?', function (req, res) {
var cid = req.params.category_id;
if (cid.match(/^\d+\.rss$/)) {
cid = cid.slice(0, -4);
var rssPath = path.join(__dirname, '../', 'feeds/categories', cid + '.rss'),
loadFeed = function() {
fs.readFile(rssPath, function(err, data) {
loadFeed = function () {
fs.readFile(rssPath, function (err, data) {
if (err) res.type('text').send(404, "Unable to locate an rss feed at this location.");
else res.type('xml').set('Content-Length', data.length).send(data);
});
@ -359,7 +376,7 @@ var express = require('express'),
};
if (!fs.existsSync(rssPath)) {
feed.updateCategory(cid, function(err) {
feed.updateCategory(cid, function (err) {
if (err) res.redirect('/404');
else loadFeed();
});
@ -369,8 +386,8 @@ var express = require('express'),
}
async.waterfall([
function(next) {
categories.getCategoryById(cid, 0, function(err, categoryData) {
function (next) {
categories.getCategoryById(cid, 0, function (err, categoryData) {
if (categoryData) {
if (categoryData.disabled === '1')
@ -379,7 +396,7 @@ var express = require('express'),
next(err, categoryData);
});
},
function(categoryData, next) {
function (categoryData, next) {
app.build_header({
req: req,
res: res,
@ -393,14 +410,14 @@ var express = require('express'),
property: "og:type",
content: 'website'
}]
}, function(err, header) {
}, function (err, header) {
next(err, {
header: header,
categories: categoryData
});
});
}
], function(err, data) {
], function (err, data) {
if (err) return res.redirect('404');
var category_url = cid + (req.params.slug ? '/' + req.params.slug : '');
@ -413,33 +430,32 @@ var express = require('express'),
});
});
app.get('/confirm/:code', function(req, res) {
app.get('/confirm/:code', function (req, res) {
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + '<script>templates.ready(function(){ajaxify.go("confirm/' + req.params.code + '");});</script>' + templates['footer']);
});
});
app.get('/sitemap.xml', function(req, res) {
app.get('/sitemap.xml', function (req, res) {
var sitemap = require('./sitemap.js');
sitemap.render(function(xml) {
sitemap.render(function (xml) {
res.type('xml').set('Content-Length', xml.length).send(xml);
});
});
app.get('/robots.txt', function(req, res) {
app.get('/robots.txt', function (req, res) {
res.set('Content-Type', 'text/plain');
res.send("User-agent: *\n" +
"Disallow: \n" +
"Disallow: /admin/\n" +
"Sitemap: " + nconf.get('url') + "sitemap.xml");
});
app.get('/cid/:cid', function(req, res) {
categories.getCategoryData(req.params.cid, function(err, data) {
app.get('/cid/:cid', function (req, res) {
categories.getCategoryData(req.params.cid, function (err, data) {
if (data)
res.send(data);
else
@ -447,8 +463,8 @@ var express = require('express'),
});
});
app.get('/tid/:tid', function(req, res) {
topics.getTopicData(req.params.tid, function(data) {
app.get('/tid/:tid', function (req, res) {
topics.getTopicData(req.params.tid, function (data) {
if (data)
res.send(data);
else
@ -456,8 +472,8 @@ var express = require('express'),
});
});
app.get('/pid/:pid', function(req, res) {
posts.getPostData(req.params.pid, function(data) {
app.get('/pid/:pid', function (req, res) {
posts.getPostData(req.params.pid, function (data) {
if (data)
res.send(data);
else
@ -465,13 +481,13 @@ var express = require('express'),
});
});
app.get('/outgoing', function(req, res) {
app.get('/outgoing', function (req, res) {
if (!req.query.url) return res.redirect('/404');
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(
header +
'\n\t<script>templates.ready(function(){ajaxify.go("outgoing?url=' + encodeURIComponent(req.query.url) + '", null, null, true);});</script>' +
@ -480,31 +496,35 @@ var express = require('express'),
});
});
app.get('/search', function(req, res) {
app.get('/search', function (req, res) {
if (!req.user)
return res.redirect('/403');
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + app.create_route("search", null, "search") + templates['footer']);
});
});
app.get('/search/:term', function(req, res) {
app.get('/search/:term', function (req, res) {
if (!req.user)
return res.redirect('/403');
app.build_header({
req: req,
res: res
}, function(err, header) {
}, function (err, header) {
res.send(header + app.create_route("search/" + req.params.term, null, "search") + templates['footer']);
});
});
app.get('/reindex', function(req, res) {
topics.reIndexAll(function(err) {
app.get('/reindex', function (req, res) {
topics.reIndexAll(function (err) {
if (err) {
return res.json(err);
}
user.reIndexAll(function(err) {
user.reIndexAll(function (err) {
if (err) {
return res.json(err);
} else {
@ -514,7 +534,6 @@ var express = require('express'),
});
});
});
}(WebServer));

@ -23,6 +23,7 @@ var SocketIO = require('socket.io').listen(global.server, {
client: RDB,
ttl: 60 * 60 * 24 * 14
}),
nconf = require('nconf'),
socketCookieParser = express.cookieParser(nconf.get('secret')),
admin = {
'categories': require('./admin/categories.js'),
@ -53,14 +54,17 @@ var SocketIO = require('socket.io').listen(global.server, {
userSockets[uid].push(socket);
if (uid) {
socket.join('uid_' + uid);
io.sockets. in ('global').emit('api:user.isOnline', isUserOnline(uid));
user.getUserField(uid, 'username', function(err, username) {
socket.emit('event:connect', {
status: 1,
username: username,
uid: uid
RDB.zadd('users:online', Date.now(), uid, function(err, data) {
socket.join('uid_' + uid);
io.sockets. in ('global').emit('api:user.isOnline', isUserOnline(uid));
user.getUserField(uid, 'username', function(err, username) {
socket.emit('event:connect', {
status: 1,
username: username,
uid: uid
});
});
});
}
@ -80,7 +84,9 @@ var SocketIO = require('socket.io').listen(global.server, {
delete users[sessionID];
delete userSockets[uid];
if (uid) {
io.sockets. in ('global').emit('api:user.isOnline', isUserOnline(uid));
RDB.zrem('users:online', uid, function(err, data) {
io.sockets. in ('global').emit('api:user.isOnline', isUserOnline(uid));
});
}
}
@ -100,7 +106,7 @@ var SocketIO = require('socket.io').listen(global.server, {
socket.on('api:get_all_rooms', function(data) {
socket.emit('api:get_all_rooms', io.sockets.manager.rooms);
})
});
function updateRoomBrowsingText(roomName) {

Loading…
Cancel
Save