Merge branch 'master' into admin-group-fix

v1.18.x
Josh Rickers 11 years ago
commit 55b0270b34

@ -454,6 +454,7 @@ var socket,
$('#search-fields').hide();
$('#search-button').show();
});
return false;
});
var loggedInMenu = $('#logged-in-menu'),
@ -501,7 +502,7 @@ var socket,
}
$('#main-nav a,#user-control-list a,#logged-out-menu li a').off('click').on('click', function() {
$('#main-nav a,#user-control-list a,#logged-out-menu li a,#logged-in-menu .visible-xs').off('click').on('click', function() {
if($('.navbar .navbar-collapse').hasClass('in')) {
$('.navbar-header button').click();
}

@ -68,10 +68,6 @@ define(['forum/accountheader'], function(header) {
app.openChat(username, theirid);
});
$('.user-recent-posts .topic-row').on('click', function() {
ajaxify.go($(this).attr('topic-url'));
});
socket.on('user.isOnline', Account.handleUserOnline);
socket.emit('user.isOnline', theirid, Account.handleUserOnline);

@ -11,12 +11,6 @@ define(function() {
themeEvent = function(e) {
if (e.target.hasAttribute('data-action')) {
switch (e.target.getAttribute('data-action')) {
case 'preview':
var cssSrc = $(e.target).parents('li').attr('data-css'),
cssEl = document.getElementById('base-theme');
cssEl.href = cssSrc;
break;
case 'use':
var parentEl = $(e.target).parents('li'),
themeType = parentEl.attr('data-type'),
@ -40,6 +34,7 @@ define(function() {
}
}
};
bootstrapThemeContainer.addEventListener('click', themeEvent);
installedThemeContainer.addEventListener('click', themeEvent);
@ -81,7 +76,6 @@ define(function() {
'<div>' +
'<div class="pull-right">' +
'<button class="btn btn-primary" data-action="use">Use</button> ' +
'<button class="btn btn-default" data-action="preview">Preview</button>' +
'</div>' +
'<h4>' + themes[x].name + '</h4>' +
'<p>' +
@ -120,7 +114,6 @@ define(function() {
'<div>' +
'<div class="pull-right">' +
'<button class="btn btn-primary" data-action="use">Use</button> ' +
'<button class="btn btn-default" data-action="preview">Preview</button>' +
'</div>' +
'<h4>' + theme.name + '</h4>' +
'<p>' + theme.description + '</p>' +

@ -153,13 +153,14 @@ define(['composer'], function(composer) {
var replies = '';
for (var i = 0, numPosts = posts.length; i < numPosts; ++i) {
replies += '<li data-pid="'+ posts[i].pid +'">' +
replies += '<li data-pid="'+ posts[i].pid +'" class="clearfix">' +
'<a href="' + RELATIVE_PATH + '/user/' + posts[i].userslug + '"><img title="' + posts[i].username + '" class="img-rounded user-img" src="' + posts[i].picture + '"/></a>' +
'<a href="' + RELATIVE_PATH + '/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">' +
'<strong><span>'+ posts[i].username + '</span></strong>' +
'<p>' + posts[i].content + '</p>' +
'</a>' +
'<span class="timeago pull-right" title="' + posts[i].relativeTime + '"></span>' +
'<strong><span>'+ posts[i].username + '</span></strong>' +
'<p>' + posts[i].content + '</p>' +
'<span class="pull-right">'+
'<a href="' + RELATIVE_PATH + '/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">posted</a> '+
'<span class="timeago" title="' + posts[i].relativeTime + '"></span>' +
'</span>'+
'</li>';
}

@ -8,11 +8,17 @@ define(['notifications', 'chat'], function(Notifications, Chat) {
Chat.prepareDOM();
translator.prepareDOM();
function updateUnreadCount(err, count) {
function updateUnreadCount(err, tids) {
var count = 0;
if(tids && tids.length) {
count = tids.length;
}
$('#unread-count').toggleClass('unread-count', count > 0);
$('#unread-count').attr('data-content', count > 20 ? '20+' : count);
}
socket.on('event:unread.updateCount', updateUnreadCount);
socket.emit('user.getUnreadCount', updateUnreadCount);
});

@ -522,8 +522,10 @@ define(['composer'], function(composer) {
$('#post-container').on('click', '.chat', function(e) {
var username = $(this).parents('li.row').attr('data-username');
var touid = $(this).parents('li.row').attr('data-uid');
var post = $(this).parents('li.post-row'),
username = post.attr('data-username'),
touid = post.attr('data-uid');
app.openChat(username, touid);
$(this).parents('.btn-group').find('.dropdown-toggle').click();
return false;
@ -537,7 +539,6 @@ define(['composer'], function(composer) {
'posts.favourite'
]);
socket.on('get_users_in_room', function(data) {
if(data && data.room.indexOf('topic') !== -1) {
@ -641,6 +642,8 @@ define(['composer'], function(composer) {
}
}
socket.emit('topics.markAsRead', {tid: tid, uid: app.uid});
createNewPosts(data);
});

@ -264,6 +264,7 @@ define(['taskbar'], function(taskbar) {
newHeight = $(window).height() - $('#header-menu').height() - 20;
}
postContainer.css('height', newHeight);
$('body').css({'margin-bottom': newHeight});
resizeSavePosition(newHeight);
}
e.preventDefault();
@ -353,6 +354,7 @@ define(['taskbar'], function(taskbar) {
postContainer.css('visibility', 'visible')
.css('z-index', 1);
$('body').css({'margin-bottom': postContainer.css('height')});
composer.focusElements(post_uuid);
}
@ -436,6 +438,7 @@ define(['taskbar'], function(taskbar) {
delete composer.posts[post_uuid];
composer.active = undefined;
taskbar.discard('composer', post_uuid);
$('body').css({'margin-bottom': 0});
}
}

@ -21,7 +21,7 @@ define(function() {
if (!err && (data.read.length + data.unread.length) > 0) {
for (x = 0; x < numUnread; x++) {
notifEl.setAttribute('data-nid', data.unread[x].nid);
notifEl.className = 'unread';
notifEl.className = data.unread[x].readClass;
notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>';
notifFrag.appendChild(notifEl.cloneNode(true));
}

@ -99,17 +99,27 @@
return difference + (min ? 'y' : ' year') + (difference !== 1 && !min ? 's' : '');
},
invalidUnicodeChars : XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'),
invalidLatinChars : /[^\w\s\d\-_]/g,
trimRegex : /^\s+|\s+$/g,
collapseWhitespace : /\s+/g,
collapseDash : /-+/g,
trimTrailingDash : /-$/g,
isLatin : /^[\w]+$/,
//http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
slugify: function(str) {
var invalidChars = XRegExp('[^\\p{L}\\s\\d\\-_]', 'g');
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.replace(utils.trimRegex, '');
str = str.toLowerCase();
str = XRegExp.replace(str, invalidChars, '-');
str = str.replace(/\s+/g, '-') // collapse whitespace and replace by -
str = str.replace(/-+/g, '-'); // collapse dashes
str = str.replace(/-$/g, '');
if(utils.isLatin.test(str)) {
str = str.replace(utils.invalidLatinChars, '-');
} else {
str = XRegExp.replace(str, utils.invalidUnicodeChars, '-');
}
str = str.replace(utils.collapseWhitespace, '-')
str = str.replace(utils.collapseDash, '-');
str = str.replace(utils.trimTrailingDash, '');
return str;
},
@ -202,7 +212,7 @@
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
return this.replace(utils.trimRegex, '');
};
}

@ -108,12 +108,30 @@
</div>
<div class="col-md-6 user-recent-posts">
<!-- BEGIN posts -->
<div class="topic-row img-thumbnail clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
<span>{posts.content}</span>
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
<div class="topic-row panel panel-default clearfix">
<div class="panel-heading">
<h3 class="panel-title">Recent Posts</h3>
</div>
<div class="panel-body">
<!-- BEGIN posts -->
<div class="clearfix">
<p>{posts.content}</p>
<small>
<span class="pull-right">
<a href="../../topic/{posts.tid}/#{posts.pid}">posted</a>
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>
<hr/>
<!-- END posts -->
</div>
</div>
<!-- END posts -->
</div>
</div>

@ -30,59 +30,60 @@
<meta itemprop="itemListOrder" content="descending">
<!-- BEGIN topics -->
<li class="category-item {topics.deleted-class}" itemprop="itemListElement">
<div class="row">
<div class="col-md-12 topic-row">
<div>
<a href="../../topic/{topics.slug}" itemprop="url">
<h3>
<meta itemprop="name" content="{topics.title}">
<span class="topic-title">
<strong><i class="fa {topics.pin-icon}"></i> <i class="fa {topics.lock-icon}"></i></strong>
{topics.title}
</span>
</h3>
<div class="col-md-12 col-xs-12 panel panel-default topic-row">
<a href="../../user/{topics.userslug}" class="pull-left">
<img class="img-rounded user-img" src="{topics.picture}" title="{topics.username}" />
</a>
<h3>
<a href="../../topic/{topics.slug}" itemprop="url">
<meta itemprop="name" content="{topics.title}">
<span class="topic-title">
<strong><i class="fa {topics.pin-icon}"></i> <i class="fa {topics.lock-icon}"></i></strong>
{topics.title}
</span>
</a>
</h3>
<small>
<span class="topic-stats">
[[category:posts]]
<strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span>
|
<span class="topic-stats">
[[category:views]]
<strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span>
|
<span>
[[category:posted]] <span class="timeago" title="{topics.relativeTime}"></span>
</span>
<span class="pull-right">
<!-- IF topics.unreplied -->
[[category:no_replies]]
<!-- ELSE -->
<a href="../../user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a>
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
[[category:replied]]
</a>
<small>
<span class="topic-stats">
[[category:posts]]
<strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span>
|
<span class="topic-stats">
[[category:views]]
<strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span>
|
<span>
<a href="../../user/{topics.userslug}">
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
</a>
[[category:posted]] <span class="timeago" title="{topics.relativeTime}"></span>
</span>
<span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied -->
</span>
</small>
<span class="pull-right hidden-xs">
<!-- IF topics.unreplied -->
[[category:no_replies]]
<!-- ELSE -->
<a href="../../user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a>
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
[[category:replied]]
</a>
<span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied -->
</span>
</small>
</div>
</div>
</div>
</li>
<!-- END topics -->
</ul>
</div>
<div class="col-md-3 {show_sidebar} category-sidebar">
<div class="col-md-3 col-xs-12 {show_sidebar} category-sidebar">
<div class="panel panel-default">
<div class="panel-heading">[[category:sidebar.recent_replies]]</div>
<div class="panel-body recent-replies">

@ -13,28 +13,31 @@
<div class="row">
<div class="col-md-12 user-favourite-posts">
<!-- BEGIN posts -->
<div class="topic-row img-thumbnail clearfix">
<a href="../../user/{posts.userslug}">
<img title="{posts.username}" class="img-rounded user-img" src="{posts.picture}">
</a>
<div class="topic-row panel panel-default clearfix">
<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>
<p>{posts.content}</p>
<a href="../../user/{posts.userslug}">
<strong><span>{posts.username}</span></strong>
</a>
<p>{posts.content}</p>
<div>
<span class="pull-right">
<a href="../../topic/{posts.tid}/#{posts.pid}">posted</a>
in
<a href="../../category/{posts.categorySlug}">
<i class="fa {posts.categoryIcon}"></i> {posts.categoryName}
</a>
<span class="timeago" title="{posts.relativeTime}"></span>
</span>
<div>
<small>
<span class="pull-right">
<a href="../../topic/{posts.tid}/#{posts.pid}">posted</a>
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>
<br/>
<!-- END posts -->
</div>
</div>

@ -68,7 +68,7 @@
</li>
<li class="visible-xs">
<a id="mobile-search-button" href="{relative_path}/search"><i class="fa fa-search" title="[[global:header.search]]"></i><span class="visible-xs-inline"> [[global:header.search]]</span></a>
<a id="mobile-search-button" href="{relative_path}/search"><i class="fa fa-search" title="[[global:header.search]]"></i> [[global:header.search]]</a>
</li>
<!-- BEGIN navigation -->
@ -99,6 +99,9 @@
</li>
</ul>
</li>
<li class="visible-xs">
<a href="{relative_path}/notifications"><i class="fa fa-exclamation-triangle" title="[[notifications:title]]"></i> [[notifications:title]]</a>
</li>
<li class="chats dropdown text-center hidden-xs">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="chat_dropdown"><i class="fa fa-comment"></i></a>

@ -5,18 +5,27 @@
<div class="row home" itemscope itemtype="http://www.schema.org/ItemList">
<!-- BEGIN categories -->
<div class="{categories.class}">
<meta itemprop="name" content="{categories.name}">
<h4>
<!-- IF !categories.link -->
<span class="badge {categories.badgeclass}">{categories.topic_count} </span>
<!-- ENDIF !categories.link -->
<!-- IF categories.link -->
<a href="{categories.link}" itemprop="url" target="_blank">
<!-- ELSE -->
<a href="{relative_path}/category/{categories.slug}" itemprop="url">
<!-- ENDIF categories.link -->
{categories.name}
</a>
</h4>
<!-- IF categories.link -->
<a href="{categories.link}" itemprop="url" target="_blank">
<!-- ELSE -->
<a href="{relative_path}/category/{categories.slug}" itemprop="url">
<!-- ENDIF categories.link -->
<meta itemprop="name" content="{categories.name}">
<h4>
<!-- IF !categories.link -->
<span class="badge {categories.badgeclass}">{categories.topic_count} </span>
<!-- ENDIF !categories.link -->
{categories.name}
</h4>
<div class="icon" style="background: {categories.background}; color: {categories.color};">
<div id="category-{categories.cid}" class="category-slider-{categories.post_count}">
<div class="category-box"><i class="fa {categories.icon} fa-4x"></i></div>

@ -24,50 +24,57 @@
<ul id="topics-container">
<!-- BEGIN topics -->
<li class="category-item {topics.deleted-class}">
<div class="row">
<div class="col-md-12 col-xs-12 topic-row img-thumbnail">
<div class="col-md-12 col-xs-12 panel panel-default topic-row">
<a href="{relative_path}/user/{topics.userslug}" class="pull-left">
<img class="img-rounded user-img" src="{topics.picture}" title="{topics.username}" />
</a>
<h3>
<a href="{relative_path}/topic/{topics.slug}">
<h3><span class="topic-title"><strong><i class="fa {topics.pin-icon}"></i> <i class="fa {topics.lock-icon}"></i></strong> {topics.title}</span></h3>
</a>
<small>
<span class="topic-stats">
posts
<strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span>
|
<span class="topic-stats">
views
<strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span>
|
<span>
<a href="{relative_path}/user/{topics.userslug}">
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
</a>
posted in
<a href="{relative_path}/category/{topics.categorySlug}">
<i class="fa {topics.categoryIcon}"></i> {topics.categoryName}
</a>
<span class="timeago" title="{topics.relativeTime}"></span>
</span>
<span class="topic-title">
<strong>
<i class="fa {topics.pin-icon}"></i>
<i class="fa {topics.lock-icon}"></i>
</strong>
{topics.title}
</span>
</a>
</h3>
<span class="pull-right hidden-xs">
<!-- IF topics.unreplied -->
No one has replied
<!-- ELSE -->
<a href="{relative_path}/user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a>
<a href="{relative_path}/topic/{topics.slug}#{topics.teaser_pid}">
replied
</a>
<span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied -->
<small>
<span class="topic-stats">
posts
<strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span>
|
<span class="topic-stats">
views
<strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span>
|
<span>
posted in
<a href="{relative_path}/category/{topics.categorySlug}">
<i class="fa {topics.categoryIcon}"></i> {topics.categoryName}
</a>
<span class="timeago" title="{topics.relativeTime}"></span>
</span>
</small>
</div>
</span>
<span class="pull-right">
<!-- IF topics.unreplied -->
No one has replied
<!-- ELSE -->
<a href="{relative_path}/user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a>
<a href="{relative_path}/topic/{topics.slug}#{topics.teaser_pid}">
replied
</a>
<span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied -->
</span>
</small>
</div>
</li>
<!-- END topics -->

@ -23,7 +23,7 @@
<ul id="post-container" class="posts" data-tid="{topic_id}">
<!-- BEGIN posts -->
<li class="row post-row infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-userslug="{posts.userslug}" data-index="{posts.index}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
<li class="post-row infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-userslug="{posts.userslug}" data-index="{posts.index}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
<meta itemprop="datePublished" content="{posts.relativeTime}">
@ -36,107 +36,106 @@
<span class="label label-danger">[[topic:banned]]</span>
<!-- ENDIF posts.user_banned -->
</a>
</div>
<div class="col-md-11">
<div class="post-block">
<a class="main-post avatar" href="{relative_path}/user/{posts.userslug}">
<img itemprop="image" src="{posts.picture}" align="left" class="img-thumbnail" width=150 height=150 />
</a>
<h3 class="main-post">
<p id="topic_title_{posts.pid}" class="topic-title" itemprop="name">{topic_name}</p>
</h3>
<div class="col-md-11 panel panel-default post-block">
<div class="topic-buttons">
<a class="main-post avatar" href="{relative_path}/user/{posts.userslug}">
<img itemprop="image" src="{posts.picture}" align="left" class="img-thumbnail" width=150 height=150 />
</a>
<h3 class="main-post">
<p id="topic_title_{posts.pid}" class="topic-title" itemprop="name">{topic_name}</p>
</h3>
<div class="btn-group">
<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" type="button" title="[[topic:posted_by]] {posts.username}">
<img class="visible-xs visible-sm pull-left" src="{posts.picture}" width=18 height=18 />
<span class="username-field" href="{relative_path}/user/{posts.userslug}" itemprop="author">&nbsp; {posts.username}&nbsp;</span>
<span class="caret"></span>
</button>
<div class="topic-buttons">
<ul class="dropdown-menu">
<li><a href="{relative_path}/user/{posts.userslug}"><i class="fa fa-user"></i> [[topic:profile]]</a></li>
<li><a href="#" class="chat"><i class="fa fa-comment"></i> [[topic:chat]]</a></li>
</ul>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" type="button" title="[[topic:posted_by]] {posts.username}">
<span class="visible-xs visible-sm pull-left"><img class="" src="{posts.picture}" width=18 height=18 />&nbsp;</span>
<span class="username-field" href="{relative_path}/user/{posts.userslug}" itemprop="author">{posts.username}&nbsp;</span>
<span class="caret"></span>
</button>
<div class="btn-group">
<!-- IF @first -->
<button class="btn btn-sm btn-default follow" type="button" title="Be notified of new replies in this topic"><i class="fa fa-eye"></i></button>
<!-- ENDIF @first -->
<button data-favourited="{posts.favourited}" class="favourite btn btn-sm btn-default <!-- IF posts.favourited --> btn-warning <!-- ENDIF posts.favourited -->" type="button">
<span class="favourite-text">[[topic:favourite]]</span>
<span class="post_rep_{posts.pid}">{posts.reputation} </span>
<!-- IF posts.favourited -->
<i class="fa fa-star"></i>
<!-- ELSE -->
<i class="fa fa-star-o"></i>
<!-- ENDIF posts.favourited -->
</button>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-default quote" type="button" title="[[topic:quote]]"><i class="fa fa-quote-left"></i></button>
<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="fa fa-reply"></i></button>
</div>
<ul class="dropdown-menu">
<li><a href="{relative_path}/user/{posts.userslug}"><i class="fa fa-user"></i> [[topic:profile]]</a></li>
<li><a href="#" class="chat"><i class="fa fa-comment"></i> [[topic:chat]]</a></li>
</ul>
</div>
<div class="pull-right">
<div class="btn-group post-tools">
<div class="dropdown share-dropdown">
<button title="[[topic:share]]"class="btn btn-sm btn-default share" data-toggle="dropdown" href="#"><i class="fa fa-share-square-o"></i></button>
<ul class="dropdown-menu text-center pull-right" role="menu" aria-labelledby="dLabel">
<!-- IF !disableSocialButtons -->
<li class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="fa fa-facebook"></i></li>
<li class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="fa fa-twitter"></i></li>
<li class="btn btn-sm btn-default google-share" type="button" title=""><i class="fa fa-google-plus"></i></li>
<!-- ENDIF !disableSocialButtons -->
<li>
<input type="text" id="post_{posts.pid}_link" value="" class="form-control pull-right post-link" style=""></input>
<li>
</ul>
</div>
<div class="btn-group">
<!-- IF @first -->
<button class="btn btn-sm btn-default follow" type="button" title="Be notified of new replies in this topic"><i class="fa fa-eye"></i></button>
<!-- ENDIF @first -->
<button data-favourited="{posts.favourited}" class="favourite btn btn-sm btn-default <!-- IF posts.favourited --> btn-warning <!-- ENDIF posts.favourited -->" type="button">
<span class="favourite-text">[[topic:favourite]]</span>
<span class="post_rep_{posts.pid}">{posts.reputation} </span>
<!-- IF posts.favourited -->
<i class="fa fa-star"></i>
<!-- ELSE -->
<i class="fa fa-star-o"></i>
<!-- ENDIF posts.favourited -->
</button>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-default quote" type="button" title="[[topic:quote]]"><i class="fa fa-quote-left"></i></button>
<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="fa fa-reply"></i></button>
</div>
<div class="pull-right">
<div class="btn-group post-tools">
<div class="dropdown share-dropdown">
<button title="[[topic:share]]"class="btn btn-sm btn-default share" data-toggle="dropdown" href="#"><i class="fa fa-share-square-o"></i></button>
<ul class="dropdown-menu text-center pull-right" role="menu" aria-labelledby="dLabel">
<!-- IF !disableSocialButtons -->
<li class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="fa fa-facebook"></i></li>
<li class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="fa fa-twitter"></i></li>
<li class="btn btn-sm btn-default google-share" type="button" title=""><i class="fa fa-google-plus"></i></li>
<!-- ENDIF !disableSocialButtons -->
<li>
<input type="text" id="post_{posts.pid}_link" value="" class="form-control pull-right post-link" style=""></input>
<li>
</ul>
</div>
</div>
<!-- IF posts.display_moderator_tools -->
<div class="btn-group post-tools">
<div class="dropdown">
<button title="[[topic:tools]]" class="btn btn-sm btn-default" data-toggle="dropdown" href="#"><i class="fa fa-gear"></i></button>
<ul class="dropdown-menu text-center pull-right" role="menu" aria-labelledby="dLabel">
<button class="btn btn-sm btn-default edit" type="button" title="[[topic:edit]]"><i class="fa fa-pencil"></i></button>
<button class="btn btn-sm btn-default delete" type="button" title="[[topic:delete]]"><i class="fa fa-trash-o"></i></button>
<!-- IF posts.display_moderator_tools -->
<div class="btn-group post-tools">
<div class="dropdown">
<button title="[[topic:tools]]" class="btn btn-sm btn-default" data-toggle="dropdown" href="#"><i class="fa fa-gear"></i></button>
<ul class="dropdown-menu text-center pull-right" role="menu" aria-labelledby="dLabel">
<button class="btn btn-sm btn-default edit" type="button" title="[[topic:edit]]"><i class="fa fa-pencil"></i></button>
<button class="btn btn-sm btn-default delete" type="button" title="[[topic:delete]]"><i class="fa fa-trash-o"></i></button>
<button class="btn btn-sm btn-default move {posts.display_move_tools}" type="button" title="[[topic:move]]"><i class="fa fa-arrows"></i></button>
<button class="btn btn-sm btn-default move {posts.display_move_tools}" type="button" title="[[topic:move]]"><i class="fa fa-arrows"></i></button>
</ul>
</div>
</ul>
</div>
<!-- ENDIF posts.display_moderator_tools -->
</div>
<!-- ENDIF posts.display_moderator_tools -->
</div>
</div>
<div id="content_{posts.pid}" class="post-content" itemprop="text">{posts.content}</div>
<!-- IF posts.signature -->
<div class="post-signature">{posts.signature}</div>
<!-- ENDIF posts.signature -->
<div id="content_{posts.pid}" class="post-content" itemprop="text">{posts.content}</div>
<!-- IF posts.signature -->
<div class="post-signature">{posts.signature}</div>
<!-- ENDIF posts.signature -->
<div class="post-info">
<span class="pull-left">
[[topic:reputation]]: <i class='fa fa-star'></i> <span class='formatted-number post_rep_{posts.uid}'>{posts.user_rep}</span>&nbsp;|&nbsp;[[topic:posts]]: <i class='fa fa-pencil'></i> <span class='formatted-number user_postcount_{posts.uid}'>{posts.user_postcount}</span>
{posts.additional_profile_info}
</span>
<span class="pull-right">
[[category:posted]] <span class="relativeTimeAgo timeago" title="{posts.relativeTime}"></span>
<!-- IF posts.editor -->
<span>| [[category:last_edited_by]] <strong><a href="{relative_path}/user/{posts.editorslug}">{posts.editorname}</a></strong></span>
<span class="timeago" title="{posts.relativeEditTime}"></span>
<!-- ENDIF posts.editor -->
</span>
<div style="clear:both;"></div>
</div>
<div class="post-info">
<span class="pull-left">
[[topic:reputation]]: <i class='fa fa-star'></i> <span class='formatted-number post_rep_{posts.uid}'>{posts.user_rep}</span>&nbsp;|&nbsp;[[topic:posts]]: <i class='fa fa-pencil'></i> <span class='formatted-number user_postcount_{posts.uid}'>{posts.user_postcount}</span>
{posts.additional_profile_info}
</span>
<span class="pull-right">
[[category:posted]] <span class="relativeTimeAgo timeago" title="{posts.relativeTime}"></span>
<!-- IF posts.editor -->
<span>| [[category:last_edited_by]] <strong><a href="{relative_path}/user/{posts.editorslug}">{posts.editorname}</a></strong></span>
<span class="timeago" title="{posts.relativeEditTime}"></span>
<!-- ENDIF posts.editor -->
</span>
<div style="clear:both;"></div>
</div>
</div>
<div style="clear:both;"></div>
</li>
<!-- IF @first -->

@ -19,49 +19,56 @@
<ul id="topics-container" data-next-start="{nextStart}">
<!-- BEGIN topics -->
<li class="category-item {topics.deleted-class}">
<div class="row">
<div class="col-md-12 topic-row">
<div class="col-md-12 col-xs-12 panel panel-default topic-row">
<a href="{relative_path}/user/{topics.userslug}" class="pull-left">
<img class="img-rounded user-img" src="{topics.picture}" title="{topics.username}" />
</a>
<h3>
<a href="{relative_path}/topic/{topics.slug}">
<h3><span class="topic-title"><strong><i class="fa {topics.pin-icon}"></i> <i class="fa {topics.lock-icon}"></i></strong> {topics.title}</span></h3>
</a>
<small>
<span class="topic-stats">
posts
<strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span>
|
<span class="topic-stats">
views
<strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span>
|
<span>
<a href="{relative_path}/user/{topics.userslug}">
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
</a>
posted in
<a href="{relative_path}/category/{topics.categorySlug}">
<i class="fa {topics.categoryIcon}"></i> {topics.categoryName}
</a>
<span class="timeago" title="{topics.relativeTime}"></span>
</span>
<span class="topic-title">
<strong>
<i class="fa {topics.pin-icon}"></i>
<i class="fa {topics.lock-icon}"></i>
</strong>
{topics.title}
</span>
</a>
</h3>
<span class="pull-right hidden-xs">
<!-- IF topics.unreplied -->
No one has replied
<!-- ELSE -->
<a href="{relative_path}/user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a>
<a href="{relative_path}/topic/{topics.slug}#{topics.teaser_pid}">
replied
</a>
<span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied -->
<small>
<span class="topic-stats">
posts
<strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span>
|
<span class="topic-stats">
views
<strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span>
|
<span>
posted in
<a href="{relative_path}/category/{topics.categorySlug}">
<i class="fa {topics.categoryIcon}"></i> {topics.categoryName}
</a>
<span class="timeago" title="{topics.relativeTime}"></span>
</span>
</small>
</div>
</span>
<span class="pull-right">
<!-- IF topics.unreplied -->
No one has replied
<!-- ELSE -->
<a href="{relative_path}/user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a>
<a href="{relative_path}/topic/{topics.slug}#{topics.teaser_pid}">
replied
</a>
<span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied -->
</span>
</small>
</div>
</li>
<!-- END topics -->

@ -126,6 +126,60 @@ var db = require('./database'),
});
};
Posts.getPostsByPids = function(pids, callback) {
var keys = [];
for(var x=0, numPids=pids.length; x<numPids; ++x) {
keys.push('post:' + pids[x]);
}
db.getObjects(keys, function(err, data) {
if(err) {
return callback(err);
}
async.map(data, function(postData, next) {
if(!postData) {
return next(null);
}
postData.relativeTime = utils.toISOString(postData.timestamp);
postData.relativeEditTime = parseInt(postData.edited, 10) !== 0 ? utils.toISOString(postData.edited) : '';
postTools.parse(postData.content, function(err, content) {
if(err) {
return next(err);
}
postData.content = content;
next(null, postData);
});
}, callback);
});
};
Posts.getPostsByUid = function(callerUid, uid, start, end, callback) {
user.getPostIds(uid, start, end, function(err, pids) {
if(err) {
return callback(err);
}
async.filter(pids, function(pid, next) {
postTools.privileges(pid, callerUid, function(err, privileges) {
next(privileges.read);
});
}, function(pids) {
if (!(pids && pids.length)) {
return callback(null, []);
}
Posts.getPostSummaryByPids(pids, false, callback);
});
});
}
Posts.addUserInfoToPost = function(post, callback) {
user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(err, userData) {
if (err) {
@ -310,39 +364,6 @@ var db = require('./database'),
db.setObject('post:' + pid, data, callback);
};
Posts.getPostsByPids = function(pids, callback) {
var keys = [];
for(var x=0, numPids=pids.length; x<numPids; x++) {
keys.push('post:' + pids[x]);
}
db.getObjects(keys, function(err, data) {
if(err) {
return callback(err);
}
async.map(data, function(postData, next) {
if(!postData) {
return next(null);
}
postData.relativeTime = utils.toISOString(postData.timestamp);
postData.relativeEditTime = parseInt(postData.edited, 10) !== 0 ? utils.toISOString(postData.edited) : '';
postTools.parse(postData.content, function(err, content) {
if(err) {
return next(err);
}
postData.content = content;
next(null, postData);
});
}, callback);
});
}
Posts.getCidByPid = function(pid, callback) {
Posts.getPostField(pid, 'tid', function(err, tid) {
if(err) {
@ -418,40 +439,6 @@ var db = require('./database'),
});
}
Posts.getPostsByUid = function(uid, start, end, callback) {
user.getPostIds(uid, start, end, function(err, pids) {
if(err) {
return callback(err);
}
async.filter(pids, function(pid, next) {
postTools.privileges(pid, 0, function(err, privileges) {
next(privileges.read);
});
}, function(pids) {
if (pids && pids.length) {
plugins.fireHook('filter:post.getTopic', pids, function(err, posts) {
if(err) {
return callback(err);
}
if (posts && posts.length) {
Posts.getPostsByPids(pids, function(err, posts) {
plugins.fireHook('action:post.gotTopic', posts);
callback(null, posts);
});
} else {
callback(null, []);
}
});
} else {
callback(null, []);
}
});
});
}
Posts.reIndexPids = function(pids, callback) {
function reIndex(pid, callback) {
@ -478,16 +465,11 @@ var db = require('./database'),
Posts.getFavourites = function(uid, callback) {
db.getSortedSetRevRange('uid:' + uid + ':favourites', 0, -1, function(err, pids) {
if (err)
if (err) {
return callback(err, null);
}
Posts.getPostSummaryByPids(pids, false, function(err, posts) {
if (err)
return callback(err, null);
callback(null, posts);
});
Posts.getPostSummaryByPids(pids, false, callback);
});
}

@ -68,39 +68,44 @@ var path = require('path'),
async.each(data.categories, iterator, function (err) {
// Assemble the MOTD
var motdString;
var motdString,
assemble = function() {
data.motd_class = (parseInt(meta.config.show_motd, 10) === 1 || meta.config.show_motd === undefined) ? '' : ' none';
data.motd_class += (meta.config.motd && meta.config.motd.length > 0 ? '' : ' default');
data.motd_class += meta.config.motd_class ? ' ' + meta.config.motd_class : '';
data.motd = require('marked')(motdString);
res.json(data);
};
if (!meta.config.motd) {
// Construct default MOTD
translator.mget(['global:motd.welcome', 'global:motd.get', 'global:motd.fork', 'global:motd.like', 'global:motd.follow'], function(err, strings) {
motdString = '\n\n# NodeBB \n<small><span>v' + pkg.version + '</span></small>\n\n' + strings[0] +
'<div class="btn-group">\
translator.translate('\n\n# NodeBB \n<small><span>v' + pkg.version + '</span></small>\n\n<h4>[[global:motd.welcome]]</h4>\
<div class="btn-group">\
<a target="_blank" href="https://www.nodebb.org" class="btn btn-default btn-lg">\
<i class="fa fa-comment"></i>\
<span class="hidden-mobile">&nbsp;' + strings[1] + '</span>\
<span class="">&nbsp;[[global:motd.get]]</span>\
</a>\
<a target="_blank" href="https://github.com/designcreateplay/NodeBB" class="btn btn-default btn-lg hidden-mobile">\
<a target="_blank" href="https://github.com/designcreateplay/NodeBB" class="btn btn-default btn-lg">\
<i class="fa fa-github"></i>\
<span class="hidden-mobile">&nbsp;' + strings[2] + '</span>\
<span class="">&nbsp;[[global:motd.fork]]</span>\
</a>\
<a target="_blank" href="https://facebook.com/NodeBB" class="btn btn-default btn-lg">\
<i class="fa fa-facebook"></i>\
<span class="hidden-mobile">&nbsp;' + strings[3] + '</span>\
<span class="">&nbsp;[[global:motd.like]]</span>\
</a>\
<a target="_blank" href="https://twitter.com/NodeBB" class="btn btn-default btn-lg">\
<i class="fa fa-twitter"></i>\
<span class="hidden-mobile">&nbsp;' + strings[4] + '</span>\
<span class="">&nbsp;[[global:motd.follow]]</span>\
</a>\
</div>';
</div>\
', function(motd) {
motdString = motd;
assemble();
});
} else {
motdString = meta.config.motd;
assemble();
}
data.motd_class = (parseInt(meta.config.show_motd, 10) === 1 || meta.config.show_motd === undefined) ? '' : ' none';
data.motd_class += (meta.config.motd && meta.config.motd.length > 0 ? '' : ' default');
data.motd_class += meta.config.motd_class ? ' ' + meta.config.motd_class : '';
data.motd = require('marked')(motdString);
res.json(data);
});
});
});

@ -407,38 +407,41 @@ var fs = require('fs'),
var callerUID = req.user ? req.user.uid : '0';
getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) {
if (userData) {
user.isFollowing(callerUID, userData.theirid, function (isFollowing) {
posts.getPostsByUid(userData.theirid, 0, 9, function (err, posts) {
if(!userData) {
return res.json(404, {
error: 'User not found!'
});
}
if(err) {
return next(err);
}
user.isFollowing(callerUID, userData.theirid, function (isFollowing) {
userData.posts = posts.filter(function (p) {
return p && parseInt(p.deleted, 10) !== 1;
});
posts.getPostsByUid(callerUID, userData.theirid, 0, 9, function (err, posts) {
userData.isFollowing = isFollowing;
if(err) {
return next(err);
}
if (!userData.profileviews) {
userData.profileviews = 1;
}
if (parseInt(callerUID, 10) !== parseInt(userData.uid, 10)) {
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
}
userData.posts = posts.filter(function (p) {
return p && parseInt(p.deleted, 10) !== 1;
});
postTools.parse(userData.signature, function (err, signature) {
userData.signature = signature;
res.json(userData);
});
userData.isFollowing = isFollowing;
if (!userData.profileviews) {
userData.profileviews = 1;
}
if (parseInt(callerUID, 10) !== parseInt(userData.uid, 10)) {
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
}
postTools.parse(userData.signature, function (err, signature) {
userData.signature = signature;
res.json(userData);
});
});
} else {
res.json(404, {
error: 'User not found!'
});
}
});
});
});

@ -71,13 +71,23 @@ SocketTopics.postcount = function(socket, tid, callback) {
topics.getTopicField(tid, 'postcount', callback);
};
SocketTopics.markAsRead = function(socket, data) {
if(!data || !data.tid || !data.uid) {
return;
}
topics.markAsRead(data.tid, data.uid, function(err) {
topics.pushUnreadCount(data.uid);
});
}
SocketTopics.markAllRead = function(socket, data, callback) {
topics.markAllRead(socket.uid, function(err) {
if(err) {
return callback(err);
}
index.server.sockets.in('uid_' + socket.uid).emit('event:unread.updateCount', null, 0);
index.server.sockets.in('uid_' + socket.uid).emit('event:unread.updateCount', null, []);
callback(null);
});

@ -141,9 +141,7 @@ SocketUser.getOnlineAnonCount = function(socket, data, callback) {
};
SocketUser.getUnreadCount = function(socket, data, callback) {
topics.getUnreadTids(socket.uid, 0, 19, function(err, tids) {
callback(err, tids?tids.length:0);
});
topics.getUnreadTids(socket.uid, 0, 19, callback);
};
SocketUser.getActiveUsers = function(socket, data, callback) {

@ -184,8 +184,9 @@ var async = require('async'),
return callback(err, null);
}
Topics.markAsRead(tid, uid);
Topics.pushUnreadCount();
Topics.markAsRead(tid, uid, function(err) {
Topics.pushUnreadCount(null);
});
});
});
});
@ -633,9 +634,13 @@ var async = require('async'),
uids = [uids];
}
uids = uids.filter(function(value) {
return parseInt(value, 10) !== 0;
});
async.each(uids, function(uid, next) {
Topics.getUnreadTids(uid, 0, 19, function(err, tids) {
websockets.in('uid_' + uid).emit('event:unread.updateCount', null, tids.length);
websockets.in('uid_' + uid).emit('event:unread.updateCount', null, tids);
next();
});
}, function(err) {
@ -764,8 +769,9 @@ var async = require('async'),
// "quiet" is used for things like RSS feed updating, HTML parsing for non-js users, etc
if (!quiet) {
Topics.markAsRead(tid, current_user);
Topics.pushUnreadCount(current_user);
Topics.markAsRead(tid, current_user, function(err) {
Topics.pushUnreadCount(current_user);
});
Topics.increaseViewCount(tid);
}
@ -916,13 +922,15 @@ var async = require('async'),
return callback(err);
}
if (tids && tids.length) {
for (var i = 0; i < tids.length; ++i) {
Topics.markAsRead(tids[i], uid);
}
if(!tids || !tids.length) {
return callback(null);
}
callback(null);
function markRead(tid, next) {
Topics.markAsRead(tid, uid, next);
}
async.each(tids, markRead, callback);
});
}
@ -938,9 +946,13 @@ var async = require('async'),
db.delete('tid:' + tid + ':read_by_uid', callback);
}
Topics.markAsRead = function(tid, uid) {
Topics.markAsRead = function(tid, uid, callback) {
db.setAdd('tid:' + tid + ':read_by_uid', uid);
db.setAdd('tid:' + tid + ':read_by_uid', uid, function(err) {
if(callback) {
callback(err);
}
});
Topics.getTopicField(tid, 'cid', function(err, cid) {

@ -471,11 +471,10 @@ var bcrypt = require('bcrypt'),
}
var usernames = Object.keys(usernamesHash),
filterRegex = new RegExp('^' + query + '.*?$', 'i'),
results = [];
results = usernames.filter(function(username) { // Remove non-matches
return filterRegex.test(username);
return username.indexOf(query) === 0;
}).sort(function(a, b) { // Sort alphabetically
return a > b;
}).slice(0, 5) // Limit 5
@ -933,6 +932,7 @@ var bcrypt = require('bcrypt'),
notifications.get(nid, uid, function(notif_data) {
// If the notification could not be found, silently drop it
if (notif_data) {
notif_data.readClass = !notif_data.read ? 'label-warning' : '';
unread.push(notif_data);
} else {
db.sortedSetRemove('uid:' + uid + ':notifications:unread', nid);
@ -1015,7 +1015,7 @@ var bcrypt = require('bcrypt'),
return parseInt(b.datetime, 10) - parseInt(a.datetime, 10);
}).map(function(notif) {
notif.datetimeISO = utils.toISOString(notif.datetime);
notif.readClass = !notif.read ? 'unread' : '';
notif.readClass = !notif.read ? 'label-warning' : '';
return notif;
});

Loading…
Cancel
Save