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

@@ -14,7 +14,9 @@ var utils = require('./../public/src/utils.js'),
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) {
@@ -36,20 +38,24 @@ var utils = require('./../public/src/utils.js'),
}, },
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) {
@@ -57,7 +63,9 @@ var utils = require('./../public/src/utils.js'),
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-'));
}); });
@@ -68,10 +76,15 @@ var utils = require('./../public/src/utils.js'),
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 {
next();
}
}, function (err, themes) { }, function (err, themes) {
themes = themes.filter(function (theme) { themes = themes.filter(function (theme) {
return (theme !== undefined); return (theme !== undefined);
@@ -81,7 +94,7 @@ var utils = require('./../public/src/utils.js'),
}); });
}); });
} }
} };
Meta.title = { Meta.title = {
build: function (urlFragment, current_user, callback) { build: function (urlFragment, current_user, callback) {
@@ -98,8 +111,11 @@ var utils = require('./../public/src/utils.js'),
}, 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);
}); });
@@ -125,9 +141,86 @@ var utils = require('./../public/src/utils.js'),
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

@@ -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,7 +78,7 @@ 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',
@@ -86,7 +89,7 @@ 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',
@@ -97,7 +100,7 @@ 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',
@@ -108,7 +111,7 @@ 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',
@@ -118,13 +121,13 @@ 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");
@@ -133,7 +136,7 @@ var user = require('./../user.js'),
}); });
}); });
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";
@@ -142,7 +145,7 @@ var user = require('./../user.js'),
}); });
}); });
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
@@ -150,7 +153,7 @@ var user = require('./../user.js'),
}); });
}); });
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,7 +180,7 @@ 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 = [];
@@ -187,35 +190,35 @@ 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) {
@@ -224,6 +227,7 @@ var user = require('./../user.js'),
}); });
}); });
}); });
});
}; };

View File

@@ -11,27 +11,28 @@ var user = require('./../user.js'),
(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 () {
app.get('/get_templates_listing', function (req, res) {
utils.walk(path.join(__dirname, '../../', 'public/templates'), function (err, data) { 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) {
@@ -40,8 +41,8 @@ var user = require('./../user.js'),
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);
}); });
} }
@@ -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,7 +108,7 @@ 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) {
@@ -119,7 +120,7 @@ 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)
@@ -129,28 +130,28 @@ 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({
@@ -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');
@@ -235,6 +237,7 @@ var user = require('./../user.js'),
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,7 +5,8 @@ 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) {
@@ -24,10 +25,10 @@ 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
@@ -36,7 +37,7 @@ var user = require('./../user.js'),
}); });
}); });
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
@@ -45,7 +46,7 @@ var user = require('./../user.js'),
}); });
}); });
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
@@ -54,7 +55,7 @@ var user = require('./../user.js'),
}); });
}); });
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
@@ -63,7 +64,16 @@ var user = require('./../user.js'),
}); });
}); });
app.get('/users/search', function(req, res) { app.get('/online', function (req, res) {
app.build_header({
req: req,
res: res
}, function (err, header) {
res.send(header + app.create_route("users/online", "users") + templates['footer']);
});
});
app.get('/search', function (req, res) {
app.build_header({ app.build_header({
req: req, req: req,
res: res res: res
@@ -71,8 +81,10 @@ var user = require('./../user.js'),
res.send(header + app.create_route("users/search", "users") + templates['footer']); res.send(header + app.create_route("users/search", "users") + templates['footer']);
}); });
}); });
});
app.get('/user/:userslug', function(req, res, next) { app.namespace('/user', function () {
app.get('/:userslug', function (req, res, next) {
if (!req.params.userslug) { if (!req.params.userslug) {
next(); next();
@@ -94,7 +106,7 @@ var user = require('./../user.js'),
}); });
}); });
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');
@@ -113,7 +125,7 @@ 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');
@@ -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;
} }
@@ -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) {
@@ -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,6 +448,7 @@ 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);
@@ -466,6 +482,16 @@ var user = require('./../user.js'),
}); });
} }
function getOnlineUsers(req, res) {
user.getUsers('users:online', 0, 49, function (err, data) {
res.json({
search_display: 'none',
loadmore_display: 'block',
users: data
});
});
}
function getUsersForSearch(req, res) { function getUsersForSearch(req, res) {
res.json({ res.json({
search_display: 'block', search_display: 'block',

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

@@ -25,7 +25,17 @@ var express = require('express'),
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
@@ -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,
@@ -238,6 +250,11 @@ var express = require('express'),
}, },
"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);
} }
@@ -433,7 +450,6 @@ var express = require('express'),
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");
}); });
@@ -481,6 +497,8 @@ 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
@@ -490,6 +508,8 @@ var express = require('express'),
}); });
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
@@ -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) {