fixed conflicts, added new language key for users/online

This commit is contained in:
psychobunny
2013-09-24 15:15:39 -04:00
40 changed files with 11076 additions and 915 deletions

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@ npm-debug.log
node_modules/ node_modules/
sftp-config.json sftp-config.json
config.json config.json
public/src/nodebb.min.js
public/config.json public/config.json
public/css/*.css public/css/*.css
public/themes/* public/themes/*

View File

@@ -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
**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** 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 Main Category Listing](http://i.imgur.com/ZBrHqLr.png)
![NodeBB Topic Page](http://i.imgur.com/YSBA6Vr.png) ![NodeBB Topic Page](http://i.imgur.com/YSBA6Vr.png)

2
app.js
View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ var socket,
(function () { (function () {
var showWelcomeMessage = false; var showWelcomeMessage = false;
function loadConfig() { app.loadConfig = function() {
$.ajax({ $.ajax({
url: RELATIVE_PATH + '/api/config', url: RELATIVE_PATH + '/api/config',
@@ -15,15 +15,21 @@ var socket,
API_URL = data.api_url; API_URL = data.api_url;
config = data; config = data;
if(socket) {
socket.disconnect();
socket.socket.connect();
} else {
socket = io.connect(config.socket.address); socket = io.connect(config.socket.address);
var reconnecting = false; var reconnecting = false;
var reconnectTries = 0; var reconnectTries = 0;
socket.on('event:connect', function (data) { socket.on('event:connect', function (data) {
console.log('connected to nodebb socket: ', data);
app.username = data.username; app.username = data.username;
app.showLoginMessage(); app.showLoginMessage();
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
}); });
socket.on('event:alert', function (data) { socket.on('event:alert', function (data) {
@@ -116,8 +122,7 @@ var socket,
}); });
app.enter_room('global'); app.enter_room('global');
}
}, },
async: false async: false
}); });
@@ -275,7 +280,7 @@ var socket,
if (active) { if (active) {
jQuery('#main-nav li a').each(function () { jQuery('#main-nav li a').each(function () {
var href = this.getAttribute('href'); 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'; active = 'users';
if (href && href.match(active)) { if (href && href.match(active)) {
jQuery(this.parentNode).addClass('active'); jQuery(this.parentNode).addClass('active');
@@ -286,6 +291,7 @@ var socket,
$('span.timeago').timeago(); $('span.timeago').timeago();
setTimeout(function () { setTimeout(function () {
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes. window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
}, 100); }, 100);
@@ -402,7 +408,8 @@ var socket,
function animateScroll() { function animateScroll() {
$('body,html').animate({ $('body,html').animate({
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height() 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) { if (!scrollTo.length && tid) {
@@ -427,19 +434,16 @@ var socket,
} }
jQuery('document').ready(function () { jQuery('document').ready(function () {
translator.load('global');
$('#search-form').on('submit', function () { $('#search-form').on('submit', function () {
var input = $(this).find('input'); var input = $(this).find('input');
ajaxify.go("search/" + input.val(), null, "search"); ajaxify.go("search/" + input.val(), null, "search");
input.val(''); input.val('');
return false; return false;
}) });
}); });
showWelcomeMessage = location.href.indexOf('loggedin') !== -1; showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
loadConfig(); app.loadConfig();
}()); }());

View File

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

View File

@@ -26,7 +26,6 @@
fields: ['username', 'picture', 'userslug'] fields: ['username', 'picture', 'userslug']
}); });
socket.on('api:updateHeader', function(data) { socket.on('api:updateHeader', function(data) {
jQuery('#search-button').on('click', function() { jQuery('#search-button').on('click', function() {
jQuery('#search-fields').removeClass('hide').show(); jQuery('#search-fields').removeClass('hide').show();
jQuery(this).hide(); jQuery(this).hide();
@@ -59,6 +58,7 @@
if (data['username']) if (data['username'])
userLabel.find('span').html(data['username']); userLabel.find('span').html(data['username']);
} else { } else {
rightMenu.empty();
var userli = $('<li> \ var userli = $('<li> \
<a id="user_label" href="/user/' + data['userslug'] + '"> \ <a id="user_label" href="/user/' + data['userslug'] + '"> \
<img src="' + data['picture'] + '"/> \ <img src="' + data['picture'] + '"/> \
@@ -67,7 +67,16 @@
</li>'); </li>');
rightMenu.append(userli); 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); rightMenu.append(logoutli);
} }
} else { } else {
@@ -86,6 +95,11 @@
right_menu.appendChild(registerEl); right_menu.appendChild(registerEl);
right_menu.appendChild(loginEl); 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 // Notifications dropdown

View File

@@ -32,7 +32,12 @@
$('#login-error-notify').show(); $('#login-error-notify').show();
} else { } else {
$('#login-error-notify').hide(); $('#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) { error: function(data, textStatus, jqXHR) {

View File

@@ -12,6 +12,13 @@
$('#search-form input').val(searchQuery); $('#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;
});
}); });
})(); })();

View File

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

View File

@@ -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) { function onUsersLoaded(users) {
var html = templates.prepare(templates['users'].blocks['users']).parse({ var html = templates.prepare(templates['users'].blocks['users']).parse({
@@ -91,23 +96,31 @@
set = 'users:postcount'; set = 'users:postcount';
} else if (active === 'sort-reputation') { } else if (active === 'sort-reputation') {
set = 'users:reputation'; set = 'users:reputation';
} else if (active === 'online') {
set = 'users:online';
} }
if (set) { if (set) {
startLoading(set, $('#users-container').children().length);
}
}
function startLoading(set, after) {
loadingMoreUsers = true; loadingMoreUsers = true;
socket.emit('api:users.loadMore', { socket.emit('api:users.loadMore', {
set: set, set: set,
after: $('#users-container').children().length after: after
}, function(data) { }, function(data) {
if (data.users.length) { if (data.users.length) {
onUsersLoaded(data.users); onUsersLoaded(data.users);
$('#load-more-users-btn').removeClass('disabled');
} else { } else {
$('#load-more-users-btn').addClass('disabled'); $('#load-more-users-btn').addClass('disabled');
} }
loadingMoreUsers = false; loadingMoreUsers = false;
}); });
} }
}
$('#load-more-users-btn').on('click', loadMoreUsers); $('#load-more-users-btn').on('click', loadMoreUsers);

View File

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

View File

@@ -48,13 +48,15 @@
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" /> <input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
</form> </form>
<div id="upload-progress-box" class="progress progress-striped active hide"> <div id="upload-progress-box" class="progress progress-striped">
<div id="upload-progress-bar" class="bar" style="width: 0%;"></div> <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>
<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-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>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button> <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>

View File

@@ -15,7 +15,7 @@
<div class="alert alert-warning"> <div class="alert alert-warning">
<p> <p>
<strong>Interesed in writing plugins for NodeBB?</strong> <strong>Interested in writing plugins for NodeBB?</strong>
</p> </p>
<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>. 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>.

View File

@@ -9,7 +9,9 @@
<label>Site Description</label> <label>Site Description</label>
<input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br /> <input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br />
<label>Imgur Client ID</label> <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> </form>
</div> </div>

View File

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

View File

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

View File

@@ -8,28 +8,18 @@
<script> <script>
var RELATIVE_PATH = "{relative_path}"; var RELATIVE_PATH = "{relative_path}";
</script> </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}/socket.io/socket.io.js"></script>
<script src="{relative_path}/src/app.js"></script> <!-- BEGIN clientScripts -->
<script src="{relative_path}/vendor/requirejs/require.js"></script> <script src="{relative_path}/{clientScripts.script}"></script>
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script> <!-- END clientScripts -->
<script> <script>
require.config({ require.config({
baseUrl: "{relative_path}/src/modules", baseUrl: "{relative_path}/src/modules",
waitSeconds: 3 waitSeconds: 3
}); });
</script> </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" /> <link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
</head> </head>
<body> <body>
@@ -41,7 +31,9 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="/">{title}</a> <a href="/">
<h1 class="navbar-brand forum-title">{title}</h1>
</a>
</div> </div>
<div class="navbar-collapse collapse navbar-ex1-collapse"> <div class="navbar-collapse collapse navbar-ex1-collapse">
@@ -58,6 +50,9 @@
<li class="visible-xs"> <li class="visible-xs">
<a href="/search">[[global:header.search]]</a> <a href="/search">[[global:header.search]]</a>
</li> </li>
<li class="visible-xs">
<a href="/search">Search</a>
</li>
<li> <li>
<a href="/"></a> <a href="/"></a>
</li> </li>

View File

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

View File

@@ -3,8 +3,18 @@
<li class="active">Search</li> <li class="active">Search</li>
</ol> </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="category search">
<div class=""> <div class="{show_results}">
<ul id="topics-container" data-search-query="{search_query}"> <ul id="topics-container" data-search-query="{search_query}">
<h3>Topics</h3> <h3>Topics</h3>
<div class="alert alert-info {show_no_topics}">No topics found!</div> <div class="alert alert-info {show_no_topics}">No topics found!</div>

View File

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

View File

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

View File

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

View File

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

9921
public/vendor/jquery/js/jquery.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -6,19 +6,21 @@ var utils = require('./../public/src/utils.js'),
winston = require('winston'), winston = require('winston'),
nconf = require('nconf'); nconf = require('nconf');
(function(Meta) { (function (Meta) {
Meta.configs = { Meta.configs = {
init: function(callback) { init: function (callback) {
Meta.configs.list(function(err, config) { Meta.configs.list(function (err, config) {
if (!err) { if (!err) {
Meta.config = config; Meta.config = config;
callback(); callback();
} else winston.error(err); } else {
winston.error(err);
}
}); });
}, },
list: function(callback) { list: function (callback) {
RDB.hgetall('config', function(err, config) { RDB.hgetall('config', function (err, config) {
if (!err) { if (!err) {
config = config || {}; config = config || {};
config.status = 'ok'; 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); RDB.hget('config', field, callback);
}, },
getFields: function(fields, callback) { getFields: function (fields, callback) {
RDB.hmgetObject('config', fields, callback); RDB.hmgetObject('config', fields, callback);
}, },
set: function(field, value, callback) { set: function (field, value, callback) {
RDB.hset('config', field, value, function(err, res) { RDB.hset('config', field, value, function (err, res) {
if (callback) if (callback) {
callback(err, res); callback(err, res);
}
}); });
}, },
setOnEmpty: function(field, value, callback) { setOnEmpty: function (field, value, callback) {
this.get(field, function(err, curValue) { this.get(field, function (err, curValue) {
if (!curValue) Meta.configs.set(field, value, callback); if (!curValue) {
else callback(); Meta.configs.set(field, value, callback);
} else {
callback();
}
}); });
}, },
remove: function(field) { remove: function (field) {
RDB.hdel('config', field); RDB.hdel('config', field);
} }
} };
Meta.themes = { Meta.themes = {
get: function(callback) { get: function (callback) {
var themePath = path.join(__dirname, '../node_modules'); var themePath = path.join(__dirname, '../node_modules');
fs.readdir(themePath, function(err, files) { fs.readdir(themePath, function (err, files) {
async.filter(files, function(file, next) { async.filter(files, function (file, next) {
fs.stat(path.join(themePath, file), function(err, fileStat) { fs.stat(path.join(themePath, file), function (err, fileStat) {
if (err) next(false); if (err) {
next(false);
}
next((fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); next((fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-'));
}); });
}, function(themes) { }, function (themes) {
async.map(themes, function(theme, next) { async.map(themes, function (theme, next) {
var config = path.join(themePath, theme, 'theme.json'); var config = path.join(themePath, theme, 'theme.json');
if (fs.existsSync(config)) { if (fs.existsSync(config)) {
fs.readFile(config, function(err, file) { fs.readFile(config, function (err, file) {
var configObj = JSON.parse(file.toString()); 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); next(err, configObj);
}); });
} else next(); } else {
}, function(err, themes) { next();
themes = themes.filter(function(theme) { }
}, function (err, themes) {
themes = themes.filter(function (theme) {
return (theme !== undefined); return (theme !== undefined);
}); });
callback(null, themes); callback(null, themes);
@@ -81,30 +94,33 @@ var utils = require('./../public/src/utils.js'),
}); });
}); });
} }
} };
Meta.title = { Meta.title = {
build: function(urlFragment, current_user, callback) { build: function (urlFragment, current_user, callback) {
var self = this, var self = this,
user = require('./user'); user = require('./user');
async.parallel({ async.parallel({
title: function(next) { title: function (next) {
self.parseFragment(urlFragment, next); self.parseFragment(urlFragment, next);
}, },
notifCount: function(next) { notifCount: function (next) {
user.notifications.getUnreadCount(current_user, next); user.notifications.getUnreadCount(current_user, next);
} }
}, function(err, values) { }, function (err, values) {
var title; var title;
if (err) title = Meta.config.title || 'NodeBB'; if (err) {
else title = (values.title ? values.title + ' | ' : '') + (Meta.config.title || 'NodeBB'); title = Meta.config.title || 'NodeBB';
} else {
title = (values.title ? values.title + ' | ' : '') + (Meta.config.title || 'NodeBB');
}
callback(null, title, values.notifCount); callback(null, title, values.notifCount);
}); });
}, },
parseFragment: function(urlFragment, callback) { parseFragment: function (urlFragment, callback) {
if (urlFragment === '') { if (urlFragment === '') {
callback(null, 'Index'); callback(null, 'Index');
} else if (urlFragment === 'recent') { } else if (urlFragment === 'recent') {
@@ -116,18 +132,95 @@ var utils = require('./../public/src/utils.js'),
} else if (/^category\/\d+\/?/.test(urlFragment)) { } else if (/^category\/\d+\/?/.test(urlFragment)) {
var cid = urlFragment.match(/category\/(\d+)/)[1]; 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); callback(null, name);
}); });
} else if (/^topic\/\d+\/?/.test(urlFragment)) { } else if (/^topic\/\d+\/?/.test(urlFragment)) {
var tid = urlFragment.match(/topic\/(\d+)/)[1]; 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); 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)); }(exports));

View File

@@ -137,7 +137,7 @@ var fs = require('fs'),
hookList = this.loadedHooks[hook]; hookList = this.loadedHooks[hook];
if (hookList && Array.isArray(hookList)) { 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]; var hookType = hook.split(':')[0];
switch (hookType) { switch (hookType) {
case 'filter': case 'filter':

View File

@@ -87,7 +87,7 @@ var RDB = require('./redis.js'),
}); });
}, },
function(next) { function(next) {
PostTools.toHTML(content, next); PostTools.parse(content, next);
} }
], function(err, results) { ], function(err, results) {
io.sockets.in('topic_' + results[0].tid).emit('event:post_edited', { 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 // 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') { if (err && err.message === 'no-undeleted-pids-found') {
threadTools.delete(postData.tid, -1, function(err) { threadTools.delete(postData.tid, -1, function(err) {
if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack); if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
@@ -164,7 +164,7 @@ var RDB = require('./redis.js'),
pid: pid 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) { posts.getPostField(pid, 'timestamp', function(timestamp) {
topics.updateTimestamp(postData.tid, 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 || ''; raw = raw || '';
plugins.fireHook('filter:post.parse', raw, function(parsed) { plugins.fireHook('filter:post.parse', raw, function(parsed) {
var cheerio = require('cheerio'); callback(null, parsed);
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>');
}
}); });
} }

View File

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

View File

@@ -8,24 +8,24 @@ var user = require('./../user.js'),
winston = require('winston'), winston = require('winston'),
nconf = require('nconf'); nconf = require('nconf');
(function(Admin) { (function (Admin) {
Admin.isAdmin = function(req, res, next) { Admin.isAdmin = function (req, res, next) {
user.isAdministrator((req.user && req.user.uid) ? req.user.uid : 0, function(isAdmin) { user.isAdministrator((req.user && req.user.uid) ? req.user.uid : 0, function (isAdmin) {
if (!isAdmin) res.redirect('/403'); if (!isAdmin) res.redirect('/403');
else next(); else next();
}); });
} }
Admin.build_header = function(res) { Admin.build_header = function (res) {
return templates['admin/header'].parse({ return templates['admin/header'].parse({
csrf: res.locals.csrf_token, csrf: res.locals.csrf_token,
relative_path: nconf.get('relative_path') relative_path: nconf.get('relative_path')
}); });
} }
Admin.create_routes = function(app) { Admin.create_routes = function (app) {
(function() { (function () {
var routes = [ var routes = [
'categories/active', 'categories/disabled', 'users', 'topics', 'settings', 'themes', 'categories/active', 'categories/disabled', 'users', 'topics', 'settings', 'themes',
'twitter', 'facebook', 'gplus', 'redis', 'motd', 'groups', '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++) { for (var i = 0, ii = routes.length; i < ii; i++) {
(function(route) { (function (route) {
app.get('/admin/' + route, Admin.isAdmin, function(req, res) { app.get('/admin/' + route, Admin.isAdmin, function (req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/' + route) + templates['admin/footer']); res.send(Admin.build_header(res) + app.create_route('admin/' + route) + templates['admin/footer']);
}); });
}(routes[i])); }(routes[i]));
@@ -44,8 +44,8 @@ var user = require('./../user.js'),
var unit_tests = ['categories']; var unit_tests = ['categories'];
for (var i = 0, ii = unit_tests.length; i < ii; i++) { for (var i = 0, ii = unit_tests.length; i < ii; i++) {
(function(route) { (function (route) {
app.get('/admin/testing/' + route, Admin.isAdmin, function(req, res) { 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']); res.send(Admin.build_header(res) + app.create_route('admin/testing/' + route) + templates['admin/footer']);
}); });
}(unit_tests[i])); }(unit_tests[i]));
@@ -53,21 +53,24 @@ var user = require('./../user.js'),
}()); }());
app.get('/admin', Admin.isAdmin, function(req, res) { 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']); res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']);
}); });
app.get('/admin/index', Admin.isAdmin, function(req, res) { app.get('/index', Admin.isAdmin, function (req, res) {
res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']); res.send(Admin.build_header(res) + app.create_route('admin/index') + templates['admin/footer']);
}); });
});
app.get('/api/admin/index', function(req, res) { app.namespace('/api/admin', function () {
app.get('/index', function (req, res) {
res.json({ res.json({
version: pkg.version version: pkg.version
}); });
}); });
app.get('/api/admin/users/search', function(req, res) { app.get('/users/search', function (req, res) {
res.json({ res.json({
search_display: 'block', search_display: 'block',
loadmore_display: 'none', loadmore_display: 'none',
@@ -75,8 +78,8 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/users/latest', function(req, res) { app.get('/users/latest', function (req, res) {
user.getUsers('users:joindate', 0, 49, function(err, data) { user.getUsers('users:joindate', 0, 49, function (err, data) {
res.json({ res.json({
search_display: 'none', search_display: 'none',
loadmore_display: 'block', loadmore_display: 'block',
@@ -86,8 +89,8 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/users/sort-posts', function(req, res) { app.get('/users/sort-posts', function (req, res) {
user.getUsers('users:postcount', 0, 49, function(err, data) { user.getUsers('users:postcount', 0, 49, function (err, data) {
res.json({ res.json({
search_display: 'none', search_display: 'none',
loadmore_display: 'block', loadmore_display: 'block',
@@ -97,8 +100,8 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/users/sort-reputation', function(req, res) { app.get('/users/sort-reputation', function (req, res) {
user.getUsers('users:reputation', 0, 49, function(err, data) { user.getUsers('users:reputation', 0, 49, function (err, data) {
res.json({ res.json({
search_display: 'none', search_display: 'none',
loadmore_display: 'block', loadmore_display: 'block',
@@ -108,8 +111,8 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/users', function(req, res) { app.get('/users', function (req, res) {
user.getUsers('users:joindate', 0, 49, function(err, data) { user.getUsers('users:joindate', 0, 49, function (err, data) {
res.json({ res.json({
search_display: 'none', search_display: 'none',
users: data, users: data,
@@ -118,40 +121,40 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/categories', function(req, res) { app.get('/categories', function (req, res) {
categories.getAllCategories(function(data) { categories.getAllCategories(function (data) {
res.json(data); res.json(data);
}); });
}); });
app.get('/api/admin/categories/active', function(req, res) { app.get('/categories/active', function (req, res) {
categories.getAllCategories(function(data) { categories.getAllCategories(function (data) {
data.categories = data.categories.filter(function(category) { data.categories = data.categories.filter(function (category) {
return (!category.disabled || category.disabled === "0"); return (!category.disabled || category.disabled === "0");
}); });
res.json(data); res.json(data);
}); });
}); });
app.get('/api/admin/categories/disabled', function(req, res) { app.get('/categories/disabled', function (req, res) {
categories.getAllCategories(function(data) { categories.getAllCategories(function (data) {
data.categories = data.categories.filter(function(category) { data.categories = data.categories.filter(function (category) {
return category.disabled === "1"; return category.disabled === "1";
}); });
res.json(data); res.json(data);
}); });
}); });
app.get('/api/admin/topics', function(req, res) { app.get('/topics', function (req, res) {
topics.getAllTopics(10, null, function(topics) { topics.getAllTopics(10, null, function (topics) {
res.json({ res.json({
topics: topics topics: topics
}); });
}); });
}); });
app.get('/api/admin/redis', function(req, res) { app.get('/redis', function (req, res) {
RDB.info(function(err, data) { RDB.info(function (err, data) {
data = data.split("\r\n"); data = data.split("\r\n");
var finalData = {}; var finalData = {};
@@ -177,8 +180,8 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/plugins', function(req, res) { app.get('/plugins', function (req, res) {
plugins.showInstalled(function(err, plugins) { plugins.showInstalled(function (err, plugins) {
if (err || !Array.isArray(plugins)) plugins = []; if (err || !Array.isArray(plugins)) plugins = [];
res.json(200, { res.json(200, {
@@ -187,43 +190,44 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/admin/settings', function(req, res) { app.get('/settings', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/motd', function(req, res) { app.get('/motd', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/themes', function(req, res) { app.get('/themes', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/twitter', function(req, res) { app.get('/twitter', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/facebook', function(req, res) { app.get('/facebook', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/gplus', function(req, res) { app.get('/gplus', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/testing/categories', function(req, res) { app.get('/testing/categories', function (req, res) {
res.json(200, {}); res.json(200, {});
}); });
app.get('/api/admin/groups', function(req, res) { app.get('/groups', function (req, res) {
Groups.list({ Groups.list({
expand: true expand: true
}, function(err, groups) { }, function (err, groups) {
res.json(200, { res.json(200, {
groups: groups groups: groups
}); });
}); });
}); });
});
}; };

View File

@@ -9,44 +9,45 @@ var user = require('./../user.js'),
nconf = require('nconf'); nconf = require('nconf');
(function(Api) { (function (Api) {
Api.create_routes = function(app) { Api.create_routes = function (app) {
app.get('/api/get_templates_listing', function(req, res) { app.namespace('/api', function () {
utils.walk(path.join(__dirname, '../../', 'public/templates'), function(err, data) { app.get('/get_templates_listing', function (req, res) {
utils.walk(path.join(__dirname, '../../', 'public/templates'), function (err, data) {
res.json(data); res.json(data);
}); });
}); });
app.get('/api/config', function(req, res, next) { app.get('/config', function (req, res, next) {
var config = require('../../public/config.json'); var config = require('../../public/config.json');
config['postDelay'] = meta.config['postDelay']; config.postDelay = meta.config.postDelay;
config['minimumTitleLength'] = meta.config['minimumTitleLength']; config.minimumTitleLength = meta.config.minimumTitleLength;
config['minimumPostLength'] = meta.config['minimumPostLength']; config.minimumPostLength = meta.config.minimumPostLength;
config['imgurClientIDSet'] = !! meta.config['imgurClientID']; config.imgurClientIDSet = !! meta.config.imgurClientID;
config['minimumUsernameLength'] = meta.config['minimumUsernameLength']; config.minimumUsernameLength = meta.config.minimumUsernameLength;
config['maximumUsernameLength'] = meta.config['maximumUsernameLength']; config.maximumUsernameLength = meta.config.maximumUsernameLength;
config['minimumPasswordLength'] = meta.config['minimumPasswordLength']; config.minimumPasswordLength = meta.config.minimumPasswordLength;
res.json(200, config); res.json(200, config);
}); });
app.get('/api/home', function(req, res) { app.get('/home', function (req, res) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
categories.getAllCategories(function(data) { categories.getAllCategories(function (data) {
data.categories = data.categories.filter(function(category) { data.categories = data.categories.filter(function (category) {
return (!category.disabled || category.disabled === "0"); return (!category.disabled || category.disabled === "0");
}); });
function iterator(category, callback) { function iterator(category, callback) {
categories.getRecentReplies(category.cid, 2, function(posts) { categories.getRecentReplies(category.cid, 2, function (posts) {
category["posts"] = posts; category.posts = posts;
category["post_count"] = posts.length > 2 ? 2 : posts.length; category.post_count = posts.length > 2 ? 2 : posts.length;
callback(null); callback(null);
}); });
} }
require('async').each(data.categories, iterator, function(err) { require('async').each(data.categories, iterator, function (err) {
data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : 'none'; 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>"); 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); res.json(data);
@@ -55,7 +56,7 @@ var user = require('./../user.js'),
}, uid); }, uid);
}); });
app.get('/api/login', function(req, res) { app.get('/login', function (req, res) {
var data = {}, var data = {},
login_strategies = auth.get_login_strategies(), login_strategies = auth.get_login_strategies(),
num_strategies = login_strategies.length; num_strategies = login_strategies.length;
@@ -80,7 +81,7 @@ var user = require('./../user.js'),
res.json(data); res.json(data);
}); });
app.get('/api/register', function(req, res) { app.get('/register', function (req, res) {
var data = {}, var data = {},
login_strategies = auth.get_login_strategies(), login_strategies = auth.get_login_strategies(),
num_strategies = login_strategies.length; num_strategies = login_strategies.length;
@@ -107,9 +108,9 @@ var user = require('./../user.js'),
res.json(data); res.json(data);
}); });
app.get('/api/topic/:id/:slug?', function(req, res, next) { app.get('/topic/:id/:slug?', function (req, res, next) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
topics.getTopicWithPosts(req.params.id, uid, 0, 10, function(err, data) { topics.getTopicWithPosts(req.params.id, uid, 0, 10, function (err, data) {
if (!err) { if (!err) {
if (data.deleted === '1' && data.expose_tools === 0) { if (data.deleted === '1' && data.expose_tools === 0) {
return res.json(404, {}); return res.json(404, {});
@@ -119,9 +120,9 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/category/:id/:slug?', function(req, res, next) { app.get('/category/:id/:slug?', function (req, res, next) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
categories.getCategoryById(req.params.id, uid, function(err, data) { categories.getCategoryById(req.params.id, uid, function (err, data) {
if (!err) if (!err)
res.json(data); res.json(data);
else else
@@ -129,29 +130,29 @@ var user = require('./../user.js'),
}, req.params.id, uid); }, req.params.id, uid);
}); });
app.get('/api/recent', function(req, res) { app.get('/recent', function (req, res) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
topics.getLatestTopics(uid, 0, 19, function(data) { topics.getLatestTopics(uid, 0, 19, function (data) {
res.json(data); res.json(data);
}); });
}); });
app.get('/api/unread', function(req, res) { app.get('/unread', function (req, res) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
topics.getUnreadTopics(uid, 0, 19, function(data) { topics.getUnreadTopics(uid, 0, 19, function (data) {
res.json(data); res.json(data);
}); });
}); });
app.get('/api/unread/total', function(req, res) { app.get('/unread/total', function (req, res) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
topics.getTotalUnread(uid, function(data) { topics.getTotalUnread(uid, function (data) {
res.json(data); res.json(data);
}); });
}); });
app.get('/api/confirm/:id', function(req, res) { app.get('/confirm/:id', function (req, res) {
user.email.confirm(req.params.id, function(data) { user.email.confirm(req.params.id, function (data) {
if (data.status === 'ok') { if (data.status === 'ok') {
res.json({ res.json({
'alert-class': 'alert-success', 'alert-class': 'alert-success',
@@ -168,7 +169,7 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/outgoing', function(req, res) { app.get('/outgoing', function (req, res) {
var url = req.query.url; var url = req.query.url;
if (url) { if (url) {
@@ -182,17 +183,18 @@ var user = require('./../user.js'),
} }
}); });
app.get('/api/search', function(req, res) { app.get('/search', function (req, res) {
return res.json({ return res.json({
show_no_topics: 'hide', show_no_topics: 'hide',
show_no_posts: 'hide', show_no_posts: 'hide',
show_results: 'hide',
search_query: '', search_query: '',
posts: [], posts: [],
topics: [] topics: []
}); });
}); });
app.get('/api/search/:term', function(req, res, next) { app.get('/search/:term', function (req, res, next) {
var reds = require('reds'); var reds = require('reds');
var postSearch = reds.createSearch('nodebbpostsearch'); var postSearch = reds.createSearch('nodebbpostsearch');
@@ -205,11 +207,11 @@ var user = require('./../user.js'),
} }
function searchPosts(callback) { function searchPosts(callback) {
search(postSearch, function(err, pids) { search(postSearch, function (err, pids) {
if (err) if (err)
return callback(err, null); return callback(err, null);
posts.getPostSummaryByPids(pids, function(err, posts) { posts.getPostSummaryByPids(pids, function (err, posts) {
if (err) if (err)
return callback(err, null); return callback(err, null);
callback(null, posts); callback(null, posts);
@@ -218,23 +220,24 @@ var user = require('./../user.js'),
} }
function searchTopics(callback) { function searchTopics(callback) {
search(topicSearch, function(err, tids) { search(topicSearch, function (err, tids) {
if (err) if (err)
return callback(err, null); return callback(err, null);
topics.getTopicsByTids(tids, 0, function(topics) { topics.getTopicsByTids(tids, 0, function (topics) {
callback(null, topics); callback(null, topics);
}, 0); }, 0);
}); });
} }
async.parallel([searchPosts, searchTopics], function(err, results) { async.parallel([searchPosts, searchTopics], function (err, results) {
if (err) if (err)
return next(); return next();
return res.json({ return res.json({
show_no_topics: results[1].length ? 'hide' : '', show_no_topics: results[1].length ? 'hide' : '',
show_no_posts: results[0].length ? 'hide' : '', show_no_posts: results[0].length ? 'hide' : '',
show_results: '',
search_query: req.params.term, search_query: req.params.term,
posts: results[0], posts: results[0],
topics: results[1] topics: results[1]
@@ -242,22 +245,23 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/api/reset', function(req, res) { app.get('/reset', function (req, res) {
res.json({}); res.json({});
}); });
app.get('/api/reset/:code', function(req, res) { app.get('/reset/:code', function (req, res) {
res.json({ res.json({
reset_code: req.params.code reset_code: req.params.code
}); });
}); });
app.get('/api/404', function(req, res) { app.get('/404', function (req, res) {
res.json({}); res.json({});
}); });
app.get('/api/403', function(req, res) { app.get('/403', function (req, res) {
res.json({}); res.json({});
}); });
});
} }
}(exports)); }(exports));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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