Merge remote-tracking branch 'origin/master' into composer-revamp

This commit is contained in:
Julian Lam
2013-12-18 15:12:30 -05:00
18 changed files with 348 additions and 104 deletions

2
nodebb
View File

@@ -20,7 +20,7 @@ case "$1" in
echo "Launching NodeBB in \"development\" mode." echo "Launching NodeBB in \"development\" mode."
echo "To run the production build of NodeBB, please use \"forever\"." echo "To run the production build of NodeBB, please use \"forever\"."
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB" echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
NODE_ENV=development supervisor --extensions 'node|js|tpl' -- app $1 NODE_ENV=development supervisor -q --extensions 'node|js|tpl' -- app $1
;; ;;
language) language)

View File

@@ -32,5 +32,8 @@
"show_email": "Show My Email", "show_email": "Show My Email",
"has_no_follower": "This user doesn't have any followers :(", "has_no_follower": "This user doesn't have any followers :(",
"follows_no_one": "This user isn't following anyone :(" "follows_no_one": "This user isn't following anyone :(",
"email_hidden": "Email Hidden",
"hidden": "hidden"
} }

View File

@@ -211,6 +211,7 @@ define(['uploader'], function(uploader) {
var modal = $('#category-permissions-modal'), var modal = $('#category-permissions-modal'),
searchEl = modal.find('#permission-search'), searchEl = modal.find('#permission-search'),
resultsEl = modal.find('.search-results'), resultsEl = modal.find('.search-results'),
groupsResultsEl = modal.find('.groups-results'),
searchDelay; searchDelay;
searchEl.off().on('keyup', function() { searchEl.off().on('keyup', function() {
@@ -263,6 +264,40 @@ define(['uploader'], function(uploader) {
searchEl.keyup(); searchEl.keyup();
}); });
// User Groups and privileges
socket.emit('api:admin.categories.groupsearch', cid, function(err, results) {
var groupsFrag = document.createDocumentFragment(),
liEl = document.createElement('li');
var numResults = results.length,
resultObj;
for(var x=0;x<numResults;x++) {
resultObj = results[x];
liEl.setAttribute('data-gid', resultObj.gid);
liEl.innerHTML = '<div class="pull-right">' +
'<div class="btn-group">' +
'<button type="button" data-gpriv="+gr" class="btn btn-default' + (resultObj.privileges['+gr'] ? ' active' : '') + '">Read</button>' +
'<button type="button" data-gpriv="+gw" class="btn btn-default' + (resultObj.privileges['+gw'] ? ' active' : '') + '">Write</button>' +
'</div>' +
'</div>' +
' '+resultObj.name;
groupsFrag.appendChild(liEl.cloneNode(true));
}
groupsResultsEl.html(groupsFrag);
});
groupsResultsEl.off().on('click', '[data-gpriv]', function(e) {
var btnEl = $(this),
gid = btnEl.parents('li[data-gid]').attr('data-gid'),
privilege = this.getAttribute('data-gpriv');
e.preventDefault();
socket.emit('api:admin.categories.setGroupPrivilege', cid, gid, privilege, !btnEl.hasClass('active'), function(err, privileges) {
btnEl.toggleClass('active', privileges[privilege]);
});
})
modal.modal(); modal.modal();
}; };

View File

@@ -102,7 +102,7 @@ define(function() {
var loadingEl = document.getElementById('categories-loading'); var loadingEl = document.getElementById('categories-loading');
if (loadingEl) { if (loadingEl) {
socket.once('api:categories.get', function(data) { socket.emit('api:categories.get', function(data) {
// Render categories // Render categories
var categoriesFrag = document.createDocumentFragment(), var categoriesFrag = document.createDocumentFragment(),
categoryEl = document.createElement('li'), categoryEl = document.createElement('li'),
@@ -172,7 +172,6 @@ define(function() {
} }
}); });
}); });
socket.emit('api:categories.get');
} }
}); });
} }
@@ -483,7 +482,21 @@ define(function() {
adjust_rep(-1, data.pid, data.uid); adjust_rep(-1, data.pid, data.uid);
}); });
socket.on('event:new_post', createNewPosts); socket.on('event:new_post', function(data) {
var posts = data.posts;
for (var p in posts) {
if (posts.hasOwnProperty(p)) {
var post = posts[p],
postcount = jQuery('.user_postcount_' + post.uid),
ptotal = parseInt(postcount.html(), 10);
ptotal += 1;
postcount.html(ptotal);
}
}
createNewPosts(data);
});
socket.on('event:topic_deleted', function(data) { socket.on('event:topic_deleted', function(data) {
if (data.tid === tid && data.status === 'ok') { if (data.tid === tid && data.status === 'ok') {

View File

@@ -20,34 +20,51 @@
<span class="label label-danger">[[user:banned]]</span> <span class="label label-danger">[[user:banned]]</span>
</div> </div>
<!-- ENDIF banned --> <!-- ENDIF banned -->
<div>
<a id="chat-btn" href="#" class="btn btn-default hide">Chat</a>
</div>
<div id="user-actions"> <div id="user-actions">
<a id="follow-btn" href="#" class="btn btn-default">Follow</a> <a id="follow-btn" href="#" class="btn btn-default hide">Follow</a>
<a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a> <a id="unfollow-btn" href="#" class="btn btn-default hide">Unfollow</a>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="inline-block"> <div class="inline-block">
<div class="account-bio-block"> <div class="account-bio-block">
<span class="account-bio-label">[[user:email]]</span><i class="fa fa-eye-slash {emailClass}" title="Email hidden"></i> <span class="account-bio-label">[[user:email]]</span><i class="fa fa-eye-slash {emailClass}" title="[[user:email_hidden]]"></i>
<!-- IF email -->
<span>{email}</span> <span>{email}</span>
<!-- ELSE -->
<i class="fa fa-eye-slash" title="[[user:email_hidden]]"></i> [[user:hidden]]
<!-- ENDIF email -->
<br/> <br/>
<!-- IF fullname -->
<span class="account-bio-label">[[user:fullname]]</span> <span class="account-bio-label">[[user:fullname]]</span>
<span>{fullname}</span> <span>{fullname}</span>
<br/> <br/>
<!-- ENDIF fullname -->
<!-- IF website -->
<span class="account-bio-label">[[user:website]]</span> <span class="account-bio-label">[[user:website]]</span>
<span><a href="{website}">{websiteName}</a></span> <span><a href="{website}">{websiteName}</a></span>
<br/> <br/>
<!-- ENDIF website -->
<!-- IF location -->
<span class="account-bio-label">[[user:location]]</span> <span class="account-bio-label">[[user:location]]</span>
<span>{location}</span> <span>{location}</span>
<br/> <br/>
<!-- ENDIF location -->
<!-- IF age -->
<span class="account-bio-label">[[user:age]]</span> <span class="account-bio-label">[[user:age]]</span>
<span>{age}</span> <span>{age}</span>
<br/> <br/>
<!-- ENDIF age -->
<hr/> <hr/>
<span class="account-bio-label">[[user:joined]]</span> <span class="account-bio-label">[[user:joined]]</span>
<span class="timeago" title="{joindate}"></span> <span class="timeago" title="{joindate}"></span>
@@ -74,10 +91,12 @@
<br/> <br/>
<hr/> <hr/>
<!-- IF signature -->
<span class="account-bio-label">[[user:signature]]</span> <span class="account-bio-label">[[user:signature]]</span>
<div class="post-signature"> <div class="post-signature">
<span id='signature'>{signature}</span> <span id='signature'>{signature}</span>
</div> </div>
<!-- ENDIF signature -->
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long

View File

@@ -40,7 +40,8 @@
</div> </div>
<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">
<button class="btn btn-primary" id="login" type="submit">[[login:login]]</button> &nbsp; <a id="reset-link" class="hide" href="/reset">[[login:forgot_password]]</a> <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="/reset">[[login:forgot_password]]</a>
</div> </div>
</div> </div>
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" /> <input type="hidden" name="_csrf" value="{token}" id="csrf-token" />

View File

@@ -15,41 +15,54 @@
<div class="category search"> <div class="category search">
<div class="{show_results}"> <div class="{show_results}">
<ul id="topics-container" data-search-query="{search_query}">
<h3>[[topic:topics]]</h3> <h3>[[topic:topics]]</h3>
<!-- IF topic_matches -->
<small>{topic_matches} result(s) matching "{search_query}"</small>
<!-- 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>
<!-- BEGIN topics -->
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
<li class="category-item">
<div>
<div class="col-md-12 img-thumbnail">
<div class="search-result-post">
<img src="{topics.teaser_userpicture}" />
<strong>{topics.teaser_username}</strong>: <span class="search-result-text">{topics.title}</span>
</div>
<ul id="topics-container" data-search-query="{search_query}">
<!-- BEGIN topics -->
<li class="category-item">
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
<div>
<div class="col-md-12 img-thumbnail">
<div class="search-result-post">
<img src="{topics.teaser_userpicture}" />
<strong>{topics.teaser_username}</strong>: <span class="search-result-text">{topics.title}</span>
</div>
</div>
</div> </div>
</div> </a>
</li> </li>
</a> <!-- END topics -->
<!-- END topics --> </ul>
<h3>Posts</h3> <h3>Posts</h3>
<!-- IF post_matches -->
<small>{post_matches} result(s) matching "{search_query}"</small>
<!-- 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>
<!-- BEGIN posts -->
<a href="../../topic/{posts.topicSlug}#{posts.pid}" id="tid-{posts.tid}">
<li class="category-item">
<div>
<div class="col-md-12 img-thumbnail">
<div class="search-result-post">
<img src="{posts.picture}" />
<strong>{posts.username}</strong>: <span class="search-result-text">{posts.content}</span>
</div>
<ul id="topics-container" data-search-query="{search_query}">
<!-- BEGIN posts -->
<li class="category-item">
<a href="../../topic/{posts.topicSlug}#{posts.pid}" id="tid-{posts.tid}">
<div>
<div class="col-md-12 img-thumbnail">
<div class="search-result-post">
<img src="{posts.picture}" />
<strong>{posts.username}</strong>: <span class="search-result-text">{posts.content}</span>
</div>
</div>
</div> </div>
</div> </a>
</li> </li>
</a> <!-- END posts -->
<!-- END posts -->
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -109,7 +109,7 @@
<div class="post-info"> <div class="post-info">
<span class="pull-left"> <span class="pull-left">
[[topic:reputation]]: <i class='fa fa-star'></i> <span class='formatted-number'>{posts.user_rep}</span>&nbsp;|&nbsp;[[topic:posts]]: <i class='fa fa-pencil'></i> <span class='formatted-number'>{posts.user_postcount}</span> [[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} {posts.additional_profile_info}
</span> </span>
<span class="pull-right"> <span class="pull-right">

View File

@@ -65,4 +65,56 @@ CategoryTools.privileges = function(cid, uid, callback) {
}); });
}; };
CategoryTools.groupPrivileges = function(cid, gid, callback) {
async.parallel({
"+gr": function(next) {
var key = 'cid:' + cid + ':privileges:+gr';
Groups.exists(key, function(err, exists) {
if (exists) {
async.parallel({
isMember: function(next) {
Groups.isMemberByGroupName(gid, key, next);
},
isEmpty: function(next) {
Groups.isEmptyByGroupName(key, next);
}
}, next);
} else {
next(null, {
isMember: false,
isEmpty: true
});
}
});
},
"+gw": function(next) {
var key = 'cid:' + cid + ':privileges:+gw';
Groups.exists(key, function(err, exists) {
if (exists) {
async.parallel({
isMember: function(next) {
Groups.isMemberByGroupName(gid, key, next);
},
isEmpty: function(next) {
Groups.isEmptyByGroupName(key, next);
}
}, next);
} else {
next(null, {
isMember: false,
isEmpty: true
});
}
});
}
}, function(err, privileges) {
callback(err, !privileges ? null : {
"+gr": privileges['+gr'].isMember,
"+gw": privileges['+gw'].isMember,
read: (privileges['+gr'].isMember || privileges['+gr'].isEmpty),
write: (privileges['+gw'].isMember || privileges['+gw'].isEmpty),
});
});
};
module.exports = CategoryTools; module.exports = CategoryTools;

View File

@@ -176,7 +176,6 @@
Groups.update = function(gid, values, callback) { Groups.update = function(gid, values, callback) {
db.exists('gid:' + gid, function (err, exists) { db.exists('gid:' + gid, function (err, exists) {
console.log('exists?', gid, exists, values);
if (!err && exists) { if (!err && exists) {
db.setObject('gid:' + gid, values, callback); db.setObject('gid:' + gid, values, callback);
} else { } else {
@@ -199,7 +198,6 @@
Groups.getGidFromName(groupName, function(err, gid) { Groups.getGidFromName(groupName, function(err, gid) {
if (err || !gid) { if (err || !gid) {
Groups.create(groupName, '', function(err, groupObj) { Groups.create(groupName, '', function(err, groupObj) {
console.log('creating group, calling hide', groupObj.gid);
async.parallel([ async.parallel([
function(next) { function(next) {
Groups.hide(groupObj.gid, next); Groups.hide(groupObj.gid, next);
@@ -263,4 +261,40 @@
}); });
}; };
Groups.getCategoryAccess = function(cid, uid, callback){
var access = false;
// check user group read access level
async.series([function(callback){
// get groups with read permission
db.getObjectField('group:gid', 'cid:' + cid + ':privileges:+gr', function(err, gid){
// get the user groups that belong to this read group
db.getSetMembers('gid:' + gid + ':members', function (err, gids) {
// check if user belong to any of these user groups
var groups_check = new Array();
gids.forEach(function(cgid){
groups_check.push(function(callback){
Groups.isMember(uid, cgid, function(err, isMember){
if (isMember){
access = true;
}
callback(null, gids);
})
});
});
// do a series check. We want to make sure we check all the groups before determining if the user
// has access or not.
async.series(groups_check, function(err, results){
callback(null, results);
});
});
});
}],
function(err, results){
// if the read group is empty we will asume that read access has been granted to ALL
if (results[0].length == 0){ access = true; }
callback(false, access);
});
};
}(module.exports)); }(module.exports));

View File

@@ -14,7 +14,8 @@ var db = require('./database'),
async = require('async'), async = require('async'),
nconf = require('nconf'), nconf = require('nconf'),
validator = require('validator'), validator = require('validator'),
winston = require('winston'); winston = require('winston'),
gravatar = require('gravatar');
(function(Posts) { (function(Posts) {
var customUserInfo = {}; var customUserInfo = {};
@@ -202,12 +203,16 @@ var db = require('./database'),
} }
postTools.parseSignature(userData.signature, function(err, signature) { postTools.parseSignature(userData.signature, function(err, signature) {
if(err) {
return callback(err);
}
post.username = userData.username || 'anonymous'; post.username = userData.username || 'anonymous';
post.userslug = userData.userslug || ''; post.userslug = userData.userslug || '';
post.user_rep = userData.reputation || 0; post.user_rep = userData.reputation || 0;
post.user_postcount = userData.postcount || 0; post.user_postcount = userData.postcount || 0;
post.user_banned = parseInt(userData.banned, 10) === 1; post.user_banned = parseInt(userData.banned, 10) === 1;
post.picture = userData.picture || require('gravatar').url('', {}, https = nconf.get('https')); post.picture = userData.picture || gravatar.url('', {}, https = nconf.get('https'));
post.signature = signature; post.signature = signature;
for (var info in customUserInfo) { for (var info in customUserInfo) {
@@ -217,11 +222,16 @@ var db = require('./database'),
} }
plugins.fireHook('filter:posts.custom_profile_info', {profile: "", uid: post.uid, pid: post.pid}, function(err, profile_info) { plugins.fireHook('filter:posts.custom_profile_info', {profile: "", uid: post.uid, pid: post.pid}, function(err, profile_info) {
if(err) {
return callback(err);
}
post.additional_profile_info = profile_info.profile; post.additional_profile_info = profile_info.profile;
if (post.editor !== '') { if (post.editor !== '') {
user.getUserFields(post.editor, ['username', 'userslug'], function(err, editorData) { user.getUserFields(post.editor, ['username', 'userslug'], function(err, editorData) {
if (err) return callback(); if (err) {
return callback(err);
}
post.editorname = editorData.username; post.editorname = editorData.username;
post.editorslug = editorData.userslug; post.editorslug = editorData.userslug;
@@ -439,8 +449,9 @@ var db = require('./database'),
Posts.uploadPostImage = function(image, callback) { Posts.uploadPostImage = function(image, callback) {
if(!image) if(!image) {
return callback('invalid image', null); return callback('invalid image', null);
}
require('./imgur').upload(meta.config.imgurClientID, image.data, 'base64', function(err, data) { require('./imgur').upload(meta.config.imgurClientID, image.data, 'base64', function(err, data) {
if(err) { if(err) {
@@ -455,22 +466,29 @@ var db = require('./database'),
} }
Posts.getPostsByUid = function(uid, start, end, callback) { Posts.getPostsByUid = function(uid, start, end, callback) {
user.getPostIds(uid, start, end, function(pids) { user.getPostIds(uid, start, end, function(err, pids) {
if(err) {
return callback(err);
}
if (pids && pids.length) { if (pids && pids.length) {
plugins.fireHook('filter:post.getTopic', pids, function(err, posts) { plugins.fireHook('filter:post.getTopic', pids, function(err, posts) {
if(err) {
return callback(err);
}
if (!err & 0 < posts.length) { if (posts && posts.length) {
Posts.getPostsByPids(pids, function(err, posts) { Posts.getPostsByPids(pids, function(err, posts) {
plugins.fireHook('action:post.gotTopic', posts); plugins.fireHook('action:post.gotTopic', posts);
callback(posts); callback(null, posts);
}); });
} else { } else {
callback(posts); callback(null, []);
} }
}); });
} else } else {
callback([]); callback(null, []);
}
}); });
} }

View File

@@ -244,7 +244,7 @@ var nconf = require('nconf'),
var custom_routes = { var custom_routes = {
'routes': [], 'routes': [],
'api_methods': [] 'api': []
}; };
plugins.ready(function() { plugins.ready(function() {
@@ -264,6 +264,19 @@ var nconf = require('nconf'),
}(route)); }(route));
} }
} }
var apiRoutes = custom_routes.api;
for (var route in apiRoutes) {
if (apiRoutes.hasOwnProperty(route)) {
(function(route) {
app[apiRoutes[route].method || 'get']('/api/admin' + apiRoutes[route].route, function(req, res) {
apiRoutes[route].callback(req, res, function(data) {
res.json(data);
});
});
}(route));
}
}
}); });
}); });

View File

@@ -4,6 +4,7 @@ var path = require('path'),
db = require('../database'), db = require('../database'),
user = require('../user'), user = require('../user'),
groups = require('../groups'),
auth = require('./authentication'), auth = require('./authentication'),
topics = require('../topics'), topics = require('../topics'),
posts = require('../posts'), posts = require('../posts'),
@@ -39,33 +40,26 @@ var path = require('path'),
res.json(200, config); res.json(200, config);
}); });
app.get('/home', function (req, res, next) { app.get('/home', function (req, res) {
var uid = (req.user) ? req.user.uid : 0; var uid = (req.user) ? req.user.uid : 0;
categories.getAllCategories(uid, function (err, data) { categories.getAllCategories(uid, function (err, data) {
data.categories = data.categories.filter(function (category) { data.categories = data.categories.filter(function (category) {
return (!category.disabled || parseInt(category.disabled, 10) === 0); return (!category.disabled || parseInt(category.disabled, 10) === 0);
}); });
function getRecentReplies(category, callback) { function iterator(category, callback) {
categories.getRecentReplies(category.cid, 2, function (err, posts) { categories.getRecentReplies(category.cid, 2, function (err, posts) {
if(err) {
return callback(err);
}
category.posts = posts; category.posts = posts;
category.post_count = posts.length > 2 ? 2 : posts.length; category.post_count = posts.length > 2 ? 2 : posts.length;
callback(null); callback(null);
}); });
} }
async.each(data.categories, getRecentReplies, function (err) { async.each(data.categories, iterator, function (err) {
if(err) {
return next(err);
}
data.motd_class = (parseInt(meta.config.show_motd, 10) === 1 || meta.config.show_motd === undefined) ? '' : ' none'; 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 && meta.config.motd.length > 0 ? '' : ' default');
data.motd = require('marked')(meta.config.motd || "<div class=\"pull-right btn-group\"><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-comment\"></i><span class='hidden-mobile'>&nbsp;Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-github\"></i><span class='hidden-mobile'>&nbsp;Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-twitter\"></i><span class='hidden-mobile'>&nbsp;@dcplabs</span></a></div>\n\n# NodeBB <span>v" + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future."); data.motd = require('marked')(meta.config.motd || "<div class=\"pull-right btn-group\"><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-comment\"></i><span class='hidden-mobile'>&nbsp;Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-github\"></i><span class='hidden-mobile'>&nbsp;Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-twitter\"></i><span class='hidden-mobile'>&nbsp;@NodeBB</span></a></div>\n\n# NodeBB <span>v" + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future.");
res.json(data); res.json(data);
}); });
}); });
@@ -128,7 +122,15 @@ var path = require('path'),
if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) { if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) {
return res.json(404, {}); return res.json(404, {});
} }
res.json(data); // get the category this post belongs to and check category access
var cid = data.category_slug.split("/")[0];
groups.getCategoryAccess(cid, uid, function(err, access){
if (access){
res.json(data);
} else {
res.send(403);
}
})
} else next(); } else next();
}); });
}); });
@@ -139,13 +141,20 @@ var path = require('path'),
// Category Whitelisting // Category Whitelisting
categoryTools.privileges(req.params.id, uid, function(err, privileges) { categoryTools.privileges(req.params.id, uid, function(err, privileges) {
if (!err && privileges.read) { if (!err && privileges.read) {
categories.getCategoryById(req.params.id, uid, function (err, data) { groups.getCategoryAccess(req.params.id, uid, function(err, access){
if (!err && data && parseInt(data.disabled, 10) === 0) { if (access){
res.json(data); categories.getCategoryById(req.params.id, uid, function (err, data) {
if (!err && data && parseInt(data.disabled, 10) === 0) {
res.json(data);
} else {
next();
}
}, req.params.id, uid);
} else { } else {
next(); res.send(403);
} }
}, req.params.id, uid);
});
} else { } else {
res.send(403); res.send(403);
} }
@@ -244,10 +253,6 @@ var path = require('path'),
return callback(err, null); return callback(err, null);
} }
if(pids.length > 50) {
pids = pids.splice(0, 50);
}
posts.getPostSummaryByPids(pids, false, function (err, posts) { posts.getPostSummaryByPids(pids, false, function (err, posts) {
if (err){ if (err){
return callback(err, null); return callback(err, null);
@@ -263,10 +268,6 @@ var path = require('path'),
return callback(err, null); return callback(err, null);
} }
if(tids.length > 50) {
tids = tids.splice(0, 50);
}
topics.getTopicsByTids(tids, 0, function (topics) { topics.getTopicsByTids(tids, 0, function (topics) {
callback(null, topics); callback(null, topics);
}, 0); }, 0);
@@ -285,7 +286,9 @@ var path = require('path'),
show_results: '', show_results: '',
search_query: req.params.term, search_query: req.params.term,
posts: results[0], posts: results[0],
topics: results[1] topics: results[1],
post_matches : results[0].length,
topic_matches : results[1].length
}); });
}); });
} else { } else {
@@ -313,7 +316,7 @@ var path = require('path'),
app.get('/500', function(req, res) { app.get('/500', function(req, res) {
res.json({errorMessage: 'testing'}); res.json({errorMessage: 'testing'});
}) });
}); });
} }
}(exports)); }(exports));

View File

@@ -72,7 +72,7 @@
login_strategies.push({ login_strategies.push({
name: 'google', name: 'google',
url: '/auth/google', url: '/auth/google',
callbackURL: nconf.get('url') + '/auth/google/callback', callbackURL: '/auth/google/callback',
icon: 'google-plus', icon: 'google-plus',
scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email' scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
}); });
@@ -95,7 +95,7 @@
login_strategies.push({ login_strategies.push({
name: 'facebook', name: 'facebook',
url: '/auth/facebook', url: '/auth/facebook',
callbackURL: nconf.get('url') + '/auth/facebook/callback', callbackURL: '/auth/facebook/callback',
icon: 'facebook', icon: 'facebook',
scope: 'email' scope: 'email'
}); });

View File

@@ -393,18 +393,24 @@ var fs = require('fs'),
}); });
}); });
app.get('/api/user/:userslug', function (req, res) { app.get('/api/user/:userslug', function (req, res, next) {
var callerUID = req.user ? req.user.uid : '0'; var callerUID = req.user ? req.user.uid : '0';
getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) { getUserDataByUserSlug(req.params.userslug, callerUID, function (userData) {
if (userData) { if (userData) {
user.isFollowing(callerUID, userData.theirid, function (isFollowing) { user.isFollowing(callerUID, userData.theirid, function (isFollowing) {
posts.getPostsByUid(userData.theirid, 0, 9, function (posts) { posts.getPostsByUid(userData.theirid, 0, 9, function (err, posts) {
if(err) {
return next(err);
}
userData.posts = posts.filter(function (p) { userData.posts = posts.filter(function (p) {
return p && parseInt(p.deleted, 10) !== 1; return p && parseInt(p.deleted, 10) !== 1;
}); });
userData.isFollowing = isFollowing; userData.isFollowing = isFollowing;
if (!userData.profileviews) { if (!userData.profileviews) {
userData.profileviews = 1; userData.profileviews = 1;
} }

View File

@@ -444,21 +444,18 @@ var bcrypt = require('bcrypt'),
User.getPostIds = function(uid, start, stop, callback) { User.getPostIds = function(uid, start, stop, callback) {
db.getListRange('uid:' + uid + ':posts', start, stop, function(err, pids) { db.getListRange('uid:' + uid + ':posts', start, stop, function(err, pids) {
if (!err) { if(err) {
if (pids && pids.length) { return callback(err);
callback(pids); }
} else {
callback([]); if (pids && pids.length) {
} callback(null, pids);
} else { } else {
console.log(err); callback(null, []);
callback([]);
} }
}); });
}; };
User.follow = function(uid, followid, callback) { User.follow = function(uid, followid, callback) {
db.setAdd('following:' + uid, followid, function(err, data) { db.setAdd('following:' + uid, followid, function(err, data) {
if (!err) { if (!err) {

View File

@@ -591,9 +591,9 @@ websockets.init = function(io) {
threadTools.move(data.tid, data.cid, socket); threadTools.move(data.tid, data.cid, socket);
}); });
socket.on('api:categories.get', function() { socket.on('api:categories.get', function(callback) {
categories.getAllCategories(0, function(err, categories) { categories.getAllCategories(0, function(err, categories) {
socket.emit('api:categories.get', categories); callback(categories);
}); });
}); });
@@ -714,10 +714,10 @@ websockets.init = function(io) {
return; return;
} }
var finalMessage = username + ' : ' + msg, var username = usersData[0].username,
notifText = 'New message from <strong>' + username + '</strong>', toUsername = usersData[1].username,
username = usersData[0].username, finalMessage = username + ' : ' + msg,
toUsername = usersData[1].username; notifText = 'New message from <strong>' + username + '</strong>';
if (!isUserOnline(touid)) { if (!isUserOnline(touid)) {
notifications.create(notifText, 'javascript:app.openChat(&apos;' + username + '&apos;, ' + uid + ');', 'notification_' + uid + '_' + touid, function(nid) { notifications.create(notifText, 'javascript:app.openChat(&apos;' + username + '&apos;, ' + uid + ');', 'notification_' + uid + '_' + touid, function(nid) {
@@ -1084,6 +1084,37 @@ websockets.init = function(io) {
}); });
}); });
socket.on('api:admin.categories.setGroupPrivilege', function(cid, gid, privilege, set, callback) {
var cb = function(err) {
CategoryTools.groupPrivileges(cid, gid, callback);
};
if (set) {
groups.joinByGroupName('cid:' + cid + ':privileges:' + privilege, gid, cb);
} else {
groups.leaveByGroupName('cid:' + cid + ':privileges:' + privilege, gid, cb);
}
});
socket.on('api:admin.categories.groupsearch', function(cid, callback) {
groups.list({expand:false}, function(err, data){
async.map(data, function(groupObj, next) {
CategoryTools.groupPrivileges(cid, groupObj.gid, function(err, privileges) {
if (!err) {
groupObj.privileges = privileges;
} else {
winston.error('[socket api:admin.categories.groupsearch] Could not retrieve permissions');
}
next(null, groupObj);
});
}, function(err, data) {
if (!callback) socket.emit('api:admin.categories.groupsearch', data);
else callback(null, data);
});
});
});
socket.on('api:admin.themes.getInstalled', function(callback) { socket.on('api:admin.themes.getInstalled', function(callback) {
meta.themes.get(function(err, themeArr) { meta.themes.get(function(err, themeArr) {
callback(themeArr); callback(themeArr);