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

v1.18.x
Julian Lam 11 years ago
commit 8e1137b5dd

@ -25,7 +25,6 @@
"passport-facebook": "0.1.5", "passport-facebook": "0.1.5",
"less-middleware": "0.1.12", "less-middleware": "0.1.12",
"marked": "0.2.8", "marked": "0.2.8",
"bcrypt": "0.7.5",
"async": "~0.2.8", "async": "~0.2.8",
"node-imagemagick": "0.1.8", "node-imagemagick": "0.1.8",
"gravatar": "1.0.6", "gravatar": "1.0.6",
@ -47,7 +46,8 @@
"semver": "~2.2.1", "semver": "~2.2.1",
"string": "~1.7.0", "string": "~1.7.0",
"xregexp": "~2.0.0", "xregexp": "~2.0.0",
"socket.io-wildcard": "~0.1.1" "socket.io-wildcard": "~0.1.1",
"bcryptjs": "~0.7.10"
}, },
"optionalDependencies": { "optionalDependencies": {
"redis": "0.8.3", "redis": "0.8.3",

@ -396,6 +396,16 @@ var socket,
}); });
}; };
app.enableInfiniteLoading = function(callback) {
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom) {
callback();
}
});
}
var titleObj = { var titleObj = {
active: false, active: false,
interval: undefined, interval: undefined,

@ -178,13 +178,13 @@ define(['uploader'], function(uploader) {
}); });
// Permissions modal // Permissions modal
$('.permissions').on('click', function() { $('.admin-categories').on('click', '.permissions', function() {
var cid = $(this).parents('li[data-cid]').attr('data-cid'); var cid = $(this).parents('li[data-cid]').attr('data-cid');
Categories.launchPermissionsModal(cid); Categories.launchPermissionsModal(cid);
}); });
$('.upload-button').on('click', function() { $('.admin-categories').on('click', '.upload-button', function() {
var inputEl = this; var inputEl = this;
var cid = $(this).parents('li[data-cid]').attr('data-cid'); var cid = $(this).parents('li[data-cid]').attr('data-cid');
uploader.open(RELATIVE_PATH + '/admin/category/uploadpicture', {cid:cid}, function(imageUrlOnServer) { uploader.open(RELATIVE_PATH + '/admin/category/uploadpicture', {cid:cid}, function(imageUrlOnServer) {
@ -196,7 +196,7 @@ define(['uploader'], function(uploader) {
}); });
}); });
$('.admin-categories').delegate('.delete-image', 'click', function() { $('.admin-categories').on('click', '.delete-image', function() {
var parent = $(this).parents('li[data-cid]'), var parent = $(this).parents('li[data-cid]'),
inputEl = parent.find('.upload-button'), inputEl = parent.find('.upload-button'),
preview = parent.find('.preview-box'), preview = parent.find('.preview-box'),

@ -18,6 +18,7 @@ define(function() {
}; };
Admin.updateRoomUsage = function(err, data) { Admin.updateRoomUsage = function(err, data) {
function getUserCountIn(room) { function getUserCountIn(room) {
var count = 0; var count = 0;
for(var user in data[room]) { for(var user in data[room]) {
@ -25,27 +26,36 @@ define(function() {
} }
return count; return count;
} }
var active_users = $('#active_users'),
var active_users = $('#active_users').html(''),
total = 0; total = 0;
if(!active_users.length) { if(!active_users.length) {
return; return;
} }
active_users.html('');
var usersHtml = ''; var sortedData = [];
for (var room in data) { for (var room in data) {
if (room !== '') { if (room !== '') {
var count = getUserCountIn(room); sortedData.push({room: room, count: data[room].length});
total += count; total += data[room].length;
usersHtml += "<div class='alert alert-success'><strong>" + room + "</strong> " + count + " active user" + (count > 1 ? "s" : "") + "</div>"; }
} }
sortedData.sort(function(a, b) {
return parseInt(b.count, 10) - parseInt(a.count, 10);
});
var usersHtml = '';
for(var i=0; i<sortedData.length; ++i) {
usersHtml += "<div class='alert alert-success'><strong>" + sortedData[i].room + "</strong> " +
sortedData[i].count + " active user" + (sortedData[i].count > 1 ? "s" : "") + "</div>";
} }
active_users.html(usersHtml); active_users.html(usersHtml);
document.getElementById('connections').innerHTML = total; $('#connections').html(total);
}; };
return Admin; return Admin;

@ -44,11 +44,9 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
function enableInfiniteLoading() { function enableInfiniteLoading() {
if(!config.usePagination) { if(!config.usePagination) {
$(window).off('scroll').on('scroll', function (ev) { app.enableInfiniteLoading(function() {
var bottom = ($(document).height() - $(window).height()) * 0.9; if(!loadingMoreTopics) {
Category.loadMoreTopics(templates.get('category_id'));
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
Category.loadMoreTopics(cid);
} }
}); });
} else { } else {
@ -147,7 +145,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
loadingMoreTopics = true; loadingMoreTopics = true;
socket.emit('categories.loadMore', { socket.emit('categories.loadMore', {
cid: cid, cid: cid,
after: $('#topics-container').children('.category-item').length after: $('#topics-container').attr('data-nextstart')
}, function (err, data) { }, function (err, data) {
if(err) { if(err) {
return app.alertError(err.message); return app.alertError(err.message);
@ -155,6 +153,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
if (data && data.topics.length) { if (data && data.topics.length) {
Category.onTopicsLoaded(data.topics); Category.onTopicsLoaded(data.topics);
$('#topics-container').attr('data-nextstart', data.nextStart);
} }
loadingMoreTopics = false; loadingMoreTopics = false;
}); });

@ -52,12 +52,6 @@ define(function() {
}); });
document.querySelector('#content input').focus(); document.querySelector('#content input').focus();
if(!config.emailSetup)
$('#reset-link').addClass('hide');
else
$('#reset-link').removeClass('hide');
}; };
return Login; return Login;

@ -14,11 +14,11 @@ define(function() {
$('.pagination') $('.pagination')
.on('click', '.previous', function() { .on('click', '.previous', function() {
pagination.loadPage(pagination.currentPage - 1); return pagination.loadPage(pagination.currentPage - 1);
}).on('click', '.next', function() { }).on('click', '.next', function() {
pagination.loadPage(pagination.currentPage + 1); return pagination.loadPage(pagination.currentPage + 1);
}).on('click', '.page', function() { }).on('click', '.page', function() {
pagination.loadPage($(this).attr('data-page')); return pagination.loadPage($(this).attr('data-page'));
}).on('click', '.select_page', function(e) { }).on('click', '.select_page', function(e) {
e.preventDefault(); e.preventDefault();
bootbox.prompt('Enter page number:', function(pageNum) { bootbox.prompt('Enter page number:', function(pageNum) {
@ -58,7 +58,7 @@ define(function() {
for(var i=0; i<pagesToShow.length; ++i) { for(var i=0; i<pagesToShow.length; ++i) {
if(i > 0) { if(i > 0) {
if (pagesToShow[i] - 1 !== pagesToShow[i-1]) { if (pagesToShow[i] - 1 !== pagesToShow[i-1]) {
html += '<li class="disabled"><a class="select_page" href="#">|</a></li>'; html += '<li><a class="select_page" href="#">|</a></li>';
} }
} }
html += '<li class="page" data-page="' + pagesToShow[i] + '"><a href="#">' + pagesToShow[i] + '</a></li>'; html += '<li class="page" data-page="' + pagesToShow[i] + '"><a href="#">' + pagesToShow[i] + '</a></li>';
@ -72,10 +72,11 @@ define(function() {
pagination.loadPage = function(page, callback) { pagination.loadPage = function(page, callback) {
page = parseInt(page, 10); page = parseInt(page, 10);
if(!utils.isNumber(page) || page < 1 || page > pagination.pageCount) { if(!utils.isNumber(page) || page < 1 || page > pagination.pageCount) {
return; return false;
} }
ajaxify.go(window.location.pathname.slice(1) + '?page=' + page); ajaxify.go(window.location.pathname.slice(1) + '?page=' + page);
return true;
} }
function updatePageLinks() { function updatePageLinks() {

@ -1,20 +1,16 @@
define(function() { define(function() {
var Recent = {}; var Recent = {};
Recent.newTopicCount = 0; var newTopicCount = 0,
Recent.newPostCount = 0; newPostCount = 0,
Recent.loadingMoreTopics = false; loadingMoreTopics = false;
var active = ''; var active = '';
Recent.init = function() { Recent.init = function() {
app.enterRoom('recent_posts'); app.enterRoom('recent_posts');
ajaxify.register_events([ Recent.watchForNewPosts();
'event:new_topic',
'event:new_post'
]);
function getActiveSection() { function getActiveSection() {
var url = window.location.href, var url = window.location.href,
@ -37,26 +33,34 @@ define(function() {
$(this).addClass('hide'); $(this).addClass('hide');
}); });
socket.on('event:new_topic', function(data) {
++Recent.newTopicCount; app.enableInfiniteLoading(function() {
Recent.updateAlertText(); if(!loadingMoreTopics) {
Recent.loadMoreTopics();
}
});
};
Recent.watchForNewPosts = function () {
newPostCount = 0;
newTopicCount = 0;
ajaxify.register_events([
'event:new_topic',
'event:new_post'
]);
socket.on('event:new_topic', function(data) {
++newTopicCount;
Recent.updateAlertText();
}); });
socket.on('event:new_post', function(data) { socket.on('event:new_post', function(data) {
++Recent.newPostCount; ++newPostCount;
Recent.updateAlertText(); Recent.updateAlertText();
}); });
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !Recent.loadingMoreTopics) {
Recent.loadMoreTopics();
} }
});
};
Recent.updateAlertText = function() { Recent.updateAlertText = function() {
var text = 'There'; var text = 'There';
@ -76,41 +80,44 @@ define(function() {
text += '. Click here to reload.'; text += '. Click here to reload.';
$('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow'); $('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow');
$('#category-no-topics').addClass('hide');
} }
Recent.onTopicsLoaded = function(topics) { Recent.loadMoreTopics = function() {
var html = templates.prepare(templates['recent'].blocks['topics']).parse({ loadingMoreTopics = true;
socket.emit('topics.loadMoreRecentTopics', {
after: $('#topics-container').attr('data-nextstart'),
term: active
}, function(err, data) {
if(err) {
return app.alertError(err.message);
}
if (data.topics && data.topics.length) {
Recent.onTopicsLoaded('recent', data.topics);
$('#topics-container').attr('data-nextstart', data.nextStart);
}
loadingMoreTopics = false;
});
}
Recent.onTopicsLoaded = function(template, topics) {
var html = templates.prepare(templates[template].blocks['topics']).parse({
topics: topics topics: topics
}); });
translator.translate(html, function(translatedHTML) { translator.translate(html, function(translatedHTML) {
var container = $('#topics-container');
$('#category-no-topics').remove(); $('#category-no-topics').remove();
html = $(translatedHTML); html = $(translatedHTML);
container.append(html); $('#topics-container').append(html);
$('span.timeago').timeago(); $('span.timeago').timeago();
app.createUserTooltips(); app.createUserTooltips();
app.makeNumbersHumanReadable(html.find('.human-readable-number')); app.makeNumbersHumanReadable(html.find('.human-readable-number'));
}); });
} }
Recent.loadMoreTopics = function() {
Recent.loadingMoreTopics = true;
socket.emit('topics.loadMoreRecentTopics', {
after: $('#topics-container').children('li').length,
term: active
}, function(err, data) {
if(err) {
return app.alertError(err.message);
}
if (data.topics && data.topics.length) {
Recent.onTopicsLoaded(data.topics);
}
Recent.loadingMoreTopics = false;
});
}
return Recent; return Recent;
}); });

@ -2,9 +2,9 @@ define(function() {
var Search = {}; var Search = {};
Search.init = function() { Search.init = function() {
var searchQuery = $('#topics-container').attr('data-search-query'); var searchQuery = $('#topic-results').attr('data-search-query');
console.log(searchQuery);
$('.search-result-text').each(function() { $('.search-result-text').children().each(function() {
var text = $(this).html(); var text = $(this).html();
var regex = new RegExp(searchQuery, 'gi'); var regex = new RegExp(searchQuery, 'gi');
text = text.replace(regex, '<span class="label label-success">' + searchQuery + '</span>'); text = text.replace(regex, '<span class="label label-success">' + searchQuery + '</span>');

@ -346,10 +346,8 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
function enableInfiniteLoading() { function enableInfiniteLoading() {
if(!config.usePagination) { if(!config.usePagination) {
$(window).off('scroll').on('scroll', function() { app.enableInfiniteLoading(function() {
var bottom = ($(document).height() - $(window).height()) * 0.9; if (!infiniteLoaderActive && $('#post-container').children().length) {
if ($(window).scrollTop() > bottom && !infiniteLoaderActive && $('#post-container').children().length) {
loadMorePosts(tid, function(posts) { loadMorePosts(tid, function(posts) {
fixDeleteStateForPosts(); fixDeleteStateForPosts();
}); });
@ -396,7 +394,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
socket.emit('posts.getRawPost', pid, function(err, post) { socket.emit('posts.getRawPost', pid, function(err, post) {
if(err) { if(err) {
return app.alert(err.message); return app.alertError(err.message);
} }
var quoted = ''; var quoted = '';
if(post) { if(post) {
@ -895,47 +893,34 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
} }
function toggle_post_delete_state(pid) { function toggle_post_delete_state(pid) {
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')); var postEl = $('#post-container li[data-pid="' + pid + '"]');
if (postEl[0]) {
quoteEl = postEl.find('.quote'),
favEl = postEl.find('.favourite'),
replyEl = postEl.find('.post_reply');
socket.emit('posts.getPrivileges', pid, function(err, privileges) {
if(err) {
return app.alert(err.message);
}
if (privileges.editable) {
if (!postEl.hasClass('deleted')) {
toggle_post_tools(pid, false);
} else {
toggle_post_tools(pid, true);
}
}
if (postEl.length) {
postEl.toggleClass('deleted'); postEl.toggleClass('deleted');
toggle_post_tools(pid, postEl.hasClass('deleted'));
updatePostCount(); updatePostCount();
});
} }
} }
function toggle_post_tools(pid, state) { function toggle_post_tools(pid, isDeleted) {
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')), var postEl = $('#post-container li[data-pid="' + pid + '"]'),
quoteEl = $(postEl[0].querySelector('.quote')), quoteEl = $(postEl[0].querySelector('.quote')),
favEl = $(postEl[0].querySelector('.favourite')), favEl = $(postEl[0].querySelector('.favourite')),
replyEl = $(postEl[0].querySelector('.post_reply')); replyEl = $(postEl[0].querySelector('.post_reply')),
chatEl = $(postEl[0].querySelector('.chat'));
if (state) { if (isDeleted) {
quoteEl.removeClass('none');
favEl.removeClass('none');
replyEl.removeClass('none');
} else {
quoteEl.addClass('none'); quoteEl.addClass('none');
favEl.addClass('none'); favEl.addClass('none');
replyEl.addClass('none'); replyEl.addClass('none');
chatEl.addClass('none');
} else {
quoteEl.removeClass('none');
favEl.removeClass('none');
replyEl.removeClass('none');
chatEl.removeClass('none');
} }
} }
@ -1112,7 +1097,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
var insertAfter = findInsertionPoint(); var insertAfter = findInsertionPoint();
parseAndTranslatePosts(data.posts, function(translatedHTML) { parseAndTranslatePosts(data, function(translatedHTML) {
var translated = $(translatedHTML); var translated = $(translatedHTML);
if(!infiniteLoaded) { if(!infiniteLoaded) {
@ -1127,8 +1112,8 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
}); });
} }
function parseAndTranslatePosts(posts, callback) { function parseAndTranslatePosts(data, callback) {
var html = templates.prepare(templates['topic'].blocks['posts']).parse({posts: posts}); var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
var regexp = new RegExp("<!--[\\s]*IF @first[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF @first[\\s]*-->", 'g'); var regexp = new RegExp("<!--[\\s]*IF @first[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF @first[\\s]*-->", 'g');
html = html.replace(regexp, ''); html = html.replace(regexp, '');

@ -1,53 +1,15 @@
define(function() { define(['forum/recent'], function(recent) {
var Unread = {}, var Unread = {},
loadingMoreTopics = false; loadingMoreTopics = false;
Unread.init = function() { Unread.init = function() {
app.enterRoom('recent_posts'); app.enterRoom('recent_posts');
ajaxify.register_events([
'event:new_topic',
'event:new_post',
'topics.markAllRead'
]);
var newTopicCount = 0,
newPostCount = 0;
$('#new-topics-alert').on('click', function() { $('#new-topics-alert').on('click', function() {
$(this).addClass('hide'); $(this).addClass('hide');
}); });
socket.on('event:new_topic', function(data) { recent.watchForNewPosts();
++newTopicCount;
updateAlertText();
});
function updateAlertText() {
var text = 'There';
if (newTopicCount > 1) {
text += ' are ' + newTopicCount + ' new topics';
} else if (newTopicCount === 1) {
text += ' is a new topic';
}
if (newPostCount > 1) {
text += (newTopicCount?' and ':' are ') + newPostCount + ' new posts';
} else if(newPostCount === 1) {
text += (newTopicCount?' and ':' is ') + ' a new post';
}
text += '. Click here to reload.';
$('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow');
$('#category-no-topics').addClass('hidden');
}
socket.on('event:new_post', function(data) {
++newPostCount;
updateAlertText();
});
$('#mark-allread-btn').on('click', function() { $('#mark-allread-btn').on('click', function() {
var btn = $(this); var btn = $(this);
@ -67,37 +29,32 @@ define(function() {
}); });
}); });
function onTopicsLoaded(topics) { if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20) {
$('#load-more-btn').show();
}
var html = templates.prepare(templates['unread'].blocks['topics']).parse({ $('#load-more-btn').on('click', function() {
topics: topics loadMoreTopics();
}); });
translator.translate(html, function(translatedHTML) { app.enableInfiniteLoading(function() {
var container = $('#topics-container'); if(!loadingMoreTopics) {
loadMoreTopics();
$('#category-no-topics').remove();
html = $(translatedHTML);
container.append(html);
$('span.timeago').timeago();
app.createUserTooltips();
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
});
} }
});
function loadMoreTopics() { function loadMoreTopics() {
loadingMoreTopics = true; loadingMoreTopics = true;
socket.emit('topics.loadMoreUnreadTopics', { socket.emit('topics.loadMoreUnreadTopics', {
after: parseInt($('#topics-container').attr('data-next-start'), 10) after: $('#topics-container').attr('data-nextstart')
}, function(err, data) { }, function(err, data) {
if(err) { if(err) {
return app.alertError(err.message); return app.alertError(err.message);
} }
if (data.topics && data.topics.length) { if (data.topics && data.topics.length) {
onTopicsLoaded(data.topics); recent.onTopicsLoaded('unread', data.topics);
$('#topics-container').attr('data-next-start', data.nextStart); $('#topics-container').attr('data-nextstart', data.nextStart);
} else { } else {
$('#load-more-btn').hide(); $('#load-more-btn').hide();
} }
@ -105,23 +62,6 @@ define(function() {
loadingMoreTopics = false; loadingMoreTopics = false;
}); });
} }
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics();
}
});
if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20) {
$('#load-more-btn').show();
}
$('#load-more-btn').on('click', function() {
loadMoreTopics();
});
}; };
return Unread; return Unread;

@ -106,6 +106,7 @@
collapseWhitespace : /\s+/g, collapseWhitespace : /\s+/g,
collapseDash : /-+/g, collapseDash : /-+/g,
trimTrailingDash : /-$/g, trimTrailingDash : /-$/g,
trimLeadingDash : /^-/g,
isLatin : /^[\w]+$/, isLatin : /^[\w]+$/,
//http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/ //http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
@ -120,6 +121,7 @@
str = str.replace(utils.collapseWhitespace, '-') str = str.replace(utils.collapseWhitespace, '-')
str = str.replace(utils.collapseDash, '-'); str = str.replace(utils.collapseDash, '-');
str = str.replace(utils.trimTrailingDash, ''); str = str.replace(utils.trimTrailingDash, '');
str = str.replace(utils.trimLeadingDash, '');
return str; return str;
}, },

@ -28,7 +28,7 @@
<div class="category row"> <div class="category row">
<div class="{topic_row_size}"> <div class="{topic_row_size}">
<ul id="topics-container" itemscope itemtype="http://www.schema.org/ItemList"> <ul id="topics-container" itemscope itemtype="http://www.schema.org/ItemList" data-nextstart="{nextStart}">
<meta itemprop="itemListOrder" content="descending"> <meta itemprop="itemListOrder" content="descending">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li class="category-item {topics.deleted-class}" itemprop="itemListElement"> <li class="category-item {topics.deleted-class}" itemprop="itemListElement">

@ -41,7 +41,10 @@
<div class="form-group"> <div class="form-group">
<div class="col-lg-offset-2 col-lg-10"> <div class="col-lg-offset-2 col-lg-10">
<hr /> <hr />
<button class="btn btn-primary btn-lg btn-block" id="login" type="submit">[[login:login]]</button> &nbsp; <a id="reset-link" class="hide" href="{relative_path}/reset">[[login:forgot_password]]</a> <button class="btn btn-primary btn-lg btn-block" id="login" type="submit">[[login:login]]</button>
<!-- IF showResetLink -->
&nbsp; <a id="reset-link" href="{relative_path}/reset">[[login:forgot_password]]</a>
<!-- ENDIF showResetLink -->
</div> </div>
</div> </div>
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" /> <input type="hidden" name="_csrf" value="{token}" id="csrf-token" />

@ -21,7 +21,7 @@
<div class="category row"> <div class="category row">
<div class="col-md-12"> <div class="col-md-12">
<ul id="topics-container"> <ul id="topics-container" data-nextstart="{nextStart}">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li class="category-item {topics.deleted-class}"> <li class="category-item {topics.deleted-class}">
<div class="col-md-12 col-xs-12 panel panel-default topic-row"> <div class="col-md-12 col-xs-12 panel panel-default topic-row">

@ -13,8 +13,11 @@
</div> </div>
</form> </form>
<div class="category search"> <div class="search favourites well">
<div class="{show_results}"> <div class="{show_results} row">
<div id="topic-results" class="col-md-12" data-search-query="{search_query}">
<h3>[[topic:topics]]</h3> <h3>[[topic:topics]]</h3>
<!-- IF topic_matches --> <!-- IF topic_matches -->
@ -22,47 +25,74 @@
<!-- ENDIF topic_matches --> <!-- ENDIF topic_matches -->
<div class="alert alert-info {show_no_topics}">[[topic:no_topics_found]]</div> <div class="alert alert-info {show_no_topics}">[[topic:no_topics_found]]</div>
<ul id="topics-container" data-search-query="{search_query}">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li class="category-item"> <div class="topic-row panel panel-default clearfix">
<a href="{relative_path}/topic/{topics.slug}" id="tid-{topics.tid}"> <div class="panel-body">
<a href="../../user/{topics.userslug}">
<img title="{topics.username}" class="img-rounded user-img" src="{topics.picture}">
</a>
<a href="../../user/{topics.userslug}">
<strong><span>{topics.username}</span></strong>
</a>
<span class="search-result-text">
<p>{topics.title}</p>
</span>
<div> <div>
<div class="col-md-12 img-thumbnail"> <small>
<div class="search-result-post"> <span class="pull-right">
<img src="{topics.teaser_userpicture}" /> <a href="../../topic/{topics.slug}">posted</a>
<strong>{topics.teaser_username}</strong>: <span class="search-result-text">{topics.title}</span> in
<a href="../../category/{topics.categorySlug}">
<i class="fa {topics.categoryIcon}"></i> {topics.categoryName}
</a>
<span class="timeago" title="{topics.relativeTime}"></span>
</span>
</small>
</div> </div>
</div> </div>
</div> </div>
</a>
</li>
<!-- END topics --> <!-- END topics -->
</ul> </div>
<div id="post-results" class="col-md-12" data-search-query="{search_query}">
<h3>Posts</h3> <h3>Posts</h3>
<!-- IF post_matches --> <!-- IF post_matches -->
<small>{post_matches} result(s) matching "{search_query}"</small> <small>{post_matches} result(s) matching "{search_query}"</small>
<!-- ENDIF post_matches --> <!-- ENDIF post_matches -->
<div class="alert alert-info {show_no_posts}">No posts found!</div> <div class="alert alert-info {show_no_posts}">No posts found!</div>
<ul id="topics-container" data-search-query="{search_query}">
<!-- BEGIN posts --> <!-- BEGIN posts -->
<li class="category-item"> <div class="topic-row panel panel-default clearfix">
<a href="{relative_path}/topic/{posts.topicSlug}#{posts.pid}" id="tid-{posts.tid}"> <div class="panel-body">
<a href="../../user/{posts.userslug}">
<img title="{posts.username}" class="img-rounded user-img" src="{posts.picture}">
</a>
<a href="../../user/{posts.userslug}">
<strong><span>{posts.username}</span></strong>
</a>
<span class="search-result-text">
{posts.content}
</span>
<div> <div>
<div class="col-md-12 img-thumbnail"> <small>
<div class="search-result-post"> <span class="pull-right">
<img src="{posts.picture}" /> <a href="../../topic/{posts.topicSlug}#{posts.pid}">posted</a>
<strong>{posts.username}</strong>: <span class="search-result-text">{posts.content}</span> in
<a href="../../category/{posts.categorySlug}">
<i class="fa {posts.categoryIcon}"></i> {posts.categoryName}
</a>
<span class="timeago" title="{posts.relativeTime}"></span>
</span>
</small>
</div> </div>
</div> </div>
</div> </div>
</a>
</li>
<!-- END posts --> <!-- END posts -->
</ul> </div>
</div> </div>
</div> </div>

@ -186,7 +186,7 @@
<!-- IF privileges.write --> <!-- IF privileges.write -->
<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button> <button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
<!-- ENDIF privileges.write --> <!-- ENDIF privileges.write -->
<div class="btn-group thread-tools hide"> <div class="btn-group thread-tools hide dropup">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button> <button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
<ul class="dropdown-menu pull-right"> <ul class="dropdown-menu pull-right">
<li><a href="#" class="markAsUnreadForAll"><i class="fa fa-inbox"></i> [[topic:thread_tools.markAsUnreadForAll]]</a></li> <li><a href="#" class="markAsUnreadForAll"><i class="fa fa-inbox"></i> [[topic:thread_tools.markAsUnreadForAll]]</a></li>

@ -16,7 +16,7 @@
<div class="category row"> <div class="category row">
<div class="col-md-12"> <div class="col-md-12">
<ul id="topics-container" data-next-start="{nextStart}"> <ul id="topics-container" data-nextstart="{nextStart}">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li class="category-item {topics.deleted-class}"> <li class="category-item {topics.deleted-class}">
<div class="col-md-12 col-xs-12 panel panel-default topic-row"> <div class="col-md-12 col-xs-12 panel panel-default topic-row">

@ -1,8 +1,8 @@
var db = require('./database.js'), var db = require('./database'),
posts = require('./posts.js'), posts = require('./posts'),
utils = require('./../public/src/utils.js'), utils = require('./../public/src/utils'),
user = require('./user.js'), user = require('./user'),
topics = require('./topics.js'), topics = require('./topics'),
plugins = require('./plugins'), plugins = require('./plugins'),
CategoryTools = require('./categoryTools'), CategoryTools = require('./categoryTools'),
meta = require('./meta'), meta = require('./meta'),
@ -52,8 +52,8 @@ var db = require('./database.js'),
return callback(err); return callback(err);
} }
function getTopicIds(next) { function getTopics(next) {
Categories.getTopicIds(category_id, start, end, next); Categories.getCategoryTopics(category_id, start, end, current_user, next);
} }
function getActiveUsers(next) { function getActiveUsers(next) {
@ -70,9 +70,12 @@ var db = require('./database.js'),
Categories.getPageCount(category_id, next); Categories.getPageCount(category_id, next);
} }
async.parallel([getTopicIds, getActiveUsers, getSidebars, getPageCount], function(err, results) { async.parallel([getTopics, getActiveUsers, getSidebars, getPageCount], function(err, results) {
var tids = results[0], if(err) {
active_users = results[1], return callback(err);
}
var active_users = results[1],
sidebars = results[2], sidebars = results[2],
pageCount = results[3]; pageCount = results[3];
@ -87,27 +90,22 @@ var db = require('./database.js'),
'topic_row_size': 'col-md-9', 'topic_row_size': 'col-md-9',
'category_id': category_id, 'category_id': category_id,
'active_users': [], 'active_users': [],
'topics': [], 'topics': results[0].topics,
'nextStart': results[0].nextStart,
'pageCount': pageCount, 'pageCount': pageCount,
'disableSocialButtons': meta.config.disableSocialButtons !== undefined ? parseInt(meta.config.disableSocialButtons, 10) !== 0 : false, 'disableSocialButtons': meta.config.disableSocialButtons !== undefined ? parseInt(meta.config.disableSocialButtons, 10) !== 0 : false,
'sidebars': sidebars 'sidebars': sidebars
}; };
function getTopics(next) {
topics.getTopicsByTids(tids, category_id, current_user, next);
}
function getModerators(next) { function getModerators(next) {
Categories.getModerators(category_id, next); Categories.getModerators(category_id, next);
} }
function getActiveUsers(next) { function getActiveUsers(next) {
user.getMultipleUserFields(active_users, ['uid', 'username', 'userslug', 'picture'], function(err, users) { user.getMultipleUserFields(active_users, ['uid', 'username', 'userslug', 'picture'], next);
next(err, users);
});
} }
if (tids.length === 0) { if (!category.topics.length) {
getModerators(function(err, moderators) { getModerators(function(err, moderators) {
category.moderator_block_class = moderators.length > 0 ? '' : 'none'; category.moderator_block_class = moderators.length > 0 ? '' : 'none';
category.moderators = moderators; category.moderators = moderators;
@ -116,11 +114,10 @@ var db = require('./database.js'),
callback(null, category); callback(null, category);
}); });
} else { } else {
async.parallel([getTopics, getModerators, getActiveUsers], function(err, results) { async.parallel([getModerators, getActiveUsers], function(err, results) {
category.topics = results[0]; category.moderator_block_class = results[0].length > 0 ? '' : 'none';
category.moderator_block_class = results[1].length > 0 ? '' : 'none'; category.moderators = results[0];
category.moderators = results[1]; category.active_users = results[1];
category.active_users = results[2];
category.show_sidebar = category.topics.length > 0 ? 'show' : 'hidden'; category.show_sidebar = category.topics.length > 0 ? 'show' : 'hidden';
callback(null, category); callback(null, category);
}); });
@ -131,13 +128,26 @@ var db = require('./database.js'),
}; };
Categories.getCategoryTopics = function(cid, start, stop, uid, callback) { Categories.getCategoryTopics = function(cid, start, stop, uid, callback) {
Categories.getTopicIds(cid, start, stop, function(err, tids) { async.waterfall([
function(next) {
Categories.getTopicIds(cid, start, stop, next);
},
function(tids, next) {
topics.getTopicsByTids(tids, cid, uid, next);
},
function(topics, next) {
db.sortedSetRevRank('categories:' + cid + ':tid', topics[topics.length - 1].tid, function(err, rank) {
if(err) { if(err) {
return callback(err); return next(err);
} }
topics.getTopicsByTids(tids, cid, uid, callback); return next(null, {
topics: topics,
nextStart: parseInt(rank, 10) + 1
});
}); });
}
], callback);
}; };
Categories.getTopicIds = function(cid, start, stop, callback) { Categories.getTopicIds = function(cid, start, stop, callback) {

@ -656,6 +656,23 @@
}); });
} }
module.sortedSetRevRank = function(key, value, callback) {
if(value !== null && value !== undefined) {
value = value.toString();
}
module.getSortedSetRange(key, 0, -1, function(err, result) {
if(err) {
return callback(err);
}
var rank = result.indexOf(value);
if(rank === -1) {
return callback(null, null);
}
callback(null, result.length - rank - 1);
});
}
module.sortedSetScore = function(key, value, callback) { module.sortedSetScore = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
value = value.toString(); value = value.toString();

@ -382,6 +382,10 @@
redisClient.zrank(key, value, callback); redisClient.zrank(key, value, callback);
} }
module.sortedSetRevRank = function(key, value, callback) {
redisClient.zrevrank(key, value, callback);
}
module.sortedSetScore = function(key, value, callback) { module.sortedSetScore = function(key, value, callback) {
redisClient.zscore(key, value, callback); redisClient.zscore(key, value, callback);
} }

@ -38,13 +38,17 @@
} }
} }
var description = topicData.posts.length ? topicData.posts[0].content : '';
var image_url = topicData.posts.length ? topicData.posts[0].picture : '';
var author = topicData.posts.length ? topicData.posts[0].username : '';
var feed = new rss({ var feed = new rss({
title: topicData.topic_name, title: topicData.topic_name,
description: topicData.posts[0].content, description: description,
feed_url: Feed.defaults.baseUrl + '/topics/' + tid + '.rss', feed_url: Feed.defaults.baseUrl + '/topics/' + tid + '.rss',
site_url: nconf.get('url') + '/topic/' + topicData.slug, site_url: nconf.get('url') + '/topic/' + topicData.slug,
image_url: topicData.posts[0].picture, image_url: image_url,
author: topicData.posts[0].username, author: author,
ttl: Feed.defaults.ttl ttl: Feed.defaults.ttl
}), }),
dateStamp; dateStamp;

@ -1,5 +1,5 @@
var user = require('./user'), var user = require('./user'),
bcrypt = require('bcrypt'), bcrypt = require('bcryptjs'),
db = require('./database'), db = require('./database'),
path = require('path'), path = require('path'),
winston = require('winston'), winston = require('winston'),

@ -56,13 +56,16 @@ var db = require('./database'),
var messages = []; var messages = [];
userData[0].uid = touid;
userData[1].uid = fromuid;
function getMessage(mid, next) { function getMessage(mid, next) {
db.getObject('message:' + mid, function(err, message) { db.getObject('message:' + mid, function(err, message) {
if (err) { if (err) {
return next(err); return next(err);
} }
Messaging.parse(message.content, message.fromuid, fromuid, userData[1], userData[0], function(result) { Messaging.parse(message.content, message.fromuid, fromuid, userData[1], userData[0], false, function(result) {
message.content = result; message.content = result;
messages.push(message); messages.push(message);
next(null); next(null);
@ -81,7 +84,7 @@ var db = require('./database'),
}); });
}; };
Messaging.parse = function (message, fromuid, myuid, toUserData, myUserData, callback) { Messaging.parse = function (message, fromuid, myuid, toUserData, myUserData, isNew, callback) {
plugins.fireHook('filter:post.parse', message, function(err, parsed) { plugins.fireHook('filter:post.parse', message, function(err, parsed) {
if (err) { if (err) {
return callback(message); return callback(message);
@ -97,7 +100,20 @@ var db = require('./database'),
username = '<span class="chat-user"> ' + toUserData.username + '</span>: '; username = '<span class="chat-user"> ' + toUserData.username + '</span>: ';
} }
callback(picture + username + parsed); var messageData = {
message: message,
parsed: parsed,
fromuid: fromuid,
myuid: myuid,
toUserData: toUserData,
myUserData: myUserData,
isNew: isNew,
parsedMessage: picture + username + parsed
};
plugins.fireHook('filter:messaging.parse', messageData, function(err, messageData) {
callback(messageData.parsedMessage);
});
}); });
}; };

@ -202,6 +202,10 @@ var fs = require('fs'),
} else return; } else return;
}; };
Plugins.hasListeners = function(hook) {
return (Plugins.loadedHooks[hook] && Plugins.loadedHooks[hook].length > 0);
};
Plugins.fireHook = function(hook, args, callback) { Plugins.fireHook = function(hook, args, callback) {
hookList = Plugins.loadedHooks[hook]; hookList = Plugins.loadedHooks[hook];

@ -11,10 +11,11 @@ var path = require('path'),
posts = require('../posts'), posts = require('../posts'),
categories = require('../categories'), categories = require('../categories'),
categoryTools = require('../categoryTools') categoryTools = require('../categoryTools')
meta = require('../meta'),
Plugins = require('../plugins'),
utils = require('../../public/src/utils'), utils = require('../../public/src/utils'),
translator = require('../../public/src/translator'), translator = require('../../public/src/translator'),
pkg = require('../../package.json'), pkg = require('../../package.json');
meta = require('../meta');
(function (Api) { (function (Api) {
@ -46,7 +47,6 @@ var path = require('path'),
config.topicsPerPage = meta.config.topicsPerPage || 20; config.topicsPerPage = meta.config.topicsPerPage || 20;
config.postsPerPage = meta.config.postsPerPage || 20; config.postsPerPage = meta.config.postsPerPage || 20;
config.maximumFileSize = meta.config.maximumFileSize; config.maximumFileSize = meta.config.maximumFileSize;
config.emailSetup = !!meta.config['email:from'];
config.defaultLang = meta.config.defaultLang || 'en'; config.defaultLang = meta.config.defaultLang || 'en';
res.json(200, config); res.json(200, config);
@ -116,7 +116,8 @@ var path = require('path'),
app.get('/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,
emailersPresent = Plugins.hasListeners('action:email.send');
if (num_strategies == 0) { if (num_strategies == 0) {
data = { data = {
@ -131,8 +132,8 @@ var path = require('path'),
} }
data.authentication = login_strategies; data.authentication = login_strategies;
data.token = res.locals.csrf_token; data.token = res.locals.csrf_token;
data.showResetLink = emailersPresent;
res.json(data); res.json(data);
}); });

@ -75,12 +75,12 @@ var DebugRoute = function(app) {
app.get('/test', function(req, res) { app.get('/test', function(req, res) {
/*topics.getTopicPosts2(2, 0, 10, 5, function(err, data) { var db = require('./../database');
res.json(data);
})*/ db.getSortedSetRevRange('topics:recent', 0 , -1, function(err, tids) {
topics.getTopicWithPosts(2, 1, 0, -1, true, function (err, topicData) { res.json(tids);
res.json(topicData);
}); });
}); });
}); });

@ -18,14 +18,10 @@ SocketCategories.loadMore = function(socket, data, callback) {
var topicsPerPage = parseInt(meta.config.topicsPerPage, 10) || 20; var topicsPerPage = parseInt(meta.config.topicsPerPage, 10) || 20;
var start = data.after, var start = parseInt(data.after, 10),
end = start + topicsPerPage - 1; end = start + topicsPerPage - 1;
categories.getCategoryTopics(data.cid, start, end, socket.uid, function(err, topics) { categories.getCategoryTopics(data.cid, start, end, socket.uid, callback);
callback(err, {
topics: topics
});
});
}; };
SocketCategories.getPageCount = function(socket, cid, callback) { SocketCategories.getPageCount = function(socket, cid, callback) {

@ -22,12 +22,11 @@ var SocketIO = require('socket.io'),
/* === */ /* === */
var users = {},
io; var io;
Sockets.userSockets = {}; Sockets.userSockets = {};
Sockets.rooms = {};
Sockets.init = function(server) { Sockets.init = function(server) {
@ -59,9 +58,9 @@ Sockets.init = function(server) {
sessionID = socket.handshake.signedCookies["express.sid"]; sessionID = socket.handshake.signedCookies["express.sid"];
db.sessionStore.get(sessionID, function(err, sessionData) { db.sessionStore.get(sessionID, function(err, sessionData) {
if (!err && sessionData && sessionData.passport && sessionData.passport.user) { if (!err && sessionData && sessionData.passport && sessionData.passport.user) {
uid = users[sessionID] = sessionData.passport.user; uid = sessionData.passport.user;
} else { } else {
uid = users[sessionID] = 0; uid = 0;
} }
socket.uid = parseInt(uid, 10); socket.uid = parseInt(uid, 10);
@ -110,8 +109,8 @@ Sockets.init = function(server) {
} }
if (Sockets.userSockets[uid] && Sockets.userSockets[uid].length === 0) { if (Sockets.userSockets[uid] && Sockets.userSockets[uid].length === 0) {
delete users[sessionID];
delete Sockets.userSockets[uid]; delete Sockets.userSockets[uid];
if (uid) { if (uid) {
db.sortedSetRemove('users:online', uid, function(err, data) { db.sortedSetRemove('users:online', uid, function(err, data) {
}); });
@ -126,17 +125,10 @@ Sockets.init = function(server) {
emitOnlineUserCount(); emitOnlineUserCount();
for (var roomName in Sockets.rooms) { for(var roomName in io.sockets.manager.roomClients[socket.id]) {
if (Sockets.rooms.hasOwnProperty(roomName)) { updateRoomBrowsingText(roomName.slice(1));
socket.leave(roomName);
if (Sockets.rooms[roomName][socket.id]) {
delete Sockets.rooms[roomName][socket.id];
} }
updateRoomBrowsingText(roomName);
}
}
}); });
socket.on('*', function(payload, callback) { socket.on('*', function(payload, callback) {
@ -222,9 +214,10 @@ function updateRoomBrowsingText(roomName) {
function getUidsInRoom(room) { function getUidsInRoom(room) {
var uids = []; var uids = [];
for (var socketId in room) { var clients = io.sockets.clients(roomName);
if (uids.indexOf(room[socketId]) === -1) { for(var i=0; i<clients.length; ++i) {
uids.push(room[socketId]); if (uids.indexOf(clients[i].uid) === -1 && clients[i].uid !== 0) {
uids.push(clients[i].uid);
} }
} }
return uids; return uids;
@ -235,15 +228,14 @@ function updateRoomBrowsingText(roomName) {
var anonCount = 0; var anonCount = 0;
for (var i = 0; i < clients.length; ++i) { for (var i = 0; i < clients.length; ++i) {
var hs = clients[i].handshake; if(clients[i].uid === 0) {
if (hs && clients[i].state && clients[i].state.user.uid === 0) {
++anonCount; ++anonCount;
} }
} }
return anonCount; return anonCount;
} }
var uids = getUidsInRoom(Sockets.rooms[roomName]), var uids = getUidsInRoom(roomName),
anonymousCount = getAnonymousCount(roomName); anonymousCount = getAnonymousCount(roomName);
user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) { user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) {

@ -84,18 +84,6 @@ SocketMeta.rooms.enter = function(socket, data) {
} }
socket.join(data.enter); socket.join(data.enter);
server.rooms[data.enter] = server.rooms[data.enter] || {};
if (socket.uid) {
server.rooms[data.enter][socket.id] = socket.uid;
if (data.leave && server.rooms[data.leave] && server.rooms[data.leave][socket.id] && data.enter !== data.leave) {
delete server.rooms[data.leave][socket.id];
if(!Object.keys(server.rooms[data.leave]).length) {
delete server.rooms[data.leave];
}
}
}
if (data.leave) { if (data.leave) {
module.parent.exports.updateRoomBrowsingText(data.leave); module.parent.exports.updateRoomBrowsingText(data.leave);
@ -104,12 +92,12 @@ SocketMeta.rooms.enter = function(socket, data) {
module.parent.exports.updateRoomBrowsingText(data.enter); module.parent.exports.updateRoomBrowsingText(data.enter);
if (data.enter != 'admin') { if (data.enter != 'admin') {
server.in('admin').emit('event:meta.rooms.update', null, server.rooms); server.in('admin').emit('event:meta.rooms.update', null, server.server.sockets.manager.rooms);
} }
}; };
SocketMeta.rooms.getAll = function(socket, data, callback) { SocketMeta.rooms.getAll = function(socket, data, callback) {
callback(null, server.rooms); callback(null, server.server.sockets.manager.rooms);
}; };
/* Exports */ /* Exports */

@ -109,7 +109,11 @@ SocketModules.chats.send = function(socket, data) {
}); });
}); });
} }
Messaging.parse(msg, socket.uid, socket.uid, usersData[1], usersData[0], function(parsed) {
usersData[0].uid = socket.uid;
usersData[1].uid = touid;
Messaging.parse(msg, socket.uid, socket.uid, usersData[1], usersData[0], true, function(parsed) {
Messaging.addMessage(socket.uid, touid, msg, function(err, message) { Messaging.addMessage(socket.uid, touid, msg, function(err, message) {
var numSockets = 0, var numSockets = 0,
x; x;

@ -93,7 +93,17 @@ SocketPosts.uploadFile = function(socket, data, callback) {
}; };
SocketPosts.getRawPost = function(socket, pid, callback) { SocketPosts.getRawPost = function(socket, pid, callback) {
posts.getPostField(pid, 'content', callback); posts.getPostFields(pid, ['content', 'deleted'], function(err, data) {
if(err) {
return callback(err);
}
if(data.deleted === '1') {
return callback(new Error('This post no longer exists'));
}
callback(null, data.content);
});
}; };
SocketPosts.edit = function(socket, data, callback) { SocketPosts.edit = function(socket, data, callback) {

@ -1,6 +1,9 @@
var topics = require('../topics'), var topics = require('../topics'),
threadTools = require('../threadTools'), threadTools = require('../threadTools'),
index = require('./index'), index = require('./index'),
async = require('async'),
SocketTopics = {}; SocketTopics = {};
SocketTopics.post = function(socket, data, callback) { SocketTopics.post = function(socket, data, callback) {
@ -233,15 +236,18 @@ SocketTopics.loadMore = function(socket, data, callback) {
var postsPerPage = parseInt(meta.config.postsPerPage, 10); var postsPerPage = parseInt(meta.config.postsPerPage, 10);
postsPerPage = postsPerPage ? postsPerPage : 20; postsPerPage = postsPerPage ? postsPerPage : 20;
var start = data.after, var start = parseInt(data.after, 10),
end = start + postsPerPage - 1; end = start + postsPerPage - 1;
topics.getTopicPosts(data.tid, start, end, socket.uid, function(err, posts) { async.parallel({
if(err) { posts: function(next) {
return callback(err); topics.getTopicPosts(data.tid, start, end, socket.uid, next);
},
privileges: function(next) {
threadTools.privileges(data.tid, socket.uid, next);
} }
}, function(err, results) {
callback(err, {posts: posts}); callback(err, results);
}); });
}; };
@ -250,14 +256,14 @@ SocketTopics.loadMoreRecentTopics = function(socket, data, callback) {
return callback(new Error('invalid data')); return callback(new Error('invalid data'));
} }
var start = data.after, var start = parseInt(data.after, 10),
end = start + 9; end = start + 9;
topics.getLatestTopics(socket.uid, start, end, data.term, callback); topics.getLatestTopics(socket.uid, start, end, data.term, callback);
}; };
SocketTopics.loadMoreUnreadTopics = function(socket, data, callback) { SocketTopics.loadMoreUnreadTopics = function(socket, data, callback) {
var start = data.after, var start = parseInt(data.after, 10),
end = start + 9; end = start + 9;
topics.getUnreadTopics(socket.uid, start, end, callback); topics.getUnreadTopics(socket.uid, start, end, callback);

@ -187,9 +187,20 @@ var winston = require('winston'),
ThreadTools.move = function(tid, cid, callback) { ThreadTools.move = function(tid, cid, callback) {
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) { topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
if(err) {
return callback(err);
}
var oldCid = topicData.cid; var oldCid = topicData.cid;
if(!oldCid) {
return callback(new Error('invalid-topic'));
}
db.sortedSetRemove('categories:' + oldCid + ':tid', tid, function(err, result) { db.sortedSetRemove('categories:' + oldCid + ':tid', tid, function(err, result) {
if(err) {
return callback(err);
}
db.sortedSetAdd('categories:' + cid + ':tid', topicData.lastposttime, tid, function(err, result) { db.sortedSetAdd('categories:' + cid + ':tid', topicData.lastposttime, tid, function(err, result) {
if(err) { if(err) {

@ -426,8 +426,9 @@ var async = require('async'),
}; };
var since = terms['day']; var since = terms['day'];
if(terms[term]) if(terms[term]) {
since = terms[term]; since = terms[term];
}
var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1]; var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
db.getSortedSetRevRangeByScore(args, function(err, tids) { db.getSortedSetRevRangeByScore(args, function(err, tids) {
@ -442,23 +443,29 @@ var async = require('async'),
if (!tids || !tids.length) { if (!tids || !tids.length) {
latestTopics.no_topics_message = 'show'; latestTopics.no_topics_message = 'show';
callback(err, latestTopics); return callback(null, latestTopics);
return;
} }
// Filter out topics that belong to categories that this user cannot access
async.filter(tids, function(tid, next) { async.filter(tids, function(tid, next) {
threadTools.privileges(tid, current_user, function(err, privileges) { threadTools.privileges(tid, current_user, function(err, privileges) {
if (!err && privileges.read) { next(!err && privileges.read);
next(true);
} else {
next(false);
}
}); });
}, function(tids) { }, function(tids) {
Topics.getTopicsByTids(tids, 0, current_user, function(err, topicData) { Topics.getTopicsByTids(tids, 0, current_user, function(err, topicData) {
if(err) {
return callback(err);
}
if(!topicData || !topicData.length) {
latestTopics.no_topics_message = 'show';
return callback(null, latestTopics);
}
db.sortedSetRevRank('topics:recent', topicData[topicData.length - 1].tid, function(err, rank) {
latestTopics.nextStart = parseInt(rank,10) + 1;
latestTopics.topics = topicData; latestTopics.topics = topicData;
callback(err, latestTopics); callback(null, latestTopics);
});
}); });
}); });
}); });
@ -538,7 +545,7 @@ var async = require('async'),
return parseInt(read[index], 10) === 0; return parseInt(read[index], 10) === 0;
}); });
// Filter out topics that belong to categories that this user cannot access
async.filter(newtids, function(tid, next) { async.filter(newtids, function(tid, next) {
threadTools.privileges(tid, uid, function(err, privileges) { threadTools.privileges(tid, uid, function(err, privileges) {
if (!err && privileges.read) { if (!err && privileges.read) {
@ -588,8 +595,14 @@ var async = require('async'),
return callback(err); return callback(err);
} }
db.sortedSetRevRank('topics:recent', topicData[topicData.length - 1].tid, function(err, rank) {
if(err) {
return callback(err);
}
unreadTopics.topics = topicData; unreadTopics.topics = topicData;
unreadTopics.nextStart = stop + 1; unreadTopics.nextStart = parseInt(rank, 10) + 1;
if (!topicData || topicData.length === 0) { if (!topicData || topicData.length === 0) {
unreadTopics.no_topics_message = 'show'; unreadTopics.no_topics_message = 'show';
} }
@ -599,6 +612,7 @@ var async = require('async'),
callback(null, unreadTopics); callback(null, unreadTopics);
}); });
});
} }
Topics.getUnreadTids(uid, start, stop, function(err, unreadTids) { Topics.getUnreadTids(uid, start, stop, function(err, unreadTids) {
@ -700,6 +714,7 @@ var async = require('async'),
function isTopicVisible(topicData, topicInfo) { function isTopicVisible(topicData, topicInfo) {
var deleted = parseInt(topicData.deleted, 10) !== 0; var deleted = parseInt(topicData.deleted, 10) !== 0;
return !deleted || (deleted && topicInfo.privileges.view_deleted) || topicData.uid === current_user; return !deleted || (deleted && topicInfo.privileges.view_deleted) || topicData.uid === current_user;
} }

@ -1,4 +1,4 @@
var bcrypt = require('bcrypt'), var bcrypt = require('bcryptjs'),
async = require('async'), async = require('async'),
nconf = require('nconf'), nconf = require('nconf'),
winston = require('winston'), winston = require('winston'),
@ -859,6 +859,10 @@ var bcrypt = require('bcrypt'),
User.email = { User.email = {
verify: function(uid, email) { verify: function(uid, email) {
if (!plugins.hasListeners('action:email.send')) {
return;
}
var confirm_code = utils.generateUUID(), var confirm_code = utils.generateUUID(),
confirm_link = nconf.get('url') + '/confirm/' + confirm_code; confirm_link = nconf.get('url') + '/confirm/' + confirm_code;
@ -895,11 +899,12 @@ var bcrypt = require('bcrypt'),
confirm: function(code, callback) { confirm: function(code, callback) {
db.getObject('confirm:' + code, function(err, confirmObj) { db.getObject('confirm:' + code, function(err, confirmObj) {
if (err) { if (err) {
callback({ return callback({
status:'error' status:'error'
}); });
} else { }
if (confirmObj.uid && confirmObj.email) {
if (confirmObj && confirmObj.uid && confirmObj.email) {
db.setObjectField('email:confirmed', confirmObj.email, '1', function() { db.setObjectField('email:confirmed', confirmObj.email, '1', function() {
callback({ callback({
status: 'ok' status: 'ok'
@ -910,7 +915,6 @@ var bcrypt = require('bcrypt'),
status: 'not_ok' status: 'not_ok'
}); });
} }
}
}); });
} }
}; };

@ -43,9 +43,9 @@ describe('Categories', function() {
describe('.getCategoryTopics', function() { describe('.getCategoryTopics', function() {
it('should return a list of topics', function(done) { it('should return a list of topics', function(done) {
Categories.getCategoryTopics(categoryObj.cid, 0, 10, 0, function(err, topics) { Categories.getCategoryTopics(categoryObj.cid, 0, 10, 0, function(err, result) {
assert(Array.isArray(topics)); assert(Array.isArray(result.topics));
assert(topics.every(function(topic) { assert(result.topics.every(function(topic) {
return topic instanceof Object; return topic instanceof Object;
})); }));

Loading…
Cancel
Save