mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
Merge branch 'master' into develop
This commit is contained in:
@@ -52,7 +52,7 @@
|
||||
"morgan": "^1.3.2",
|
||||
"mousetrap": "^1.5.3",
|
||||
"nconf": "~0.8.2",
|
||||
"nodebb-plugin-composer-default": "4.4.1",
|
||||
"nodebb-plugin-composer-default": "4.4.2",
|
||||
"nodebb-plugin-dbsearch": "2.0.2",
|
||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||
"nodebb-plugin-emoji-one": "1.1.5",
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"alert.confirm-reload": "Are you sure you wish to reload NodeBB?",
|
||||
"alert.confirm-restart": "Are you sure you wish to restart NodeBB?",
|
||||
"alert.confirm-reload": "Bạn có thật sự muốn tải lại NodeBB",
|
||||
"alert.confirm-restart": "Bạn có thật sự muốn khởi động lại NodeBB",
|
||||
|
||||
"acp-title": "%1 | NodeBB Admin Control Panel",
|
||||
"settings-header-contents": "Contents"
|
||||
"acp-title": "%1 | Bảng điểu khiển",
|
||||
"settings-header-contents": "Nội dung"
|
||||
}
|
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"post-cache": "Post Cache",
|
||||
"posts-in-cache": "Posts in Cache",
|
||||
"average-post-size": "Average Post Size",
|
||||
"length-to-max": "Length / Max",
|
||||
"percent-full": "%1% Full",
|
||||
"post-cache-size": "Post Cache Size",
|
||||
"items-in-cache": "Items in Cache",
|
||||
"control-panel": "Control Panel",
|
||||
"update-settings": "Update Cache Settings"
|
||||
"post-cache": "Cache bài viết",
|
||||
"posts-in-cache": "Cache cho bài viết",
|
||||
"average-post-size": "Kích thước bài viết",
|
||||
"length-to-max": "Độ dài / Tối Đa",
|
||||
"percent-full": "%1% Đầy",
|
||||
"post-cache-size": "Kích thước cache bài viết",
|
||||
"items-in-cache": "Thành phần trong Cache",
|
||||
"control-panel": "Bảng điều khiển",
|
||||
"update-settings": "Cập nhật thiết lập Cache"
|
||||
}
|
@@ -1,30 +1,30 @@
|
||||
{
|
||||
"x-b": "%1 b",
|
||||
"x-mb": "%1 mb",
|
||||
"uptime-seconds": "Uptime in Seconds",
|
||||
"uptime-days": "Uptime in Days",
|
||||
"uptime-seconds": "Thời gian hoạt động(giây)",
|
||||
"uptime-days": "Thời gian hoạt động(Ngày)",
|
||||
|
||||
"mongo": "Mongo",
|
||||
"mongo.version": "MongoDB Version",
|
||||
"mongo.storage-engine": "Storage Engine",
|
||||
"mongo.collections": "Collections",
|
||||
"mongo.objects": "Objects",
|
||||
"mongo.avg-object-size": "Avg. Object Size",
|
||||
"mongo.data-size": "Data Size",
|
||||
"mongo.storage-size": "Storage Size",
|
||||
"mongo.index-size": "Index Size",
|
||||
"mongo.file-size": "File Size",
|
||||
"mongo.version": "Phiên bản MongoDB ",
|
||||
"mongo.storage-engine": "Lưu Trữ",
|
||||
"mongo.collections": "Tập dữ liệu",
|
||||
"mongo.objects": "Đối tượng",
|
||||
"mongo.avg-object-size": "Kích thước trung bình",
|
||||
"mongo.data-size": "Kích thước dữ liệu",
|
||||
"mongo.storage-size": "Kích thước lưu trữ",
|
||||
"mongo.index-size": "Kích thước chỉ mục",
|
||||
"mongo.file-size": "Kích thước tập tin",
|
||||
"mongo.resident-memory": "Resident Memory",
|
||||
"mongo.virtual-memory": "Virtual Memory",
|
||||
"mongo.virtual-memory": "Bộ nhớ ảo",
|
||||
"mongo.mapped-memory": "Mapped Memory",
|
||||
"mongo.raw-info": "MongoDB Raw Info",
|
||||
"mongo.raw-info": "Thông tin MongoDB",
|
||||
|
||||
"redis": "Redis",
|
||||
"redis.version": "Redis Version",
|
||||
"redis.connected-clients": "Connected Clients",
|
||||
"redis.version": "Phiên bản Redis",
|
||||
"redis.connected-clients": "Người dùng kết nối",
|
||||
"redis.connected-slaves": "Connected Slaves",
|
||||
"redis.blocked-clients": "Blocked Clients",
|
||||
"redis.used-memory": "Used Memory",
|
||||
"redis.blocked-clients": "Người dùng vi phạm",
|
||||
"redis.used-memory": "Bộ nhớ đã sử dụng",
|
||||
"redis.memory-frag-ratio": "Memory Fragmentation Ratio",
|
||||
"redis.total-connections-recieved": "Total Connections Received",
|
||||
"redis.total-commands-processed": "Total Commands Processed",
|
||||
|
@@ -103,5 +103,5 @@
|
||||
"cookies.message": "Trang web này sử dụng cookie để đảm bảo trải nghiệm tốt nhất cho người dùng",
|
||||
"cookies.accept": "Đã rõ!",
|
||||
"cookies.learn_more": "Xem thêm",
|
||||
"edited": "Edited"
|
||||
"edited": "Đã cập nhật"
|
||||
}
|
@@ -13,8 +13,8 @@
|
||||
"notify_me": "Được thông báo khi có trả lời mới trong chủ đề này",
|
||||
"quote": "Trích dẫn",
|
||||
"reply": "Trả lời",
|
||||
"replies_to_this_post": "%1 Replies",
|
||||
"last_reply_time": "Last reply",
|
||||
"replies_to_this_post": "%1 trả lời",
|
||||
"last_reply_time": "Trả lời cuối cùng",
|
||||
"reply-as-topic": "Trả lời dưới dạng chủ đề",
|
||||
"guest-login-reply": "Hãy đăng nhập để trả lời",
|
||||
"edit": "Chỉnh sửa",
|
||||
|
@@ -6,7 +6,7 @@
|
||||
"search": "搜索",
|
||||
"enter_username": "输入用户名搜索",
|
||||
"load_more": "加载更多",
|
||||
"users-found-search-took": "找到 %1 位用户!耗时 %2 毫秒。",
|
||||
"users-found-search-took": "找到 %1 位用户!耗时 %2 秒。",
|
||||
"filter-by": "过滤选项",
|
||||
"online-only": "只看在线",
|
||||
"invite": "邀请注册",
|
||||
|
@@ -241,22 +241,29 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
|
||||
uploadModal.find('.upload-btn').on('click', function () {
|
||||
var url = uploadModal.find('#uploadFromUrl').val();
|
||||
if (!url) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
uploadModal.modal('hide');
|
||||
|
||||
pictureCropper.handleImageCrop({
|
||||
socket.emit('user.uploadProfileImageFromUrl', {
|
||||
uid: ajaxify.data.uid,
|
||||
url: url,
|
||||
socketMethod: 'user.uploadCroppedPicture',
|
||||
aspectRatio: '1 / 1',
|
||||
allowSkippingCrop: false,
|
||||
restrictImageDimension: true,
|
||||
imageDimension: ajaxify.data.profileImageDimension,
|
||||
paramName: 'uid',
|
||||
paramValue: ajaxify.data.theirid,
|
||||
}, onUploadComplete);
|
||||
}, function (err, url) {
|
||||
if (err) {
|
||||
return app.alertError(err);
|
||||
}
|
||||
|
||||
uploadModal.modal('hide');
|
||||
|
||||
pictureCropper.handleImageCrop({
|
||||
url: url,
|
||||
socketMethod: 'user.uploadCroppedPicture',
|
||||
aspectRatio: '1 / 1',
|
||||
allowSkippingCrop: false,
|
||||
restrictImageDimension: true,
|
||||
imageDimension: ajaxify.data.profileImageDimension,
|
||||
paramName: 'uid',
|
||||
paramValue: ajaxify.data.theirid,
|
||||
}, onUploadComplete);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
@@ -4,6 +4,12 @@
|
||||
define('forum/account/settings', ['forum/account/header', 'components', 'sounds'], function (header, components, sounds) {
|
||||
var AccountSettings = {};
|
||||
|
||||
$(window).on('action:ajaxify.start', function () {
|
||||
if (ajaxify.data.template.name === 'account/settings' && $('#bootswatchSkin').val() !== config.bootswatchSkin) {
|
||||
changePageSkin(config.bootswatchSkin);
|
||||
}
|
||||
});
|
||||
|
||||
AccountSettings.init = function () {
|
||||
header.init();
|
||||
|
||||
@@ -24,10 +30,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'sounds'
|
||||
});
|
||||
|
||||
$('#bootswatchSkin').on('change', function () {
|
||||
var css = $('#bootswatchCSS');
|
||||
var val = $(this).val() === 'default' ? config['theme:src'] : '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + $(this).val() + '/bootstrap.min.css';
|
||||
|
||||
css.attr('href', val);
|
||||
changePageSkin($(this).val());
|
||||
});
|
||||
|
||||
$('[data-property="homePageRoute"]').on('change', toggleCustomRoute);
|
||||
@@ -44,6 +47,26 @@ define('forum/account/settings', ['forum/account/header', 'components', 'sounds'
|
||||
components.get('user/sessions').find('.timeago').timeago();
|
||||
};
|
||||
|
||||
function changePageSkin(skinName) {
|
||||
var css = $('#bootswatchCSS');
|
||||
if (skinName === 'default') {
|
||||
css.remove();
|
||||
} else {
|
||||
var cssSource = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + skinName + '/bootstrap.min.css';
|
||||
if (css.length) {
|
||||
css.attr('href', cssSource);
|
||||
} else {
|
||||
css = $('<link id="bootswatchCSS" href="' + cssSource + '" rel="stylesheet" media="screen">');
|
||||
$('head').append(css);
|
||||
}
|
||||
}
|
||||
|
||||
var currentSkinClassName = $('body').attr('class').split(/\s+/).filter(function (className) {
|
||||
return className.startsWith('skin-');
|
||||
});
|
||||
$('body').removeClass(currentSkinClassName.join(' ')).addClass('skin-' + skinName);
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
var settings = {};
|
||||
|
||||
|
@@ -7,11 +7,12 @@ define('forum/topic', [
|
||||
'forum/topic/postTools',
|
||||
'forum/topic/events',
|
||||
'forum/topic/posts',
|
||||
'forum/topic/images',
|
||||
'forum/topic/replies',
|
||||
'navigator',
|
||||
'sort',
|
||||
'components',
|
||||
], function (infinitescroll, threadTools, postTools, events, posts, replies, navigator, sort, components) {
|
||||
], function (infinitescroll, threadTools, postTools, events, posts, images, replies, navigator, sort, components) {
|
||||
var Topic = {};
|
||||
var currentUrl = '';
|
||||
|
||||
@@ -238,7 +239,7 @@ define('forum/topic', [
|
||||
return;
|
||||
}
|
||||
|
||||
posts.loadImages(threshold);
|
||||
images.loadImages(threshold);
|
||||
|
||||
var newUrl = 'topic/' + ajaxify.data.slug + (index > 1 ? ('/' + index) : '');
|
||||
|
||||
|
@@ -6,9 +6,10 @@ define('forum/topic/events', [
|
||||
'forum/topic/postTools',
|
||||
'forum/topic/threadTools',
|
||||
'forum/topic/posts',
|
||||
'forum/topic/images',
|
||||
'components',
|
||||
'translator',
|
||||
], function (postTools, threadTools, posts, components, translator) {
|
||||
], function (postTools, threadTools, posts, images, components, translator) {
|
||||
var Events = {};
|
||||
|
||||
var events = {
|
||||
@@ -128,9 +129,9 @@ define('forum/topic/events', [
|
||||
editedPostEl.html(translator.unescape(data.post.content));
|
||||
editedPostEl.find('img:not(.not-responsive)').addClass('img-responsive');
|
||||
app.replaceSelfLinks(editedPostEl.find('a'));
|
||||
posts.wrapImagesInLinks(editedPostEl.parent());
|
||||
posts.unloadImages(editedPostEl.parent());
|
||||
posts.loadImages();
|
||||
images.wrapImagesInLinks(editedPostEl.parent());
|
||||
images.unloadImages(editedPostEl.parent());
|
||||
images.loadImages();
|
||||
editedPostEl.fadeIn(250);
|
||||
|
||||
var editData = {
|
||||
|
119
public/src/client/topic/images.js
Normal file
119
public/src/client/topic/images.js
Normal file
@@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
define('forum/topic/images', [
|
||||
'forum/topic/postTools',
|
||||
'navigator',
|
||||
'components',
|
||||
], function (postTools, navigator, components) {
|
||||
var Images = {
|
||||
_imageLoaderTimeout: undefined,
|
||||
};
|
||||
|
||||
Images.unloadImages = function (posts) {
|
||||
var images = posts.find('[component="post/content"] img:not(.not-responsive)');
|
||||
|
||||
if (config.delayImageLoading) {
|
||||
images.each(function () {
|
||||
$(this).attr('data-src', $(this).attr('src'));
|
||||
}).attr('data-state', 'unloaded').attr('src', 'about:blank');
|
||||
} else {
|
||||
images.attr('data-state', 'loaded');
|
||||
Images.wrapImagesInLinks(posts);
|
||||
}
|
||||
};
|
||||
|
||||
Images.loadImages = function (threshold) {
|
||||
if (Images._imageLoaderTimeout) {
|
||||
clearTimeout(Images._imageLoaderTimeout);
|
||||
}
|
||||
|
||||
Images._imageLoaderTimeout = setTimeout(function () {
|
||||
/*
|
||||
If threshold is defined, images loaded above this threshold will modify
|
||||
the user's scroll position so they are not scrolled away from content
|
||||
they were reading. Images loaded below this threshold will push down content.
|
||||
|
||||
If no threshold is defined, loaded images will push down content, as per
|
||||
default
|
||||
*/
|
||||
|
||||
var images = components.get('post/content').find('img[data-state="unloaded"]');
|
||||
var visible = images.filter(function () {
|
||||
return utils.isElementInViewport(this);
|
||||
});
|
||||
var posts = $.unique(visible.map(function () {
|
||||
return $(this).parents('[component="post"]').get(0);
|
||||
}));
|
||||
var scrollTop = $(window).scrollTop();
|
||||
var adjusting = false;
|
||||
var adjustQueue = [];
|
||||
var oldHeight;
|
||||
var newHeight;
|
||||
|
||||
function adjustPosition() {
|
||||
adjusting = true;
|
||||
oldHeight = document.body.clientHeight;
|
||||
|
||||
// Display the image
|
||||
$(this).attr('data-state', 'loaded');
|
||||
newHeight = document.body.clientHeight;
|
||||
|
||||
var imageRect = this.getBoundingClientRect();
|
||||
if (imageRect.top < threshold) {
|
||||
scrollTop += newHeight - oldHeight;
|
||||
$(window).scrollTop(scrollTop);
|
||||
}
|
||||
|
||||
if (adjustQueue.length) {
|
||||
adjustQueue.pop()();
|
||||
} else {
|
||||
adjusting = false;
|
||||
|
||||
Images.wrapImagesInLinks(posts);
|
||||
posts.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// For each image, reset the source and adjust scrollTop when loaded
|
||||
visible.attr('data-state', 'loading');
|
||||
visible.each(function (index, image) {
|
||||
image = $(image);
|
||||
|
||||
image.on('load', function () {
|
||||
if (!adjusting) {
|
||||
adjustPosition.call(this);
|
||||
} else {
|
||||
adjustQueue.push(adjustPosition.bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
image.attr('src', image.attr('data-src'));
|
||||
image.removeAttr('data-src');
|
||||
});
|
||||
}, 250);
|
||||
};
|
||||
|
||||
Images.wrapImagesInLinks = function (posts) {
|
||||
posts.find('[component="post/content"] img:not(.emoji)').each(function () {
|
||||
var $this = $(this);
|
||||
var src = $this.attr('src');
|
||||
var suffixRegex = /-resized(\.[\w]+)?$/;
|
||||
|
||||
if (src === 'about:blank') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.isRelativeUrl(src) && suffixRegex.test(src)) {
|
||||
src = src.replace(suffixRegex, '$1');
|
||||
}
|
||||
|
||||
if (!$this.parent().is('a')) {
|
||||
$this.wrap('<a href="' + src + '" target="_blank">');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return Images;
|
||||
});
|
@@ -5,19 +5,14 @@ define('forum/topic/posts', [
|
||||
'forum/pagination',
|
||||
'forum/infinitescroll',
|
||||
'forum/topic/postTools',
|
||||
'forum/topic/images',
|
||||
'navigator',
|
||||
'components',
|
||||
], function (pagination, infinitescroll, postTools, navigator, components) {
|
||||
var Posts = {
|
||||
_imageLoaderTimeout: undefined,
|
||||
};
|
||||
], function (pagination, infinitescroll, postTools, images, navigator, components) {
|
||||
var Posts = { };
|
||||
|
||||
Posts.onNewPost = function (data) {
|
||||
if (!data || !data.posts || !data.posts.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parseInt(data.posts[0].tid, 10) !== parseInt(ajaxify.data.tid, 10)) {
|
||||
if (!data || !data.posts || !data.posts.length || parseInt(data.posts[0].tid, 10) !== parseInt(ajaxify.data.tid, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,7 +58,7 @@ define('forum/topic/posts', [
|
||||
function onNewPostPagination(data) {
|
||||
function scrollToPost() {
|
||||
scrollToPostIfSelf(data.posts[0]);
|
||||
Posts.loadImages();
|
||||
images.loadImages();
|
||||
}
|
||||
|
||||
var posts = data.posts;
|
||||
@@ -107,7 +102,7 @@ define('forum/topic/posts', [
|
||||
html.addClass('new');
|
||||
}
|
||||
scrollToPostIfSelf(data.posts[0]);
|
||||
Posts.loadImages();
|
||||
images.loadImages();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -247,7 +242,7 @@ define('forum/topic/posts', [
|
||||
};
|
||||
|
||||
Posts.processPage = function (posts) {
|
||||
Posts.unloadImages(posts);
|
||||
images.unloadImages(posts);
|
||||
Posts.showBottomPostBar();
|
||||
posts.find('[component="post/content"] img:not(.not-responsive)').addClass('img-responsive');
|
||||
app.createUserTooltips(posts);
|
||||
@@ -260,110 +255,6 @@ define('forum/topic/posts', [
|
||||
hidePostToolsForDeletedPosts(posts);
|
||||
};
|
||||
|
||||
Posts.unloadImages = function (posts) {
|
||||
var images = posts.find('[component="post/content"] img:not(.not-responsive)');
|
||||
|
||||
if (config.delayImageLoading) {
|
||||
images.each(function () {
|
||||
$(this).attr('data-src', $(this).attr('src'));
|
||||
}).attr('data-state', 'unloaded').attr('src', 'about:blank');
|
||||
} else {
|
||||
images.attr('data-state', 'loaded');
|
||||
Posts.wrapImagesInLinks(posts);
|
||||
}
|
||||
};
|
||||
|
||||
Posts.loadImages = function (threshold) {
|
||||
if (Posts._imageLoaderTimeout) {
|
||||
clearTimeout(Posts._imageLoaderTimeout);
|
||||
}
|
||||
|
||||
Posts._imageLoaderTimeout = setTimeout(function () {
|
||||
/*
|
||||
If threshold is defined, images loaded above this threshold will modify
|
||||
the user's scroll position so they are not scrolled away from content
|
||||
they were reading. Images loaded below this threshold will push down content.
|
||||
|
||||
If no threshold is defined, loaded images will push down content, as per
|
||||
default
|
||||
*/
|
||||
|
||||
var images = components.get('post/content').find('img[data-state="unloaded"]');
|
||||
var visible = images.filter(function () {
|
||||
return utils.isElementInViewport(this);
|
||||
});
|
||||
var posts = $.unique(visible.map(function () {
|
||||
return $(this).parents('[component="post"]').get(0);
|
||||
}));
|
||||
var scrollTop = $(window).scrollTop();
|
||||
var adjusting = false;
|
||||
var adjustQueue = [];
|
||||
var oldHeight;
|
||||
var newHeight;
|
||||
|
||||
function adjustPosition() {
|
||||
adjusting = true;
|
||||
oldHeight = document.body.clientHeight;
|
||||
|
||||
// Display the image
|
||||
$(this).attr('data-state', 'loaded');
|
||||
newHeight = document.body.clientHeight;
|
||||
|
||||
var imageRect = this.getBoundingClientRect();
|
||||
if (imageRect.top < threshold) {
|
||||
scrollTop += newHeight - oldHeight;
|
||||
$(window).scrollTop(scrollTop);
|
||||
}
|
||||
|
||||
if (adjustQueue.length) {
|
||||
adjustQueue.pop()();
|
||||
} else {
|
||||
adjusting = false;
|
||||
|
||||
Posts.wrapImagesInLinks(posts);
|
||||
posts.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// For each image, reset the source and adjust scrollTop when loaded
|
||||
visible.attr('data-state', 'loading');
|
||||
visible.each(function (index, image) {
|
||||
image = $(image);
|
||||
|
||||
image.on('load', function () {
|
||||
if (!adjusting) {
|
||||
adjustPosition.call(this);
|
||||
} else {
|
||||
adjustQueue.push(adjustPosition.bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
image.attr('src', image.attr('data-src'));
|
||||
image.removeAttr('data-src');
|
||||
});
|
||||
}, 250);
|
||||
};
|
||||
|
||||
Posts.wrapImagesInLinks = function (posts) {
|
||||
posts.find('[component="post/content"] img:not(.emoji)').each(function () {
|
||||
var $this = $(this);
|
||||
var src = $this.attr('src');
|
||||
var suffixRegex = /-resized(\.[\w]+)?$/;
|
||||
|
||||
if (src === 'about:blank') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.isRelativeUrl(src) && suffixRegex.test(src)) {
|
||||
src = src.replace(suffixRegex, '$1');
|
||||
}
|
||||
|
||||
if (!$this.parent().is('a')) {
|
||||
$this.wrap('<a href="' + src + '" target="_blank">');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Posts.showBottomPostBar = function () {
|
||||
var mainPost = components.get('post', 'index', 0);
|
||||
var placeHolder = $('.post-bar-placeholder');
|
||||
|
@@ -16,9 +16,9 @@
|
||||
return false;
|
||||
}
|
||||
var properties = item.properties;
|
||||
|
||||
var loggedIn = data.config ? data.config.loggedIn : false;
|
||||
if (properties) {
|
||||
if ((properties.loggedIn && !data.config.loggedIn) ||
|
||||
if ((properties.loggedIn && !loggedIn) ||
|
||||
(properties.globalMod && !data.isGlobalMod && !data.isAdmin) ||
|
||||
(properties.adminOnly && !data.isAdmin) ||
|
||||
(properties.searchInstalled && !data.searchEnabled)) {
|
||||
@@ -26,11 +26,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (item.route.match('/users') && data.privateUserInfo && !data.config.loggedIn) {
|
||||
if (item.route.match('/users') && data.privateUserInfo && !loggedIn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.route.match('/tags') && data.privateTagListing && !data.config.loggedIn) {
|
||||
if (item.route.match('/tags') && data.privateTagListing && !loggedIn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
47
src/controllers/404.js
Normal file
47
src/controllers/404.js
Normal file
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
var nconf = require('nconf');
|
||||
var winston = require('winston');
|
||||
var validator = require('validator');
|
||||
|
||||
var meta = require('../meta');
|
||||
var plugins = require('../plugins');
|
||||
|
||||
exports.handle404 = function (req, res) {
|
||||
var relativePath = nconf.get('relative_path');
|
||||
var isClientScript = new RegExp('^' + relativePath + '\\/assets\\/src\\/.+\\.js');
|
||||
|
||||
if (plugins.hasListeners('action:meta.override404')) {
|
||||
return plugins.fireHook('action:meta.override404', {
|
||||
req: req,
|
||||
res: res,
|
||||
error: {},
|
||||
});
|
||||
}
|
||||
|
||||
if (isClientScript.test(req.url)) {
|
||||
res.type('text/javascript').status(200).send('');
|
||||
} else if (req.path.startsWith(relativePath + '/assets/uploads') || (req.get('accept') && req.get('accept').indexOf('text/html') === -1) || req.path === '/favicon.ico') {
|
||||
meta.errors.log404(req.path || '');
|
||||
res.sendStatus(404);
|
||||
} else if (req.accepts('html')) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.warn('Route requested but not found: ' + req.url);
|
||||
}
|
||||
|
||||
meta.errors.log404(req.path.replace(/^\/api/, '') || '');
|
||||
res.status(404);
|
||||
|
||||
var path = String(req.path || '');
|
||||
|
||||
if (res.locals.isAPI) {
|
||||
return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
|
||||
}
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
|
||||
});
|
||||
} else {
|
||||
res.status(404).type('txt').send('Not found');
|
||||
}
|
||||
};
|
@@ -13,9 +13,8 @@ var privileges = require('../privileges');
|
||||
var plugins = require('../plugins');
|
||||
var widgets = require('../widgets');
|
||||
var translator = require('../../public/src/modules/translator');
|
||||
var accountHelpers = require('../controllers/accounts/helpers');
|
||||
|
||||
var apiController = {};
|
||||
var apiController = module.exports;
|
||||
|
||||
apiController.getConfig = function (req, res, next) {
|
||||
var config = {};
|
||||
@@ -62,7 +61,7 @@ apiController.getConfig = function (req, res, next) {
|
||||
config.categoryTopicSort = meta.config.categoryTopicSort || 'newest_to_oldest';
|
||||
config.csrf_token = req.csrfToken();
|
||||
config.searchEnabled = plugins.hasListeners('filter:search.query');
|
||||
config.bootswatchSkin = 'default';
|
||||
config.bootswatchSkin = meta.config.bootswatchSkin || 'default';
|
||||
|
||||
var timeagoCutoff = meta.config.timeagoCutoff === undefined ? 30 : meta.config.timeagoCutoff;
|
||||
config.timeagoCutoff = timeagoCutoff !== '' ? Math.max(0, parseInt(timeagoCutoff, 10)) : timeagoCutoff;
|
||||
@@ -220,92 +219,6 @@ apiController.getObject = function (req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
apiController.getCurrentUser = function (req, res, next) {
|
||||
if (!req.uid) {
|
||||
return res.status(401).json('not-authorized');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUserField(req.uid, 'userslug', next);
|
||||
},
|
||||
function (userslug, next) {
|
||||
accountHelpers.getUserDataByUserSlug(userslug, req.uid, next);
|
||||
},
|
||||
], function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.json(userData);
|
||||
});
|
||||
};
|
||||
|
||||
apiController.getUserByUID = function (req, res, next) {
|
||||
byType('uid', req, res, next);
|
||||
};
|
||||
|
||||
apiController.getUserByUsername = function (req, res, next) {
|
||||
byType('username', req, res, next);
|
||||
};
|
||||
|
||||
apiController.getUserByEmail = function (req, res, next) {
|
||||
byType('email', req, res, next);
|
||||
};
|
||||
|
||||
function byType(type, req, res, next) {
|
||||
apiController.getUserDataByField(req.uid, type, req.params[type], function (err, data) {
|
||||
if (err || !data) {
|
||||
return next(err);
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
}
|
||||
|
||||
apiController.getUserDataByField = function (callerUid, field, fieldValue, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (field === 'uid') {
|
||||
next(null, fieldValue);
|
||||
} else if (field === 'username') {
|
||||
user.getUidByUsername(fieldValue, next);
|
||||
} else if (field === 'email') {
|
||||
user.getUidByEmail(fieldValue, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
function (uid, next) {
|
||||
if (!uid) {
|
||||
return next();
|
||||
}
|
||||
apiController.getUserDataByUID(callerUid, uid, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
apiController.getUserDataByUID = function (callerUid, uid, callback) {
|
||||
if (!parseInt(callerUid, 10) && parseInt(meta.config.privateUserInfo, 10) === 1) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
if (!parseInt(uid, 10)) {
|
||||
return callback(new Error('[[error:no-user]]'));
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
userData: async.apply(user.getUserData, uid),
|
||||
settings: async.apply(user.getSettings, uid),
|
||||
}, function (err, results) {
|
||||
if (err || !results.userData) {
|
||||
return callback(err || new Error('[[error:no-user]]'));
|
||||
}
|
||||
|
||||
results.userData.email = results.settings.showemail ? results.userData.email : undefined;
|
||||
results.userData.fullname = results.settings.showfullname ? results.userData.fullname : undefined;
|
||||
|
||||
callback(null, results.userData);
|
||||
});
|
||||
};
|
||||
|
||||
apiController.getModerators = function (req, res, next) {
|
||||
categories.getModerators(req.params.cid, function (err, moderators) {
|
||||
if (err) {
|
||||
@@ -314,16 +227,3 @@ apiController.getModerators = function (req, res, next) {
|
||||
res.json({ moderators: moderators });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
apiController.getRecentPosts = function (req, res, next) {
|
||||
posts.getRecentPosts(req.uid, 0, 19, req.params.term, function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = apiController;
|
||||
|
@@ -13,9 +13,6 @@ categoriesController.list = function (req, res, next) {
|
||||
res.locals.metaTags = [{
|
||||
name: 'title',
|
||||
content: String(meta.config.title || 'NodeBB'),
|
||||
}, {
|
||||
name: 'description',
|
||||
content: String(meta.config.description || ''),
|
||||
}, {
|
||||
property: 'og:title',
|
||||
content: '[[pages:categories]]',
|
||||
|
63
src/controllers/errors.js
Normal file
63
src/controllers/errors.js
Normal file
@@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
var nconf = require('nconf');
|
||||
var winston = require('winston');
|
||||
var validator = require('validator');
|
||||
|
||||
exports.handleURIErrors = function (err, req, res, next) {
|
||||
// Handle cases where malformed URIs are passed in
|
||||
if (err instanceof URIError) {
|
||||
var tidMatch = req.path.match(/^\/topic\/(\d+)\//);
|
||||
var cidMatch = req.path.match(/^\/category\/(\d+)\//);
|
||||
|
||||
if (tidMatch) {
|
||||
res.redirect(nconf.get('relative_path') + tidMatch[0]);
|
||||
} else if (cidMatch) {
|
||||
res.redirect(nconf.get('relative_path') + cidMatch[0]);
|
||||
} else {
|
||||
winston.warn('[controller] Bad request: ' + req.path);
|
||||
if (res.locals.isAPI) {
|
||||
res.status(400).json({
|
||||
error: '[[global:400.title]]',
|
||||
});
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('400', { error: validator.escape(String(err.message)) });
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
// this needs to have four arguments or express treats it as `(req, res, next)`
|
||||
// don't remove `next`!
|
||||
exports.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars
|
||||
switch (err.code) {
|
||||
case 'EBADCSRFTOKEN':
|
||||
winston.error(req.path + '\n', err.message);
|
||||
return res.sendStatus(403);
|
||||
case 'blacklisted-ip':
|
||||
return res.status(403).type('text/plain').send(err.message);
|
||||
}
|
||||
|
||||
if (parseInt(err.status, 10) === 302 && err.path) {
|
||||
return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path);
|
||||
}
|
||||
|
||||
winston.error(req.path + '\n', err.stack);
|
||||
|
||||
res.status(err.status || 500);
|
||||
|
||||
var path = String(req.path || '');
|
||||
if (res.locals.isAPI) {
|
||||
res.json({ path: validator.escape(path), error: err.message });
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
|
||||
});
|
||||
}
|
||||
};
|
@@ -3,34 +3,35 @@
|
||||
var async = require('async');
|
||||
var nconf = require('nconf');
|
||||
var validator = require('validator');
|
||||
var winston = require('winston');
|
||||
|
||||
var meta = require('../meta');
|
||||
var user = require('../user');
|
||||
var plugins = require('../plugins');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
var Controllers = {
|
||||
topics: require('./topics'),
|
||||
posts: require('./posts'),
|
||||
categories: require('./categories'),
|
||||
category: require('./category'),
|
||||
unread: require('./unread'),
|
||||
recent: require('./recent'),
|
||||
popular: require('./popular'),
|
||||
tags: require('./tags'),
|
||||
search: require('./search'),
|
||||
users: require('./users'),
|
||||
groups: require('./groups'),
|
||||
accounts: require('./accounts'),
|
||||
authentication: require('./authentication'),
|
||||
api: require('./api'),
|
||||
admin: require('./admin'),
|
||||
globalMods: require('./globalmods'),
|
||||
mods: require('./mods'),
|
||||
sitemap: require('./sitemap'),
|
||||
};
|
||||
var Controllers = module.exports;
|
||||
|
||||
Controllers.topics = require('./topics');
|
||||
Controllers.posts = require('./posts');
|
||||
Controllers.categories = require('./categories');
|
||||
Controllers.category = require('./category');
|
||||
Controllers.unread = require('./unread');
|
||||
Controllers.recent = require('./recent');
|
||||
Controllers.popular = require('./popular');
|
||||
Controllers.tags = require('./tags');
|
||||
Controllers.search = require('./search');
|
||||
Controllers.user = require('./user');
|
||||
Controllers.users = require('./users');
|
||||
Controllers.groups = require('./groups');
|
||||
Controllers.accounts = require('./accounts');
|
||||
Controllers.authentication = require('./authentication');
|
||||
Controllers.api = require('./api');
|
||||
Controllers.admin = require('./admin');
|
||||
Controllers.globalMods = require('./globalmods');
|
||||
Controllers.mods = require('./mods');
|
||||
Controllers.sitemap = require('./sitemap');
|
||||
Controllers['404'] = require('./404');
|
||||
Controllers.errors = require('./errors');
|
||||
|
||||
Controllers.home = function (req, res, next) {
|
||||
var route = meta.config.homePageRoute || (meta.config.homePageCustom || '').replace(/^\/+/, '') || 'categories';
|
||||
@@ -321,19 +322,18 @@ Controllers.manifest = function (req, res) {
|
||||
res.status(200).json(manifest);
|
||||
};
|
||||
|
||||
Controllers.outgoing = function (req, res) {
|
||||
Controllers.outgoing = function (req, res, next) {
|
||||
var url = req.query.url || '';
|
||||
var data = {
|
||||
|
||||
if (!url) {
|
||||
return next();
|
||||
}
|
||||
|
||||
res.render('outgoing', {
|
||||
outgoing: validator.escape(String(url)),
|
||||
title: meta.config.title,
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[notifications:outgoing_link]]' }]),
|
||||
};
|
||||
|
||||
if (url) {
|
||||
res.render('outgoing', data);
|
||||
} else {
|
||||
res.status(404).redirect(nconf.get('relative_path') + '/404');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Controllers.termsOfUse = function (req, res, next) {
|
||||
@@ -346,102 +346,3 @@ Controllers.termsOfUse = function (req, res, next) {
|
||||
Controllers.ping = function (req, res) {
|
||||
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
|
||||
};
|
||||
|
||||
Controllers.handle404 = function (req, res) {
|
||||
var relativePath = nconf.get('relative_path');
|
||||
var isClientScript = new RegExp('^' + relativePath + '\\/assets\\/src\\/.+\\.js');
|
||||
|
||||
if (plugins.hasListeners('action:meta.override404')) {
|
||||
return plugins.fireHook('action:meta.override404', {
|
||||
req: req,
|
||||
res: res,
|
||||
error: {},
|
||||
});
|
||||
}
|
||||
|
||||
if (isClientScript.test(req.url)) {
|
||||
res.type('text/javascript').status(200).send('');
|
||||
} else if (req.path.startsWith(relativePath + '/assets/uploads') || (req.get('accept') && req.get('accept').indexOf('text/html') === -1) || req.path === '/favicon.ico') {
|
||||
meta.errors.log404(req.path || '');
|
||||
res.sendStatus(404);
|
||||
} else if (req.accepts('html')) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.warn('Route requested but not found: ' + req.url);
|
||||
}
|
||||
|
||||
meta.errors.log404(req.path.replace(/^\/api/, '') || '');
|
||||
res.status(404);
|
||||
|
||||
var path = String(req.path || '');
|
||||
|
||||
if (res.locals.isAPI) {
|
||||
return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
|
||||
}
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
|
||||
});
|
||||
} else {
|
||||
res.status(404).type('txt').send('Not found');
|
||||
}
|
||||
};
|
||||
|
||||
Controllers.handleURIErrors = function (err, req, res, next) {
|
||||
// Handle cases where malformed URIs are passed in
|
||||
if (err instanceof URIError) {
|
||||
var tidMatch = req.path.match(/^\/topic\/(\d+)\//);
|
||||
var cidMatch = req.path.match(/^\/category\/(\d+)\//);
|
||||
|
||||
if (tidMatch) {
|
||||
res.redirect(nconf.get('relative_path') + tidMatch[0]);
|
||||
} else if (cidMatch) {
|
||||
res.redirect(nconf.get('relative_path') + cidMatch[0]);
|
||||
} else {
|
||||
winston.warn('[controller] Bad request: ' + req.path);
|
||||
if (res.locals.isAPI) {
|
||||
res.status(400).json({
|
||||
error: '[[global:400.title]]',
|
||||
});
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('400', { error: validator.escape(String(err.message)) });
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
// this needs to have four arguments or express treats it as `(req, res, next)`
|
||||
// don't remove `next`!
|
||||
Controllers.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars
|
||||
switch (err.code) {
|
||||
case 'EBADCSRFTOKEN':
|
||||
winston.error(req.path + '\n', err.message);
|
||||
return res.sendStatus(403);
|
||||
case 'blacklisted-ip':
|
||||
return res.status(403).type('text/plain').send(err.message);
|
||||
}
|
||||
|
||||
if (parseInt(err.status, 10) === 302 && err.path) {
|
||||
return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path);
|
||||
}
|
||||
|
||||
winston.error(req.path + '\n', err.stack);
|
||||
|
||||
res.status(err.status || 500);
|
||||
|
||||
var path = String(req.path || '');
|
||||
if (res.locals.isAPI) {
|
||||
res.json({ path: validator.escape(path), error: err.message });
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Controllers;
|
||||
|
@@ -1,24 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var posts = require('../posts');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
var postsController = {};
|
||||
var postsController = module.exports;
|
||||
|
||||
postsController.redirectToPost = function (req, res, callback) {
|
||||
postsController.redirectToPost = function (req, res, next) {
|
||||
var pid = parseInt(req.params.pid, 10);
|
||||
if (!pid) {
|
||||
return callback();
|
||||
return next();
|
||||
}
|
||||
|
||||
posts.generatePostPath(pid, req.uid, function (err, path) {
|
||||
if (err || !path) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
helpers.redirect(res, path);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
posts.generatePostPath(pid, req.uid, next);
|
||||
},
|
||||
function (path, next) {
|
||||
if (!path) {
|
||||
return next();
|
||||
}
|
||||
helpers.redirect(res, path);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
|
||||
module.exports = postsController;
|
||||
postsController.getRecentPosts = function (req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
posts.getRecentPosts(req.uid, 0, 19, req.params.term, next);
|
||||
},
|
||||
function (data) {
|
||||
res.json(data);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
@@ -1,68 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var sitemap = require('../sitemap');
|
||||
var meta = require('../meta');
|
||||
|
||||
var sitemapController = {};
|
||||
sitemapController.render = function (req, res, next) {
|
||||
sitemap.render(function (err, tplData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var sitemapController = module.exports;
|
||||
|
||||
req.app.render('sitemap', tplData, function (err, xml) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
sitemapController.render = function (req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
sitemap.render(next);
|
||||
},
|
||||
function (tplData, next) {
|
||||
req.app.render('sitemap', tplData, next);
|
||||
},
|
||||
function (xml) {
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
});
|
||||
});
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
sitemapController.getPages = function (req, res, next) {
|
||||
if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
|
||||
return next();
|
||||
}
|
||||
|
||||
sitemap.getPages(function (err, xml) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
});
|
||||
sendSitemap(sitemap.getPages, res, next);
|
||||
};
|
||||
|
||||
sitemapController.getCategories = function (req, res, next) {
|
||||
if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
|
||||
return next();
|
||||
}
|
||||
|
||||
sitemap.getCategories(function (err, xml) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
});
|
||||
sendSitemap(sitemap.getCategories, res, next);
|
||||
};
|
||||
|
||||
sitemapController.getTopicPage = function (req, res, next) {
|
||||
if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
|
||||
return next();
|
||||
}
|
||||
|
||||
sitemap.getTopicPage(parseInt(req.params[0], 10), function (err, xml) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
} else if (!xml) {
|
||||
return next();
|
||||
}
|
||||
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
});
|
||||
sendSitemap(function (callback) {
|
||||
sitemap.getTopicPage(parseInt(req.params[0], 10), callback);
|
||||
}, res, next);
|
||||
};
|
||||
|
||||
module.exports = sitemapController;
|
||||
function sendSitemap(method, res, callback) {
|
||||
if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
|
||||
return callback();
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
method(next);
|
||||
},
|
||||
function (xml) {
|
||||
if (!xml) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
|
@@ -66,7 +66,7 @@ function uploadAsImage(req, uploadedFile, callback) {
|
||||
file.isFileTypeAllowed(uploadedFile.path, next);
|
||||
},
|
||||
function (next) {
|
||||
uploadFile(req.uid, uploadedFile, next);
|
||||
uploadsController.uploadFile(req.uid, uploadedFile, next);
|
||||
},
|
||||
function (fileObj, next) {
|
||||
if (parseInt(meta.config.maximumImageWidth, 10) === 0) {
|
||||
@@ -90,7 +90,7 @@ function uploadAsFile(req, uploadedFile, callback) {
|
||||
if (parseInt(meta.config.allowFileUploads, 10) !== 1) {
|
||||
return next(new Error('[[error:uploads-are-disabled]]'));
|
||||
}
|
||||
uploadFile(req.uid, uploadedFile, next);
|
||||
uploadsController.uploadFile(req.uid, uploadedFile, next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
@@ -161,7 +161,7 @@ uploadsController.uploadThumb = function (req, res, next) {
|
||||
}, next);
|
||||
}
|
||||
|
||||
uploadFile(req.uid, uploadedFile, next);
|
||||
uploadsController.uploadFile(req.uid, uploadedFile, next);
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
@@ -192,7 +192,7 @@ uploadsController.uploadGroupCover = function (uid, uploadedFile, callback) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
function uploadFile(uid, uploadedFile, callback) {
|
||||
uploadsController.uploadFile = function (uid, uploadedFile, callback) {
|
||||
if (plugins.hasListeners('filter:uploadFile')) {
|
||||
return plugins.fireHook('filter:uploadFile', {
|
||||
file: uploadedFile,
|
||||
@@ -217,7 +217,7 @@ function uploadFile(uid, uploadedFile, callback) {
|
||||
}
|
||||
|
||||
saveFileToLocal(uploadedFile, callback);
|
||||
}
|
||||
};
|
||||
|
||||
function saveFileToLocal(uploadedFile, callback) {
|
||||
var extension = file.typeToExtension(uploadedFile.type);
|
||||
|
99
src/controllers/user.js
Normal file
99
src/controllers/user.js
Normal file
@@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var user = require('../user');
|
||||
var meta = require('../meta');
|
||||
var accountHelpers = require('./accounts/helpers');
|
||||
|
||||
var userController = module.exports;
|
||||
|
||||
userController.getCurrentUser = function (req, res, next) {
|
||||
if (!req.uid) {
|
||||
return res.status(401).json('not-authorized');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUserField(req.uid, 'userslug', next);
|
||||
},
|
||||
function (userslug, next) {
|
||||
accountHelpers.getUserDataByUserSlug(userslug, req.uid, next);
|
||||
},
|
||||
function (userData) {
|
||||
res.json(userData);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
|
||||
userController.getUserByUID = function (req, res, next) {
|
||||
byType('uid', req, res, next);
|
||||
};
|
||||
|
||||
userController.getUserByUsername = function (req, res, next) {
|
||||
byType('username', req, res, next);
|
||||
};
|
||||
|
||||
userController.getUserByEmail = function (req, res, next) {
|
||||
byType('email', req, res, next);
|
||||
};
|
||||
|
||||
function byType(type, req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
userController.getUserDataByField(req.uid, type, req.params[type], next);
|
||||
},
|
||||
function (data, next) {
|
||||
if (!data) {
|
||||
return next();
|
||||
}
|
||||
res.json(data);
|
||||
},
|
||||
], next);
|
||||
}
|
||||
|
||||
userController.getUserDataByField = function (callerUid, field, fieldValue, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (field === 'uid') {
|
||||
next(null, fieldValue);
|
||||
} else if (field === 'username') {
|
||||
user.getUidByUsername(fieldValue, next);
|
||||
} else if (field === 'email') {
|
||||
user.getUidByEmail(fieldValue, next);
|
||||
} else {
|
||||
next(null, null);
|
||||
}
|
||||
},
|
||||
function (uid, next) {
|
||||
if (!uid) {
|
||||
return next(null, null);
|
||||
}
|
||||
userController.getUserDataByUID(callerUid, uid, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
userController.getUserDataByUID = function (callerUid, uid, callback) {
|
||||
if (!parseInt(callerUid, 10) && parseInt(meta.config.privateUserInfo, 10) === 1) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
if (!parseInt(uid, 10)) {
|
||||
return callback(new Error('[[error:no-user]]'));
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
userData: async.apply(user.getUserData, uid),
|
||||
settings: async.apply(user.getSettings, uid),
|
||||
}, function (err, results) {
|
||||
if (err || !results.userData) {
|
||||
return callback(err || new Error('[[error:no-user]]'));
|
||||
}
|
||||
|
||||
results.userData.email = results.settings.showemail ? results.userData.email : undefined;
|
||||
results.userData.fullname = results.settings.showfullname ? results.userData.fullname : undefined;
|
||||
|
||||
callback(null, results.userData);
|
||||
});
|
||||
};
|
@@ -8,9 +8,7 @@ var pagination = require('../pagination');
|
||||
var db = require('../database');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
|
||||
var usersController = {};
|
||||
|
||||
var usersController = module.exports;
|
||||
|
||||
usersController.index = function (req, res, next) {
|
||||
var section = req.query.section || 'joindate';
|
||||
@@ -33,62 +31,65 @@ usersController.index = function (req, res, next) {
|
||||
};
|
||||
|
||||
usersController.search = function (req, res, next) {
|
||||
async.parallel({
|
||||
search: function (next) {
|
||||
user.search({
|
||||
query: req.query.term,
|
||||
searchBy: req.query.searchBy || 'username',
|
||||
page: req.query.page || 1,
|
||||
sortBy: req.query.sortBy,
|
||||
onlineOnly: req.query.onlineOnly === 'true',
|
||||
bannedOnly: req.query.bannedOnly === 'true',
|
||||
flaggedOnly: req.query.flaggedOnly === 'true',
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
search: function (next) {
|
||||
user.search({
|
||||
query: req.query.term,
|
||||
searchBy: req.query.searchBy || 'username',
|
||||
page: req.query.page || 1,
|
||||
sortBy: req.query.sortBy,
|
||||
onlineOnly: req.query.onlineOnly === 'true',
|
||||
bannedOnly: req.query.bannedOnly === 'true',
|
||||
flaggedOnly: req.query.flaggedOnly === 'true',
|
||||
}, next);
|
||||
},
|
||||
isAdminOrGlobalMod: function (next) {
|
||||
user.isAdminOrGlobalMod(req.uid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
isAdminOrGlobalMod: function (next) {
|
||||
user.isAdminOrGlobalMod(req.uid, next);
|
||||
function (results, next) {
|
||||
var section = req.query.section || 'joindate';
|
||||
|
||||
results.search.isAdminOrGlobalMod = results.isAdminOrGlobalMod;
|
||||
results.search.pagination = pagination.create(req.query.page, results.search.pageCount, req.query);
|
||||
results.search['section_' + section] = true;
|
||||
render(req, res, results.search, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var section = req.query.section || 'joindate';
|
||||
|
||||
results.search.isAdminOrGlobalMod = results.isAdminOrGlobalMod;
|
||||
results.search.pagination = pagination.create(req.query.page, results.search.pageCount, req.query);
|
||||
results.search['section_' + section] = true;
|
||||
render(req, res, results.search, next);
|
||||
});
|
||||
], next);
|
||||
};
|
||||
|
||||
usersController.getOnlineUsers = function (req, res, next) {
|
||||
async.parallel({
|
||||
users: function (next) {
|
||||
usersController.getUsers('users:online', req.uid, req.query, next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
users: function (next) {
|
||||
usersController.getUsers('users:online', req.uid, req.query, next);
|
||||
},
|
||||
guests: function (next) {
|
||||
require('../socket.io/admin/rooms').getTotalGuestCount(next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
guests: function (next) {
|
||||
require('../socket.io/admin/rooms').getTotalGuestCount(next);
|
||||
function (results, next) {
|
||||
var userData = results.users;
|
||||
var hiddenCount = 0;
|
||||
if (!userData.isAdminOrGlobalMod) {
|
||||
userData.users = userData.users.filter(function (user) {
|
||||
if (user && user.status === 'offline') {
|
||||
hiddenCount += 1;
|
||||
}
|
||||
return user && user.status !== 'offline';
|
||||
});
|
||||
}
|
||||
|
||||
userData.anonymousUserCount = results.guests + hiddenCount;
|
||||
|
||||
render(req, res, userData, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var userData = results.users;
|
||||
var hiddenCount = 0;
|
||||
if (!userData.isAdminOrGlobalMod) {
|
||||
userData.users = userData.users.filter(function (user) {
|
||||
if (user && user.status === 'offline') {
|
||||
hiddenCount += 1;
|
||||
}
|
||||
return user && user.status !== 'offline';
|
||||
});
|
||||
}
|
||||
|
||||
userData.anonymousUserCount = results.guests + hiddenCount;
|
||||
|
||||
render(req, res, userData, next);
|
||||
});
|
||||
], next);
|
||||
};
|
||||
|
||||
usersController.getUsersSortedByPosts = function (req, res, next) {
|
||||
@@ -107,41 +108,36 @@ usersController.getUsersSortedByJoinDate = function (req, res, next) {
|
||||
};
|
||||
|
||||
usersController.getBannedUsers = function (req, res, next) {
|
||||
usersController.getUsers('users:banned', req.uid, req.query, function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!userData.isAdminOrGlobalMod) {
|
||||
return next();
|
||||
}
|
||||
|
||||
render(req, res, userData, next);
|
||||
});
|
||||
renderIfAdminOrGlobalMod('users:banned', req, res, next);
|
||||
};
|
||||
|
||||
usersController.getFlaggedUsers = function (req, res, next) {
|
||||
usersController.getUsers('users:flags', req.uid, req.query, function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!userData.isAdminOrGlobalMod) {
|
||||
return next();
|
||||
}
|
||||
|
||||
render(req, res, userData, next);
|
||||
});
|
||||
renderIfAdminOrGlobalMod('users:flags', req, res, next);
|
||||
};
|
||||
|
||||
usersController.renderUsersPage = function (set, req, res, next) {
|
||||
usersController.getUsers(set, req.uid, req.query, function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
function renderIfAdminOrGlobalMod(set, req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.isAdminOrGlobalMod(req.uid, next);
|
||||
},
|
||||
function (isAdminOrGlobalMod, next) {
|
||||
if (!isAdminOrGlobalMod) {
|
||||
return helpers.notAllowed(req, res);
|
||||
}
|
||||
usersController.renderUsersPage(set, req, res, next);
|
||||
},
|
||||
], next);
|
||||
}
|
||||
|
||||
render(req, res, userData, next);
|
||||
});
|
||||
usersController.renderUsersPage = function (set, req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
usersController.getUsers(set, req.uid, req.query, next);
|
||||
},
|
||||
function (userData, next) {
|
||||
render(req, res, userData, next);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
usersController.getUsers = function (set, uid, query, callback) {
|
||||
@@ -169,59 +165,62 @@ usersController.getUsers = function (set, uid, query, callback) {
|
||||
var start = Math.max(0, page - 1) * resultsPerPage;
|
||||
var stop = start + resultsPerPage - 1;
|
||||
|
||||
async.parallel({
|
||||
isAdminOrGlobalMod: function (next) {
|
||||
user.isAdminOrGlobalMod(uid, next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
isAdminOrGlobalMod: function (next) {
|
||||
user.isAdminOrGlobalMod(uid, next);
|
||||
},
|
||||
usersData: function (next) {
|
||||
usersController.getUsersAndCount(set, uid, start, stop, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
usersData: function (next) {
|
||||
usersController.getUsersAndCount(set, uid, start, stop, next);
|
||||
function (results, next) {
|
||||
var pageCount = Math.ceil(results.usersData.count / resultsPerPage);
|
||||
var userData = {
|
||||
users: results.usersData.users,
|
||||
pagination: pagination.create(page, pageCount, query),
|
||||
userCount: results.usersData.count,
|
||||
title: setToData[set].title || '[[pages:users/latest]]',
|
||||
breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
|
||||
isAdminOrGlobalMod: results.isAdminOrGlobalMod,
|
||||
};
|
||||
userData['section_' + (query.section || 'joindate')] = true;
|
||||
next(null, userData);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var pageCount = Math.ceil(results.usersData.count / resultsPerPage);
|
||||
var userData = {
|
||||
users: results.usersData.users,
|
||||
pagination: pagination.create(page, pageCount, query),
|
||||
userCount: results.usersData.count,
|
||||
title: setToData[set].title || '[[pages:users/latest]]',
|
||||
breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
|
||||
isAdminOrGlobalMod: results.isAdminOrGlobalMod,
|
||||
};
|
||||
userData['section_' + (query.section || 'joindate')] = true;
|
||||
callback(null, userData);
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
usersController.getUsersAndCount = function (set, uid, start, stop, callback) {
|
||||
async.parallel({
|
||||
users: function (next) {
|
||||
user.getUsersFromSet(set, uid, start, stop, next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
users: function (next) {
|
||||
user.getUsersFromSet(set, uid, start, stop, next);
|
||||
},
|
||||
count: function (next) {
|
||||
if (set === 'users:online') {
|
||||
var now = Date.now();
|
||||
db.sortedSetCount('users:online', now - 300000, '+inf', next);
|
||||
} else if (set === 'users:banned') {
|
||||
db.sortedSetCard('users:banned', next);
|
||||
} else if (set === 'users:flags') {
|
||||
db.sortedSetCard('users:flags', next);
|
||||
} else {
|
||||
db.getObjectField('global', 'userCount', next);
|
||||
}
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
count: function (next) {
|
||||
if (set === 'users:online') {
|
||||
var now = Date.now();
|
||||
db.sortedSetCount('users:online', now - 300000, '+inf', next);
|
||||
} else if (set === 'users:banned') {
|
||||
db.sortedSetCard('users:banned', next);
|
||||
} else if (set === 'users:flags') {
|
||||
db.sortedSetCard('users:flags', next);
|
||||
} else {
|
||||
db.getObjectField('global', 'userCount', next);
|
||||
}
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
results.users = results.users.filter(function (user) {
|
||||
return user && parseInt(user.uid, 10);
|
||||
});
|
||||
function (results, next) {
|
||||
results.users = results.users.filter(function (user) {
|
||||
return user && parseInt(user.uid, 10);
|
||||
});
|
||||
|
||||
callback(null, results);
|
||||
});
|
||||
next(null, results);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
function render(req, res, data, next) {
|
||||
@@ -232,16 +231,15 @@ function render(req, res, data, next) {
|
||||
data.adminInviteOnly = registrationType === 'admin-invite-only';
|
||||
data['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
|
||||
|
||||
user.getInvitesNumber(req.uid, function (err, numInvites) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getInvitesNumber(req.uid, next);
|
||||
},
|
||||
function (numInvites) {
|
||||
res.append('X-Total-Count', data.userCount);
|
||||
data.invites = numInvites;
|
||||
|
||||
res.append('X-Total-Count', data.userCount);
|
||||
data.invites = numInvites;
|
||||
|
||||
res.render('users', data);
|
||||
});
|
||||
res.render('users', data);
|
||||
},
|
||||
], next);
|
||||
}
|
||||
|
||||
module.exports = usersController;
|
||||
|
649
src/groups.js
649
src/groups.js
@@ -6,215 +6,197 @@ var validator = require('validator');
|
||||
var user = require('./user');
|
||||
var db = require('./database');
|
||||
var plugins = require('./plugins');
|
||||
var posts = require('./posts');
|
||||
var privileges = require('./privileges');
|
||||
var utils = require('../public/src/utils');
|
||||
|
||||
(function (Groups) {
|
||||
require('./groups/create')(Groups);
|
||||
require('./groups/delete')(Groups);
|
||||
require('./groups/update')(Groups);
|
||||
require('./groups/membership')(Groups);
|
||||
require('./groups/ownership')(Groups);
|
||||
require('./groups/search')(Groups);
|
||||
require('./groups/cover')(Groups);
|
||||
var Groups = module.exports;
|
||||
|
||||
var ephemeralGroups = ['guests'];
|
||||
require('./groups/data')(Groups);
|
||||
require('./groups/create')(Groups);
|
||||
require('./groups/delete')(Groups);
|
||||
require('./groups/update')(Groups);
|
||||
require('./groups/membership')(Groups);
|
||||
require('./groups/ownership')(Groups);
|
||||
require('./groups/search')(Groups);
|
||||
require('./groups/cover')(Groups);
|
||||
require('./groups/posts')(Groups);
|
||||
require('./groups/user')(Groups);
|
||||
|
||||
var internals = {
|
||||
getEphemeralGroup: function (groupName) {
|
||||
return {
|
||||
name: groupName,
|
||||
slug: utils.slugify(groupName),
|
||||
description: '',
|
||||
deleted: '0',
|
||||
hidden: '0',
|
||||
system: '1',
|
||||
};
|
||||
},
|
||||
removeEphemeralGroups: function (groups) {
|
||||
for (var x = groups.length; x >= 0; x -= 1) {
|
||||
if (ephemeralGroups.indexOf(groups[x]) !== -1) {
|
||||
groups.splice(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return groups;
|
||||
},
|
||||
Groups.ephemeralGroups = ['guests'];
|
||||
|
||||
Groups.getEphemeralGroup = function (groupName) {
|
||||
return {
|
||||
name: groupName,
|
||||
slug: utils.slugify(groupName),
|
||||
description: '',
|
||||
deleted: '0',
|
||||
hidden: '0',
|
||||
system: '1',
|
||||
};
|
||||
};
|
||||
|
||||
Groups.internals = internals;
|
||||
|
||||
var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/;
|
||||
Groups.isPrivilegeGroup = function (groupName) {
|
||||
return isPrivilegeGroupRegex.test(groupName);
|
||||
};
|
||||
|
||||
Groups.getEphemeralGroups = function () {
|
||||
return ephemeralGroups;
|
||||
};
|
||||
|
||||
Groups.getGroupsFromSet = function (set, uid, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (set === 'groups:visible:name') {
|
||||
db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next);
|
||||
} else {
|
||||
db.getSortedSetRevRange(set, start, stop, next);
|
||||
}
|
||||
},
|
||||
function (groupNames, next) {
|
||||
if (set === 'groups:visible:name') {
|
||||
groupNames = groupNames.map(function (name) {
|
||||
return name.split(':')[1];
|
||||
});
|
||||
}
|
||||
|
||||
Groups.getGroupsAndMembers(groupNames, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.getGroups = function (set, start, stop, callback) {
|
||||
db.getSortedSetRevRange(set, start, stop, callback);
|
||||
};
|
||||
|
||||
Groups.getGroupsAndMembers = function (groupNames, callback) {
|
||||
async.parallel({
|
||||
groups: function (next) {
|
||||
Groups.getGroupsData(groupNames, next);
|
||||
},
|
||||
members: function (next) {
|
||||
Groups.getMemberUsers(groupNames, 0, 3, next);
|
||||
},
|
||||
}, function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
data.groups.forEach(function (group, index) {
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
group.members = data.members[index] || [];
|
||||
group.truncated = group.memberCount > data.members.length;
|
||||
});
|
||||
|
||||
callback(null, data.groups);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.get = function (groupName, options, callback) {
|
||||
if (!groupName) {
|
||||
return callback(new Error('[[error:invalid-group]]'));
|
||||
Groups.removeEphemeralGroups = function (groups) {
|
||||
for (var x = groups.length; x >= 0; x -= 1) {
|
||||
if (Groups.ephemeralGroups.indexOf(groups[x]) !== -1) {
|
||||
groups.splice(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
var stop = -1;
|
||||
return groups;
|
||||
};
|
||||
|
||||
async.parallel({
|
||||
base: function (next) {
|
||||
db.getObject('group:' + groupName, next);
|
||||
},
|
||||
members: function (next) {
|
||||
if (options.truncateUserList) {
|
||||
stop = (parseInt(options.userListCount, 10) || 4) - 1;
|
||||
var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/;
|
||||
Groups.isPrivilegeGroup = function (groupName) {
|
||||
return isPrivilegeGroupRegex.test(groupName);
|
||||
};
|
||||
|
||||
Groups.getGroupsFromSet = function (set, uid, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (set === 'groups:visible:name') {
|
||||
db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next);
|
||||
} else {
|
||||
db.getSortedSetRevRange(set, start, stop, next);
|
||||
}
|
||||
},
|
||||
function (groupNames, next) {
|
||||
if (set === 'groups:visible:name') {
|
||||
groupNames = groupNames.map(function (name) {
|
||||
return name.split(':')[1];
|
||||
});
|
||||
}
|
||||
|
||||
Groups.getGroupsAndMembers(groupNames, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.getGroups = function (set, start, stop, callback) {
|
||||
db.getSortedSetRevRange(set, start, stop, callback);
|
||||
};
|
||||
|
||||
Groups.getGroupsAndMembers = function (groupNames, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
groups: function (next) {
|
||||
Groups.getGroupsData(groupNames, next);
|
||||
},
|
||||
members: function (next) {
|
||||
Groups.getMemberUsers(groupNames, 0, 3, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (data, next) {
|
||||
data.groups.forEach(function (group, index) {
|
||||
if (group) {
|
||||
group.members = data.members[index] || [];
|
||||
group.truncated = group.memberCount > data.members.length;
|
||||
}
|
||||
});
|
||||
next(null, data.groups);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next);
|
||||
},
|
||||
pending: function (next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSetMembers('group:' + groupName + ':pending', next);
|
||||
},
|
||||
function (uids, next) {
|
||||
user.getUsersData(uids, next);
|
||||
},
|
||||
], next);
|
||||
},
|
||||
invited: function (next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSetMembers('group:' + groupName + ':invited', next);
|
||||
},
|
||||
function (uids, next) {
|
||||
user.getUsersData(uids, next);
|
||||
},
|
||||
], next);
|
||||
},
|
||||
isMember: async.apply(Groups.isMember, options.uid, groupName),
|
||||
isPending: async.apply(Groups.isPending, options.uid, groupName),
|
||||
isInvited: async.apply(Groups.isInvited, options.uid, groupName),
|
||||
isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName),
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
} else if (!results.base) {
|
||||
Groups.get = function (groupName, options, callback) {
|
||||
if (!groupName) {
|
||||
return callback(new Error('[[error:invalid-group]]'));
|
||||
}
|
||||
|
||||
var stop = -1;
|
||||
|
||||
var results;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
base: function (next) {
|
||||
db.getObject('group:' + groupName, next);
|
||||
},
|
||||
members: function (next) {
|
||||
if (options.truncateUserList) {
|
||||
stop = (parseInt(options.userListCount, 10) || 4) - 1;
|
||||
}
|
||||
|
||||
Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next);
|
||||
},
|
||||
pending: function (next) {
|
||||
Groups.getUsersFromSet('group:' + groupName + ':pending', next);
|
||||
},
|
||||
invited: function (next) {
|
||||
Groups.getUsersFromSet('group:' + groupName + ':invited', next);
|
||||
},
|
||||
isMember: async.apply(Groups.isMember, options.uid, groupName),
|
||||
isPending: async.apply(Groups.isPending, options.uid, groupName),
|
||||
isInvited: async.apply(Groups.isInvited, options.uid, groupName),
|
||||
isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName),
|
||||
}, next);
|
||||
},
|
||||
function (_results, next) {
|
||||
results = _results;
|
||||
if (!results.base) {
|
||||
return callback(new Error('[[error:no-group]]'));
|
||||
}
|
||||
plugins.fireHook('filter:parse.raw', results.base.description, next);
|
||||
},
|
||||
function (descriptionParsed, next) {
|
||||
var groupData = results.base;
|
||||
Groups.escapeGroupData(groupData);
|
||||
|
||||
results.base['cover:url'] = results.base['cover:url'] || require('./coverPhoto').getDefaultGroupCover(groupName);
|
||||
results.base['cover:position'] = validator.escape(String(results.base['cover:position'] || '50% 50%'));
|
||||
results.base.labelColor = validator.escape(String(results.base.labelColor || '#000000'));
|
||||
results.base.icon = validator.escape(String(results.base.icon || ''));
|
||||
groupData.descriptionParsed = descriptionParsed;
|
||||
groupData.userTitleEnabled = groupData.userTitleEnabled ? !!parseInt(groupData.userTitleEnabled, 10) : true;
|
||||
groupData.createtimeISO = utils.toISOString(groupData.createtime);
|
||||
groupData.members = results.members;
|
||||
groupData.membersNextStart = stop + 1;
|
||||
groupData.pending = results.pending.filter(Boolean);
|
||||
groupData.invited = results.invited.filter(Boolean);
|
||||
groupData.deleted = !!parseInt(groupData.deleted, 10);
|
||||
groupData.hidden = !!parseInt(groupData.hidden, 10);
|
||||
groupData.system = !!parseInt(groupData.system, 10);
|
||||
groupData.memberCount = parseInt(groupData.memberCount, 10);
|
||||
groupData.private = (groupData.private === null || groupData.private === undefined) ? true : !!parseInt(groupData.private, 10);
|
||||
groupData.disableJoinRequests = parseInt(groupData.disableJoinRequests, 10) === 1;
|
||||
groupData.isMember = results.isMember;
|
||||
groupData.isPending = results.isPending;
|
||||
groupData.isInvited = results.isInvited;
|
||||
groupData.isOwner = results.isOwner;
|
||||
groupData['cover:url'] = groupData['cover:url'] || require('./coverPhoto').getDefaultGroupCover(groupName);
|
||||
groupData['cover:position'] = validator.escape(String(groupData['cover:position'] || '50% 50%'));
|
||||
groupData.labelColor = validator.escape(String(groupData.labelColor || '#000000'));
|
||||
groupData.icon = validator.escape(String(groupData.icon || ''));
|
||||
|
||||
plugins.fireHook('filter:parse.raw', results.base.description, function (err, descriptionParsed) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
plugins.fireHook('filter:group.get', { group: groupData }, next);
|
||||
},
|
||||
function (results, next) {
|
||||
next(null, results.group);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.escapeGroupData(results.base);
|
||||
|
||||
results.base.descriptionParsed = descriptionParsed;
|
||||
results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true;
|
||||
results.base.createtimeISO = utils.toISOString(results.base.createtime);
|
||||
results.base.members = results.members;
|
||||
results.base.membersNextStart = stop + 1;
|
||||
results.base.pending = results.pending.filter(Boolean);
|
||||
results.base.invited = results.invited.filter(Boolean);
|
||||
results.base.deleted = !!parseInt(results.base.deleted, 10);
|
||||
results.base.hidden = !!parseInt(results.base.hidden, 10);
|
||||
results.base.system = !!parseInt(results.base.system, 10);
|
||||
results.base.memberCount = parseInt(results.base.memberCount, 10);
|
||||
results.base.private = (results.base.private === null || results.base.private === undefined) ? true : !!parseInt(results.base.private, 10);
|
||||
results.base.disableJoinRequests = parseInt(results.base.disableJoinRequests, 10) === 1;
|
||||
results.base.isMember = results.isMember;
|
||||
results.base.isPending = results.isPending;
|
||||
results.base.isInvited = results.isInvited;
|
||||
results.base.isOwner = results.isOwner;
|
||||
|
||||
plugins.fireHook('filter:group.get', { group: results.base }, function (err, data) {
|
||||
callback(err, data ? data.group : null);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getOwners = function (groupName, callback) {
|
||||
db.getSetMembers('group:' + groupName + ':owners', callback);
|
||||
};
|
||||
|
||||
Groups.getOwnersAndMembers = function (groupName, uid, start, stop, callback) {
|
||||
async.parallel({
|
||||
owners: function (next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSetMembers('group:' + groupName + ':owners', next);
|
||||
},
|
||||
function (uids, next) {
|
||||
user.getUsers(uids, uid, next);
|
||||
},
|
||||
], next);
|
||||
},
|
||||
members: function (next) {
|
||||
user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
Groups.getOwners = function (groupName, callback) {
|
||||
db.getSetMembers('group:' + groupName + ':owners', callback);
|
||||
};
|
||||
|
||||
Groups.getOwnersAndMembers = function (groupName, uid, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
owners: function (next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSetMembers('group:' + groupName + ':owners', next);
|
||||
},
|
||||
function (uids, next) {
|
||||
user.getUsers(uids, uid, next);
|
||||
},
|
||||
], next);
|
||||
},
|
||||
members: function (next) {
|
||||
user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
var ownerUids = [];
|
||||
results.owners.forEach(function (user) {
|
||||
if (user) {
|
||||
@@ -228,225 +210,94 @@ var utils = require('../public/src/utils');
|
||||
});
|
||||
results.members = results.owners.concat(results.members);
|
||||
|
||||
callback(null, results.members);
|
||||
});
|
||||
};
|
||||
next(null, results.members);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.escapeGroupData = function (group) {
|
||||
if (group) {
|
||||
group.nameEncoded = encodeURIComponent(group.name);
|
||||
group.displayName = validator.escape(String(group.name));
|
||||
group.description = validator.escape(String(group.description || ''));
|
||||
group.userTitle = validator.escape(String(group.userTitle || '')) || group.displayName;
|
||||
}
|
||||
};
|
||||
Groups.escapeGroupData = function (group) {
|
||||
if (group) {
|
||||
group.nameEncoded = encodeURIComponent(group.name);
|
||||
group.displayName = validator.escape(String(group.name));
|
||||
group.description = validator.escape(String(group.description || ''));
|
||||
group.userTitle = validator.escape(String(group.userTitle || '')) || group.displayName;
|
||||
}
|
||||
};
|
||||
|
||||
Groups.getByGroupslug = function (slug, options, callback) {
|
||||
db.getObjectField('groupslug:groupname', slug, function (err, groupName) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
} else if (!groupName) {
|
||||
return callback(new Error('[[error:no-group]]'));
|
||||
Groups.getByGroupslug = function (slug, options, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObjectField('groupslug:groupname', slug, next);
|
||||
},
|
||||
function (groupName, next) {
|
||||
if (!groupName) {
|
||||
return next(new Error('[[error:no-group]]'));
|
||||
}
|
||||
Groups.get(groupName, options, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.get(groupName, options, callback);
|
||||
Groups.getGroupNameByGroupSlug = function (slug, callback) {
|
||||
db.getObjectField('groupslug:groupname', slug, callback);
|
||||
};
|
||||
|
||||
Groups.isPrivate = function (groupName, callback) {
|
||||
isFieldOn(groupName, 'private', callback);
|
||||
};
|
||||
|
||||
Groups.isHidden = function (groupName, callback) {
|
||||
isFieldOn(groupName, 'hidden', callback);
|
||||
};
|
||||
|
||||
function isFieldOn(groupName, field, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObjectField('group:' + groupName, field, next);
|
||||
},
|
||||
function (value, next) {
|
||||
next(null, parseInt(value, 10) === 1);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
Groups.exists = function (name, callback) {
|
||||
if (Array.isArray(name)) {
|
||||
var slugs = name.map(function (groupName) {
|
||||
return utils.slugify(groupName);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getGroupNameByGroupSlug = function (slug, callback) {
|
||||
db.getObjectField('groupslug:groupname', slug, callback);
|
||||
};
|
||||
|
||||
Groups.getGroupFields = function (groupName, fields, callback) {
|
||||
Groups.getMultipleGroupFields([groupName], fields, function (err, groups) {
|
||||
callback(err, groups ? groups[0] : null);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getMultipleGroupFields = function (groups, fields, callback) {
|
||||
db.getObjectsFields(groups.map(function (group) {
|
||||
return 'group:' + group;
|
||||
}), fields, callback);
|
||||
};
|
||||
|
||||
Groups.setGroupField = function (groupName, field, value, callback) {
|
||||
db.setObjectField('group:' + groupName, field, value, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
plugins.fireHook('action:group.set', { field: field, value: value, type: 'set' });
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
Groups.isPrivate = function (groupName, callback) {
|
||||
db.getObjectField('group:' + groupName, 'private', function (err, isPrivate) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, parseInt(isPrivate, 10) !== 0);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.isHidden = function (groupName, callback) {
|
||||
db.getObjectField('group:' + groupName, 'hidden', function (err, isHidden) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, parseInt(isHidden, 10) === 1);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.exists = function (name, callback) {
|
||||
if (Array.isArray(name)) {
|
||||
var slugs = name.map(function (groupName) {
|
||||
return utils.slugify(groupName);
|
||||
});
|
||||
async.parallel([
|
||||
function (next) {
|
||||
next(null, slugs.map(function (slug) {
|
||||
return ephemeralGroups.indexOf(slug) !== -1;
|
||||
}));
|
||||
},
|
||||
async.apply(db.isSortedSetMembers, 'groups:createtime', name),
|
||||
], function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, name.map(function (n, index) {
|
||||
return results[0][index] || results[1][index];
|
||||
async.parallel([
|
||||
function (next) {
|
||||
next(null, slugs.map(function (slug) {
|
||||
return Groups.ephemeralGroups.indexOf(slug) !== -1;
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
var slug = utils.slugify(name);
|
||||
async.parallel([
|
||||
function (next) {
|
||||
next(null, ephemeralGroups.indexOf(slug) !== -1);
|
||||
},
|
||||
async.apply(db.isSortedSetMember, 'groups:createtime', name),
|
||||
], function (err, results) {
|
||||
callback(err, !err ? (results[0] || results[1]) : null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Groups.existsBySlug = function (slug, callback) {
|
||||
if (Array.isArray(slug)) {
|
||||
db.isObjectFields('groupslug:groupname', slug, callback);
|
||||
} else {
|
||||
db.isObjectField('groupslug:groupname', slug, callback);
|
||||
}
|
||||
};
|
||||
|
||||
Groups.getLatestMemberPosts = function (groupName, max, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Groups.getMembers(groupName, 0, -1, next);
|
||||
},
|
||||
function (uids, next) {
|
||||
if (!Array.isArray(uids) || !uids.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
var keys = uids.map(function (uid) {
|
||||
return 'uid:' + uid + ':posts';
|
||||
});
|
||||
db.getSortedSetRevRange(keys, 0, max - 1, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
privileges.posts.filter('read', pids, uid, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.getGroupData = function (groupName, callback) {
|
||||
Groups.getGroupsData([groupName], function (err, groupsData) {
|
||||
callback(err, Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getGroupsData = function (groupNames, callback) {
|
||||
if (!Array.isArray(groupNames) || !groupNames.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
var keys = groupNames.map(function (groupName) {
|
||||
return 'group:' + groupName;
|
||||
});
|
||||
|
||||
var ephemeralIdx = groupNames.reduce(function (memo, cur, idx) {
|
||||
if (ephemeralGroups.indexOf(cur) !== -1) {
|
||||
memo.push(idx);
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
|
||||
db.getObjects(keys, function (err, groupData) {
|
||||
async.apply(db.isSortedSetMembers, 'groups:createtime', name),
|
||||
], function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (ephemeralIdx.length) {
|
||||
ephemeralIdx.forEach(function (idx) {
|
||||
groupData[idx] = internals.getEphemeralGroup(groupNames[idx]);
|
||||
});
|
||||
}
|
||||
|
||||
groupData.forEach(function (group) {
|
||||
if (group) {
|
||||
Groups.escapeGroupData(group);
|
||||
group.userTitleEnabled = group.userTitleEnabled ? parseInt(group.userTitleEnabled, 10) === 1 : true;
|
||||
group.labelColor = validator.escape(String(group.labelColor || '#000000'));
|
||||
group.icon = validator.escape(String(group.icon || ''));
|
||||
group.createtimeISO = utils.toISOString(group.createtime);
|
||||
group.hidden = parseInt(group.hidden, 10) === 1;
|
||||
group.system = parseInt(group.system, 10) === 1;
|
||||
group.private = (group.private === null || group.private === undefined) ? true : !!parseInt(group.private, 10);
|
||||
group.disableJoinRequests = parseInt(group.disableJoinRequests, 10) === 1;
|
||||
|
||||
group['cover:url'] = group['cover:url'] || require('./coverPhoto').getDefaultGroupCover(group.name);
|
||||
group['cover:thumb:url'] = group['cover:thumb:url'] || group['cover:url'];
|
||||
group['cover:position'] = validator.escape(String(group['cover:position'] || '50% 50%'));
|
||||
}
|
||||
});
|
||||
|
||||
plugins.fireHook('filter:groups.get', { groups: groupData }, function (err, data) {
|
||||
callback(err, data ? data.groups : null);
|
||||
});
|
||||
callback(null, name.map(function (n, index) {
|
||||
return results[0][index] || results[1][index];
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getUserGroups = function (uids, callback) {
|
||||
Groups.getUserGroupsFromSet('groups:visible:createtime', uids, callback);
|
||||
};
|
||||
|
||||
Groups.getUserGroupsFromSet = function (set, uids, callback) {
|
||||
async.waterfall([
|
||||
} else {
|
||||
var slug = utils.slugify(name);
|
||||
async.parallel([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange(set, 0, -1, next);
|
||||
next(null, Groups.ephemeralGroups.indexOf(slug) !== -1);
|
||||
},
|
||||
function (groupNames, next) {
|
||||
async.map(uids, function (uid, next) {
|
||||
Groups.isMemberOfGroups(uid, groupNames, function (err, isMembers) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.apply(db.isSortedSetMember, 'groups:createtime', name),
|
||||
], function (err, results) {
|
||||
callback(err, !err ? (results[0] || results[1]) : null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var memberOf = [];
|
||||
isMembers.forEach(function (isMember, index) {
|
||||
if (isMember) {
|
||||
memberOf.push(groupNames[index]);
|
||||
}
|
||||
});
|
||||
|
||||
Groups.getGroupsData(memberOf, next);
|
||||
});
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
}(module.exports));
|
||||
Groups.existsBySlug = function (slug, callback) {
|
||||
if (Array.isArray(slug)) {
|
||||
db.isObjectFields('groupslug:groupname', slug, callback);
|
||||
} else {
|
||||
db.isObjectField('groupslug:groupname', slug, callback);
|
||||
}
|
||||
};
|
||||
|
93
src/groups/data.js
Normal file
93
src/groups/data.js
Normal file
@@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var validator = require('validator');
|
||||
|
||||
var db = require('../database');
|
||||
var plugins = require('../plugins');
|
||||
var utils = require('../../public/src/utils');
|
||||
|
||||
module.exports = function (Groups) {
|
||||
Groups.getGroupsData = function (groupNames, callback) {
|
||||
if (!Array.isArray(groupNames) || !groupNames.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
var keys = groupNames.map(function (groupName) {
|
||||
return 'group:' + groupName;
|
||||
});
|
||||
|
||||
var ephemeralIdx = groupNames.reduce(function (memo, cur, idx) {
|
||||
if (Groups.ephemeralGroups.indexOf(cur) !== -1) {
|
||||
memo.push(idx);
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObjects(keys, next);
|
||||
},
|
||||
function (groupData, next) {
|
||||
if (ephemeralIdx.length) {
|
||||
ephemeralIdx.forEach(function (idx) {
|
||||
groupData[idx] = Groups.getEphemeralGroup(groupNames[idx]);
|
||||
});
|
||||
}
|
||||
|
||||
groupData.forEach(function (group) {
|
||||
if (group) {
|
||||
Groups.escapeGroupData(group);
|
||||
group.userTitleEnabled = group.userTitleEnabled ? parseInt(group.userTitleEnabled, 10) === 1 : true;
|
||||
group.labelColor = validator.escape(String(group.labelColor || '#000000'));
|
||||
group.icon = validator.escape(String(group.icon || ''));
|
||||
group.createtimeISO = utils.toISOString(group.createtime);
|
||||
group.hidden = parseInt(group.hidden, 10) === 1;
|
||||
group.system = parseInt(group.system, 10) === 1;
|
||||
group.private = (group.private === null || group.private === undefined) ? true : !!parseInt(group.private, 10);
|
||||
group.disableJoinRequests = parseInt(group.disableJoinRequests, 10) === 1;
|
||||
|
||||
group['cover:url'] = group['cover:url'] || require('../coverPhoto').getDefaultGroupCover(group.name);
|
||||
group['cover:thumb:url'] = group['cover:thumb:url'] || group['cover:url'];
|
||||
group['cover:position'] = validator.escape(String(group['cover:position'] || '50% 50%'));
|
||||
}
|
||||
});
|
||||
|
||||
plugins.fireHook('filter:groups.get', { groups: groupData }, next);
|
||||
},
|
||||
function (results, next) {
|
||||
next(null, results.groups);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.getGroupData = function (groupName, callback) {
|
||||
Groups.getGroupsData([groupName], function (err, groupsData) {
|
||||
callback(err, Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getGroupFields = function (groupName, fields, callback) {
|
||||
Groups.getMultipleGroupFields([groupName], fields, function (err, groups) {
|
||||
callback(err, groups ? groups[0] : null);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getMultipleGroupFields = function (groups, fields, callback) {
|
||||
db.getObjectsFields(groups.map(function (group) {
|
||||
return 'group:' + group;
|
||||
}), fields, callback);
|
||||
};
|
||||
|
||||
Groups.setGroupField = function (groupName, field, value, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.setObjectField('group:' + groupName, field, value, next);
|
||||
},
|
||||
function (next) {
|
||||
plugins.fireHook('action:group.set', { field: field, value: value, type: 'set' });
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
};
|
@@ -413,32 +413,33 @@ module.exports = function (Groups) {
|
||||
};
|
||||
|
||||
Groups.getMemberCount = function (groupName, callback) {
|
||||
db.getObjectField('group:' + groupName, 'memberCount', function (err, count) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, parseInt(count, 10));
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObjectField('group:' + groupName, 'memberCount', next);
|
||||
},
|
||||
function (count, next) {
|
||||
next(null, parseInt(count, 10));
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.isMemberOfGroupList = function (uid, groupListKey, callback) {
|
||||
db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function (err, groupNames) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
groupNames = Groups.internals.removeEphemeralGroups(groupNames);
|
||||
if (groupNames.length === 0) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
Groups.isMemberOfGroups(uid, groupNames, function (err, isMembers) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, next);
|
||||
},
|
||||
function (groupNames, next) {
|
||||
groupNames = Groups.removeEphemeralGroups(groupNames);
|
||||
if (groupNames.length === 0) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
callback(null, isMembers.indexOf(true) !== -1);
|
||||
});
|
||||
});
|
||||
Groups.isMemberOfGroups(uid, groupNames, next);
|
||||
},
|
||||
function (isMembers, next) {
|
||||
next(null, isMembers.indexOf(true) !== -1);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.isMemberOfGroupsList = function (uid, groupListKeys, callback) {
|
||||
@@ -446,19 +447,20 @@ module.exports = function (Groups) {
|
||||
return 'group:' + groupName + ':members';
|
||||
});
|
||||
|
||||
db.getSortedSetsMembers(sets, function (err, members) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var uniqueGroups = _.unique(_.flatten(members));
|
||||
uniqueGroups = Groups.internals.removeEphemeralGroups(uniqueGroups);
|
||||
|
||||
Groups.isMemberOfGroups(uid, uniqueGroups, function (err, isMembers) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var uniqueGroups;
|
||||
var members;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetsMembers(sets, next);
|
||||
},
|
||||
function (_members, next) {
|
||||
members = _members;
|
||||
uniqueGroups = _.unique(_.flatten(members));
|
||||
uniqueGroups = Groups.removeEphemeralGroups(uniqueGroups);
|
||||
|
||||
Groups.isMemberOfGroups(uid, uniqueGroups, next);
|
||||
},
|
||||
function (isMembers, next) {
|
||||
var map = {};
|
||||
|
||||
uniqueGroups.forEach(function (groupName, index) {
|
||||
@@ -474,62 +476,63 @@ module.exports = function (Groups) {
|
||||
return false;
|
||||
});
|
||||
|
||||
callback(null, result);
|
||||
});
|
||||
});
|
||||
next(null, result);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.isMembersOfGroupList = function (uids, groupListKey, callback) {
|
||||
db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function (err, groupNames) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var groupNames;
|
||||
var results = [];
|
||||
uids.forEach(function () {
|
||||
results.push(false);
|
||||
});
|
||||
|
||||
var results = [];
|
||||
uids.forEach(function () {
|
||||
results.push(false);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, next);
|
||||
},
|
||||
function (_groupNames, next) {
|
||||
groupNames = Groups.removeEphemeralGroups(_groupNames);
|
||||
|
||||
groupNames = Groups.internals.removeEphemeralGroups(groupNames);
|
||||
if (groupNames.length === 0) {
|
||||
return callback(null, results);
|
||||
}
|
||||
if (groupNames.length === 0) {
|
||||
return callback(null, results);
|
||||
}
|
||||
|
||||
async.each(groupNames, function (groupName, next) {
|
||||
Groups.isMembers(uids, groupName, function (err, isMembers) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.map(groupNames, function (groupName, next) {
|
||||
Groups.isMembers(uids, groupName, next);
|
||||
}, next);
|
||||
},
|
||||
function (isGroupMembers, next) {
|
||||
isGroupMembers.forEach(function (isMembers) {
|
||||
results.forEach(function (isMember, index) {
|
||||
if (!isMember && isMembers[index]) {
|
||||
results[index] = true;
|
||||
}
|
||||
});
|
||||
next();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
});
|
||||
next(null, results);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.isInvited = function (uid, groupName, callback) {
|
||||
if (!uid) {
|
||||
return callback(null, false);
|
||||
return setImmediate(callback, null, false);
|
||||
}
|
||||
db.isSetMember('group:' + groupName + ':invited', uid, callback);
|
||||
};
|
||||
|
||||
Groups.isPending = function (uid, groupName, callback) {
|
||||
if (!uid) {
|
||||
return callback(null, false);
|
||||
return setImmediate(callback, null, false);
|
||||
}
|
||||
db.isSetMember('group:' + groupName + ':pending', uid, callback);
|
||||
};
|
||||
|
||||
Groups.getPending = function (groupName, callback) {
|
||||
if (!groupName) {
|
||||
return callback(null, []);
|
||||
return setImmediate(callback, null, []);
|
||||
}
|
||||
db.getSetMembers('group:' + groupName + ':pending', callback);
|
||||
};
|
||||
|
32
src/groups/posts.js
Normal file
32
src/groups/posts.js
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var db = require('../database');
|
||||
var privileges = require('../privileges');
|
||||
var posts = require('../posts');
|
||||
|
||||
module.exports = function (Groups) {
|
||||
Groups.getLatestMemberPosts = function (groupName, max, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Groups.getMembers(groupName, 0, -1, next);
|
||||
},
|
||||
function (uids, next) {
|
||||
if (!Array.isArray(uids) || !uids.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
var keys = uids.map(function (uid) {
|
||||
return 'uid:' + uid + ':posts';
|
||||
});
|
||||
db.getSortedSetRevRange(keys, 0, max - 1, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
privileges.posts.filter('read', pids, uid, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
};
|
@@ -16,7 +16,7 @@ module.exports = function (Groups) {
|
||||
async.apply(db.getObjectValues, 'groupslug:groupname'),
|
||||
function (groupNames, next) {
|
||||
// Ephemeral groups and the registered-users groups are searchable
|
||||
groupNames = Groups.getEphemeralGroups().concat(groupNames).concat('registered-users');
|
||||
groupNames = Groups.ephemeralGroups.concat(groupNames).concat('registered-users');
|
||||
groupNames = groupNames.filter(function (name) {
|
||||
return name.toLowerCase().indexOf(query) !== -1 && name !== 'administrators' && !Groups.isPrivilegeGroup(name);
|
||||
});
|
||||
|
50
src/groups/user.js
Normal file
50
src/groups/user.js
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var db = require('../database');
|
||||
var user = require('../user');
|
||||
|
||||
module.exports = function (Groups) {
|
||||
Groups.getUsersFromSet = function (set, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSetMembers(set, next);
|
||||
},
|
||||
function (uids, next) {
|
||||
user.getUsersData(uids, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.getUserGroups = function (uids, callback) {
|
||||
Groups.getUserGroupsFromSet('groups:visible:createtime', uids, callback);
|
||||
};
|
||||
|
||||
Groups.getUserGroupsFromSet = function (set, uids, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange(set, 0, -1, next);
|
||||
},
|
||||
function (groupNames, next) {
|
||||
async.map(uids, function (uid, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Groups.isMemberOfGroups(uid, groupNames, next);
|
||||
},
|
||||
function (isMembers, next) {
|
||||
var memberOf = [];
|
||||
isMembers.forEach(function (isMember, index) {
|
||||
if (isMember) {
|
||||
memberOf.push(groupNames[index]);
|
||||
}
|
||||
});
|
||||
|
||||
Groups.getGroupsData(memberOf, next);
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
};
|
@@ -58,6 +58,7 @@ module.exports = function (Meta) {
|
||||
'public/src/client/topic/fork.js',
|
||||
'public/src/client/topic/move.js',
|
||||
'public/src/client/topic/posts.js',
|
||||
'public/src/client/topic/images.js',
|
||||
'public/src/client/topic/postTools.js',
|
||||
'public/src/client/topic/threadTools.js',
|
||||
'public/src/client/categories.js',
|
||||
|
@@ -131,10 +131,10 @@ module.exports = function (Meta) {
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasDescription) {
|
||||
if (!hasDescription && Meta.config.description) {
|
||||
meta.push({
|
||||
name: 'description',
|
||||
content: validator.escape(String(Meta.config.description || '')),
|
||||
content: validator.escape(String(Meta.config.description)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -114,7 +114,7 @@ module.exports = function (Meta) {
|
||||
themeData['theme:templates'] = config.templates ? config.templates : '';
|
||||
themeData['theme:src'] = '';
|
||||
|
||||
db.setObject('config', themeData, next);
|
||||
Meta.configs.setMultiple(themeData, next);
|
||||
|
||||
// Re-set the themes path (for when NodeBB is reloaded)
|
||||
Meta.themes.setPath(config);
|
||||
@@ -125,7 +125,10 @@ module.exports = function (Meta) {
|
||||
break;
|
||||
|
||||
case 'bootswatch':
|
||||
Meta.configs.set('theme:src', data.src, callback);
|
||||
Meta.configs.setMultiple({
|
||||
'theme:src': data.src,
|
||||
bootswatchSkin: data.id.toLowerCase(),
|
||||
}, callback);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
@@ -42,7 +42,6 @@ module.exports = function (middleware) {
|
||||
middleware.renderHeader = function (req, res, data, callback) {
|
||||
var registrationType = meta.config.registrationType || 'normal';
|
||||
var templateValues = {
|
||||
bootswatchCSS: meta.config['theme:src'],
|
||||
title: meta.config.title || '',
|
||||
description: meta.config.description || '',
|
||||
'cache-buster': meta.config['cache-buster'] || '',
|
||||
@@ -117,9 +116,7 @@ module.exports = function (middleware) {
|
||||
results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1;
|
||||
results.user.isEmailConfirmSent = !!results.isEmailConfirmSent;
|
||||
|
||||
if (res.locals.config && parseInt(meta.config.disableCustomUserSkins, 10) !== 1 && res.locals.config.bootswatchSkin !== 'default') {
|
||||
templateValues.bootswatchCSS = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + res.locals.config.bootswatchSkin + '/bootstrap.min.css';
|
||||
}
|
||||
setBootswatchCSS(templateValues, res.locals.config);
|
||||
|
||||
templateValues.browserTitle = controllers.helpers.buildTitle(data.title);
|
||||
templateValues.navigation = results.navigation;
|
||||
@@ -191,5 +188,21 @@ module.exports = function (middleware) {
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
function setBootswatchCSS(obj, config) {
|
||||
if (config && config.bootswatchSkin !== 'default') {
|
||||
var skinToUse = '';
|
||||
|
||||
if (parseInt(meta.config.disableCustomUserSkins, 10) !== 1) {
|
||||
skinToUse = config.bootswatchSkin;
|
||||
} else if (meta.config.bootswatchSkin !== 'default') {
|
||||
skinToUse = meta.config.bootswatchSkin;
|
||||
}
|
||||
|
||||
if (skinToUse) {
|
||||
obj.bootswatchCSS = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + skinToUse + '/bootstrap.min.css';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -53,22 +53,24 @@ middleware.ensureSelfOrGlobalPrivilege = function (req, res, next) {
|
||||
The "self" part of this middleware hinges on you having used
|
||||
middleware.exposeUid prior to invoking this middleware.
|
||||
*/
|
||||
if (req.user) {
|
||||
if (req.user.uid === res.locals.uid) {
|
||||
return next();
|
||||
}
|
||||
|
||||
user.isAdminOrGlobalMod(req.uid, function (err, ok) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
} else if (ok) {
|
||||
return next();
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (!req.uid) {
|
||||
return setImmediate(next, null, false);
|
||||
}
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
});
|
||||
} else {
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
if (req.uid === parseInt(res.locals.uid, 10)) {
|
||||
return setImmediate(next, null, true);
|
||||
}
|
||||
user.isAdminOrGlobalMod(req.uid, next);
|
||||
},
|
||||
function (isAdminOrGlobalMod, next) {
|
||||
if (!isAdminOrGlobalMod) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
next();
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
middleware.ensureSelfOrPrivileged = function (req, res, next) {
|
||||
|
@@ -96,7 +96,7 @@ module.exports = function (privileges) {
|
||||
return groupName.indexOf(':privileges:') === -1 && uniqueGroups.indexOf(groupName) !== -1;
|
||||
});
|
||||
|
||||
groupNames = groups.getEphemeralGroups().concat(groupNames);
|
||||
groupNames = groups.ephemeralGroups.concat(groupNames);
|
||||
var registeredUsersIndex = groupNames.indexOf('registered-users');
|
||||
if (registeredUsersIndex !== -1) {
|
||||
groupNames.splice(0, 0, groupNames.splice(registeredUsersIndex, 1)[0]);
|
||||
@@ -155,34 +155,36 @@ module.exports = function (privileges) {
|
||||
|
||||
privileges.categories.get = function (cid, uid, callback) {
|
||||
var privs = ['topics:create', 'topics:read', 'read'];
|
||||
async.parallel({
|
||||
privileges: function (next) {
|
||||
helpers.isUserAllowedTo(privs, uid, cid, next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
privileges: function (next) {
|
||||
helpers.isUserAllowedTo(privs, uid, cid, next);
|
||||
},
|
||||
isAdministrator: function (next) {
|
||||
user.isAdministrator(uid, next);
|
||||
},
|
||||
isModerator: function (next) {
|
||||
user.isModerator(uid, cid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
isAdministrator: function (next) {
|
||||
user.isAdministrator(uid, next);
|
||||
},
|
||||
isModerator: function (next) {
|
||||
user.isModerator(uid, cid, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var privData = _.object(privs, results.privileges);
|
||||
var isAdminOrMod = results.isAdministrator || results.isModerator;
|
||||
function (results, next) {
|
||||
var privData = _.object(privs, results.privileges);
|
||||
var isAdminOrMod = results.isAdministrator || results.isModerator;
|
||||
|
||||
plugins.fireHook('filter:privileges.categories.get', {
|
||||
'topics:create': privData['topics:create'] || isAdminOrMod,
|
||||
'topics:read': privData['topics:read'] || isAdminOrMod,
|
||||
read: privData.read || isAdminOrMod,
|
||||
cid: cid,
|
||||
uid: uid,
|
||||
editable: isAdminOrMod,
|
||||
view_deleted: isAdminOrMod,
|
||||
isAdminOrMod: isAdminOrMod,
|
||||
}, callback);
|
||||
});
|
||||
plugins.fireHook('filter:privileges.categories.get', {
|
||||
'topics:create': privData['topics:create'] || isAdminOrMod,
|
||||
'topics:read': privData['topics:read'] || isAdminOrMod,
|
||||
read: privData.read || isAdminOrMod,
|
||||
cid: cid,
|
||||
uid: uid,
|
||||
editable: isAdminOrMod,
|
||||
view_deleted: isAdminOrMod,
|
||||
isAdminOrMod: isAdminOrMod,
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
privileges.categories.isAdminOrMod = function (cid, uid, callback) {
|
||||
@@ -213,29 +215,29 @@ module.exports = function (privileges) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
categories.getCategoryField(cid, 'disabled', function (err, disabled) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (parseInt(disabled, 10) === 1) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
helpers.some([
|
||||
function (next) {
|
||||
helpers.isUserAllowedTo(privilege, uid, [cid], function (err, results) {
|
||||
next(err, Array.isArray(results) && results.length ? results[0] : false);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
user.isModerator(uid, cid, next);
|
||||
},
|
||||
function (next) {
|
||||
user.isAdministrator(uid, next);
|
||||
},
|
||||
], callback);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
categories.getCategoryField(cid, 'disabled', next);
|
||||
},
|
||||
function (disabled, next) {
|
||||
if (parseInt(disabled, 10) === 1) {
|
||||
return callback(null, false);
|
||||
}
|
||||
helpers.some([
|
||||
function (next) {
|
||||
helpers.isUserAllowedTo(privilege, uid, [cid], function (err, results) {
|
||||
next(err, Array.isArray(results) && results.length ? results[0] : false);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
user.isModerator(uid, cid, next);
|
||||
},
|
||||
function (next) {
|
||||
user.isAdministrator(uid, next);
|
||||
},
|
||||
], next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
privileges.categories.filterCids = function (privilege, cids, uid, callback) {
|
||||
@@ -247,18 +249,19 @@ module.exports = function (privileges) {
|
||||
return array.indexOf(cid) === index;
|
||||
});
|
||||
|
||||
privileges.categories.getBase(privilege, cids, uid, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.categories.getBase(privilege, cids, uid, next);
|
||||
},
|
||||
function (results, next) {
|
||||
cids = cids.filter(function (cid, index) {
|
||||
return !results.categories[index].disabled &&
|
||||
(results.allowedTo[index] || results.isAdmin || results.isModerators[index]);
|
||||
});
|
||||
|
||||
cids = cids.filter(function (cid, index) {
|
||||
return !results.categories[index].disabled &&
|
||||
(results.allowedTo[index] || results.isAdmin || results.isModerators[index]);
|
||||
});
|
||||
|
||||
callback(null, cids.filter(Boolean));
|
||||
});
|
||||
next(null, cids.filter(Boolean));
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
privileges.categories.getBase = function (privilege, cids, uid, callback) {
|
||||
@@ -287,26 +290,27 @@ module.exports = function (privileges) {
|
||||
return array.indexOf(uid) === index;
|
||||
});
|
||||
|
||||
async.parallel({
|
||||
allowedTo: function (next) {
|
||||
helpers.isUsersAllowedTo(privilege, uids, cid, next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
allowedTo: function (next) {
|
||||
helpers.isUsersAllowedTo(privilege, uids, cid, next);
|
||||
},
|
||||
isModerators: function (next) {
|
||||
user.isModerator(uids, cid, next);
|
||||
},
|
||||
isAdmin: function (next) {
|
||||
user.isAdministrator(uids, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
isModerators: function (next) {
|
||||
user.isModerator(uids, cid, next);
|
||||
function (results, next) {
|
||||
uids = uids.filter(function (uid, index) {
|
||||
return results.allowedTo[index] || results.isModerators[index] || results.isAdmin[index];
|
||||
});
|
||||
next(null, uids);
|
||||
},
|
||||
isAdmin: function (next) {
|
||||
user.isAdministrator(uids, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
uids = uids.filter(function (uid, index) {
|
||||
return results.allowedTo[index] || results.isModerators[index] || results.isAdmin[index];
|
||||
});
|
||||
callback(null, uids);
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
privileges.categories.give = function (privileges, cid, groupName, callback) {
|
||||
@@ -324,23 +328,24 @@ module.exports = function (privileges) {
|
||||
}
|
||||
|
||||
privileges.categories.canMoveAllTopics = function (currentCid, targetCid, uid, callback) {
|
||||
async.parallel({
|
||||
isAdministrator: function (next) {
|
||||
user.isAdministrator(uid, next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
isAdministrator: function (next) {
|
||||
user.isAdministrator(uid, next);
|
||||
},
|
||||
moderatorOfCurrent: function (next) {
|
||||
user.isModerator(uid, currentCid, next);
|
||||
},
|
||||
moderatorOfTarget: function (next) {
|
||||
user.isModerator(uid, targetCid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
moderatorOfCurrent: function (next) {
|
||||
user.isModerator(uid, currentCid, next);
|
||||
function (results, next) {
|
||||
next(null, results.isAdministrator || (results.moderatorOfCurrent && results.moderatorOfTarget));
|
||||
},
|
||||
moderatorOfTarget: function (next) {
|
||||
user.isModerator(uid, targetCid, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, results.isAdministrator || (results.moderatorOfCurrent && results.moderatorOfTarget));
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
privileges.categories.userPrivileges = function (cid, uid, callback) {
|
||||
|
@@ -11,17 +11,17 @@ module.exports = function (app, middleware, controllers) {
|
||||
router.get('/config', middleware.applyCSRF, controllers.api.getConfig);
|
||||
router.get('/widgets/render', controllers.api.renderWidgets);
|
||||
|
||||
router.get('/me', middleware.checkGlobalPrivacySettings, controllers.api.getCurrentUser);
|
||||
router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.api.getUserByUID);
|
||||
router.get('/user/username/:username', middleware.checkGlobalPrivacySettings, controllers.api.getUserByUsername);
|
||||
router.get('/user/email/:email', middleware.checkGlobalPrivacySettings, controllers.api.getUserByEmail);
|
||||
router.get('/me', middleware.checkGlobalPrivacySettings, controllers.user.getCurrentUser);
|
||||
router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUID);
|
||||
router.get('/user/username/:username', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUsername);
|
||||
router.get('/user/email/:email', middleware.checkGlobalPrivacySettings, controllers.user.getUserByEmail);
|
||||
|
||||
router.get('/:type/pid/:id', controllers.api.getObject);
|
||||
router.get('/:type/tid/:id', controllers.api.getObject);
|
||||
router.get('/:type/cid/:id', controllers.api.getObject);
|
||||
|
||||
router.get('/categories/:cid/moderators', controllers.api.getModerators);
|
||||
router.get('/recent/posts/:term?', controllers.api.getRecentPosts);
|
||||
router.get('/recent/posts/:term?', controllers.posts.getRecentPosts);
|
||||
router.get('/unread/:filter?/total', middleware.authenticate, controllers.unread.unreadTotal);
|
||||
router.get('/topic/teaser/:topic_id', controllers.topics.teaser);
|
||||
router.get('/topic/pagination/:topic_id', controllers.topics.pagination);
|
||||
|
@@ -199,9 +199,9 @@ module.exports = function (app, middleware, hotswapIds) {
|
||||
});
|
||||
|
||||
app.use(relativePath + '/assets/vendor/jquery/timeago/locales', middleware.processTimeagoLocales);
|
||||
app.use(controllers.handle404);
|
||||
app.use(controllers.handleURIErrors);
|
||||
app.use(controllers.handleErrors);
|
||||
app.use(controllers['404'].handle404);
|
||||
app.use(controllers.errors.handleURIErrors);
|
||||
app.use(controllers.errors.handleErrors);
|
||||
|
||||
// Add plugin routes
|
||||
async.series([
|
||||
|
@@ -1,12 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var topics = require('../topics');
|
||||
var websockets = require('./index');
|
||||
var user = require('../user');
|
||||
var apiController = require('../controllers/api');
|
||||
var socketHelpers = require('./helpers');
|
||||
|
||||
var SocketTopics = {};
|
||||
var SocketTopics = module.exports;
|
||||
|
||||
require('./topics/unread')(SocketTopics);
|
||||
require('./topics/move')(SocketTopics);
|
||||
@@ -23,18 +25,19 @@ SocketTopics.post = function (socket, data, callback) {
|
||||
data.req = websockets.reqFromSocket(socket);
|
||||
data.timestamp = Date.now();
|
||||
|
||||
topics.post(data, function (err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.post(data, next);
|
||||
},
|
||||
function (result, next) {
|
||||
next(null, result.topicData);
|
||||
|
||||
callback(null, result.topicData);
|
||||
socket.emit('event:new_post', { posts: [result.postData] });
|
||||
socket.emit('event:new_topic', result.topicData);
|
||||
|
||||
socket.emit('event:new_post', { posts: [result.postData] });
|
||||
socket.emit('event:new_topic', result.topicData);
|
||||
|
||||
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
||||
});
|
||||
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketTopics.postcount = function (socket, tid, callback) {
|
||||
@@ -61,7 +64,7 @@ SocketTopics.createTopicFromPosts = function (socket, data, callback) {
|
||||
};
|
||||
|
||||
SocketTopics.changeWatching = function (socket, data, callback) {
|
||||
if (!data.tid || !data.type) {
|
||||
if (!data || !data.tid || !data.type) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
var commands = ['follow', 'unfollow', 'ignore'];
|
||||
@@ -90,20 +93,23 @@ SocketTopics.isFollowed = function (socket, tid, callback) {
|
||||
};
|
||||
|
||||
SocketTopics.search = function (socket, data, callback) {
|
||||
if (!data) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
topics.search(data.tid, data.term, callback);
|
||||
};
|
||||
|
||||
SocketTopics.isModerator = function (socket, tid, callback) {
|
||||
topics.getTopicField(tid, 'cid', function (err, cid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
user.isModerator(socket.uid, cid, callback);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.getTopicField(tid, 'cid', next);
|
||||
},
|
||||
function (cid, next) {
|
||||
user.isModerator(socket.uid, cid, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketTopics.getTopic = function (socket, tid, callback) {
|
||||
apiController.getTopicData(tid, socket.uid, callback);
|
||||
};
|
||||
|
||||
module.exports = SocketTopics;
|
||||
|
@@ -12,7 +12,7 @@ var meta = require('../meta');
|
||||
var events = require('../events');
|
||||
var emailer = require('../emailer');
|
||||
var db = require('../database');
|
||||
var apiController = require('../controllers/api');
|
||||
var userController = require('../controllers/user');
|
||||
var privileges = require('../privileges');
|
||||
|
||||
var SocketUser = {};
|
||||
@@ -303,15 +303,15 @@ SocketUser.invite = function (socket, email, callback) {
|
||||
};
|
||||
|
||||
SocketUser.getUserByUID = function (socket, uid, callback) {
|
||||
apiController.getUserDataByField(socket.uid, 'uid', uid, callback);
|
||||
userController.getUserDataByField(socket.uid, 'uid', uid, callback);
|
||||
};
|
||||
|
||||
SocketUser.getUserByUsername = function (socket, username, callback) {
|
||||
apiController.getUserDataByField(socket.uid, 'username', username, callback);
|
||||
userController.getUserDataByField(socket.uid, 'username', username, callback);
|
||||
};
|
||||
|
||||
SocketUser.getUserByEmail = function (socket, email, callback) {
|
||||
apiController.getUserDataByField(socket.uid, 'email', email, callback);
|
||||
userController.getUserDataByField(socket.uid, 'email', email, callback);
|
||||
};
|
||||
|
||||
SocketUser.setModerationNote = function (socket, data, callback) {
|
||||
|
@@ -317,7 +317,7 @@ var social = require('./social');
|
||||
term: term,
|
||||
}, callback);
|
||||
} else {
|
||||
callback(new Error('no-plugins-available'), []);
|
||||
callback(new Error('[[error:no-plugins-available]]'), []);
|
||||
}
|
||||
};
|
||||
}(exports));
|
||||
|
@@ -330,7 +330,7 @@ module.exports = function (Topics) {
|
||||
function check(item, min, max, minError, maxError, callback) {
|
||||
// Trim and remove HTML (latter for composers that send in HTML, like redactor)
|
||||
if (typeof item === 'string') {
|
||||
item = S(item.trim()).stripTags().s;
|
||||
item = S(item).stripTags().s.trim();
|
||||
}
|
||||
|
||||
if (!item || item.length < parseInt(min, 10)) {
|
||||
|
632
src/user.js
632
src/user.js
@@ -6,388 +6,324 @@ var _ = require('underscore');
|
||||
var groups = require('./groups');
|
||||
var plugins = require('./plugins');
|
||||
var db = require('./database');
|
||||
var topics = require('./topics');
|
||||
var privileges = require('./privileges');
|
||||
var meta = require('./meta');
|
||||
|
||||
(function (User) {
|
||||
User.email = require('./user/email');
|
||||
User.notifications = require('./user/notifications');
|
||||
User.reset = require('./user/reset');
|
||||
User.digest = require('./user/digest');
|
||||
var User = module.exports;
|
||||
|
||||
require('./user/data')(User);
|
||||
require('./user/auth')(User);
|
||||
require('./user/bans')(User);
|
||||
require('./user/create')(User);
|
||||
require('./user/posts')(User);
|
||||
require('./user/topics')(User);
|
||||
require('./user/categories')(User);
|
||||
require('./user/follow')(User);
|
||||
require('./user/profile')(User);
|
||||
require('./user/admin')(User);
|
||||
require('./user/delete')(User);
|
||||
require('./user/settings')(User);
|
||||
require('./user/search')(User);
|
||||
require('./user/jobs')(User);
|
||||
require('./user/picture')(User);
|
||||
require('./user/approval')(User);
|
||||
require('./user/invite')(User);
|
||||
require('./user/password')(User);
|
||||
require('./user/info')(User);
|
||||
User.email = require('./user/email');
|
||||
User.notifications = require('./user/notifications');
|
||||
User.reset = require('./user/reset');
|
||||
User.digest = require('./user/digest');
|
||||
|
||||
User.updateLastOnlineTime = function (uid, callback) {
|
||||
callback = callback || function () {};
|
||||
db.getObjectFields('user:' + uid, ['status', 'lastonline'], function (err, userData) {
|
||||
var now = Date.now();
|
||||
if (err || userData.status === 'offline' || now - parseInt(userData.lastonline, 10) < 300000) {
|
||||
return callback(err);
|
||||
}
|
||||
User.setUserField(uid, 'lastonline', now, callback);
|
||||
});
|
||||
};
|
||||
|
||||
User.updateOnlineUsers = function (uid, callback) {
|
||||
callback = callback || function () {};
|
||||
require('./user/data')(User);
|
||||
require('./user/auth')(User);
|
||||
require('./user/bans')(User);
|
||||
require('./user/create')(User);
|
||||
require('./user/posts')(User);
|
||||
require('./user/topics')(User);
|
||||
require('./user/categories')(User);
|
||||
require('./user/follow')(User);
|
||||
require('./user/profile')(User);
|
||||
require('./user/admin')(User);
|
||||
require('./user/delete')(User);
|
||||
require('./user/settings')(User);
|
||||
require('./user/search')(User);
|
||||
require('./user/jobs')(User);
|
||||
require('./user/picture')(User);
|
||||
require('./user/approval')(User);
|
||||
require('./user/invite')(User);
|
||||
require('./user/password')(User);
|
||||
require('./user/info')(User);
|
||||
require('./user/online')(User);
|
||||
|
||||
User.getUidsFromSet = function (set, start, stop, callback) {
|
||||
if (set === 'users:online') {
|
||||
var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
|
||||
var now = Date.now();
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sortedSetScore('users:online', uid, next);
|
||||
},
|
||||
function (userOnlineTime, next) {
|
||||
if (now - parseInt(userOnlineTime, 10) < 300000) {
|
||||
return callback();
|
||||
}
|
||||
db.sortedSetAdd('users:online', now, uid, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.pushUnreadCount(uid);
|
||||
plugins.fireHook('action:user.online', { uid: uid, timestamp: now });
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - 300000, callback);
|
||||
} else {
|
||||
db.getSortedSetRevRange(set, start, stop, callback);
|
||||
}
|
||||
};
|
||||
|
||||
User.getUidsFromSet = function (set, start, stop, callback) {
|
||||
if (set === 'users:online') {
|
||||
var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
|
||||
var now = Date.now();
|
||||
db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - 300000, callback);
|
||||
} else {
|
||||
db.getSortedSetRevRange(set, start, stop, callback);
|
||||
}
|
||||
};
|
||||
User.getUsersFromSet = function (set, uid, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUidsFromSet(set, start, stop, next);
|
||||
},
|
||||
function (uids, next) {
|
||||
User.getUsers(uids, uid, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getUsersFromSet = function (set, uid, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUidsFromSet(set, start, stop, next);
|
||||
},
|
||||
function (uids, next) {
|
||||
User.getUsers(uids, uid, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getUsersWithFields = function (uids, fields, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
plugins.fireHook('filter:users.addFields', { fields: fields }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
data.fields = data.fields.filter(function (field, index, array) {
|
||||
return array.indexOf(field) === index;
|
||||
});
|
||||
|
||||
async.parallel({
|
||||
userData: function (next) {
|
||||
User.getUsersFields(uids, data.fields, next);
|
||||
},
|
||||
isAdmin: function (next) {
|
||||
User.isAdministrator(uids, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
results.userData.forEach(function (user, index) {
|
||||
if (user) {
|
||||
user.status = User.getStatus(user);
|
||||
user.administrator = results.isAdmin[index];
|
||||
user.banned = parseInt(user.banned, 10) === 1;
|
||||
user.banned_until = parseInt(user['banned:expire'], 10) || 0;
|
||||
user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned';
|
||||
user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1;
|
||||
}
|
||||
});
|
||||
plugins.fireHook('filter:userlist.get', { users: results.userData, uid: uid }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
next(null, data.users);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getUsers = function (uids, uid, callback) {
|
||||
var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'flags',
|
||||
'banned', 'banned:expire', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
|
||||
|
||||
User.getUsersWithFields(uids, fields, uid, callback);
|
||||
};
|
||||
|
||||
User.getStatus = function (userData) {
|
||||
var isOnline = (Date.now() - parseInt(userData.lastonline, 10)) < 300000;
|
||||
return isOnline ? (userData.status || 'online') : 'offline';
|
||||
};
|
||||
|
||||
User.isOnline = function (uid, callback) {
|
||||
if (Array.isArray(uid)) {
|
||||
db.sortedSetScores('users:online', uid, function (err, lastonline) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var now = Date.now();
|
||||
var isOnline = uid.map(function (uid, index) {
|
||||
return now - lastonline[index] < 300000;
|
||||
});
|
||||
callback(null, isOnline);
|
||||
User.getUsersWithFields = function (uids, fields, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
plugins.fireHook('filter:users.addFields', { fields: fields }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
data.fields = data.fields.filter(function (field, index, array) {
|
||||
return array.indexOf(field) === index;
|
||||
});
|
||||
} else {
|
||||
db.sortedSetScore('users:online', uid, function (err, lastonline) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
|
||||
async.parallel({
|
||||
userData: function (next) {
|
||||
User.getUsersFields(uids, data.fields, next);
|
||||
},
|
||||
isAdmin: function (next) {
|
||||
User.isAdministrator(uids, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
results.userData.forEach(function (user, index) {
|
||||
if (user) {
|
||||
user.status = User.getStatus(user);
|
||||
user.administrator = results.isAdmin[index];
|
||||
user.banned = parseInt(user.banned, 10) === 1;
|
||||
user.banned_until = parseInt(user['banned:expire'], 10) || 0;
|
||||
user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned';
|
||||
user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1;
|
||||
}
|
||||
var isOnline = Date.now() - parseInt(lastonline, 10) < 300000;
|
||||
callback(null, isOnline);
|
||||
});
|
||||
}
|
||||
};
|
||||
plugins.fireHook('filter:userlist.get', { users: results.userData, uid: uid }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
next(null, data.users);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.exists = function (uid, callback) {
|
||||
db.isSortedSetMember('users:joindate', uid, callback);
|
||||
};
|
||||
User.getUsers = function (uids, uid, callback) {
|
||||
var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'flags',
|
||||
'banned', 'banned:expire', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
|
||||
|
||||
User.existsBySlug = function (userslug, callback) {
|
||||
User.getUidByUserslug(userslug, function (err, exists) {
|
||||
callback(err, !!exists);
|
||||
});
|
||||
};
|
||||
User.getUsersWithFields(uids, fields, uid, callback);
|
||||
};
|
||||
|
||||
User.getUidByUsername = function (username, callback) {
|
||||
if (!username) {
|
||||
return callback(null, 0);
|
||||
}
|
||||
db.sortedSetScore('username:uid', username, callback);
|
||||
};
|
||||
User.getStatus = function (userData) {
|
||||
var isOnline = (Date.now() - parseInt(userData.lastonline, 10)) < 300000;
|
||||
return isOnline ? (userData.status || 'online') : 'offline';
|
||||
};
|
||||
|
||||
User.getUidsByUsernames = function (usernames, callback) {
|
||||
db.sortedSetScores('username:uid', usernames, callback);
|
||||
};
|
||||
User.exists = function (uid, callback) {
|
||||
db.isSortedSetMember('users:joindate', uid, callback);
|
||||
};
|
||||
|
||||
User.getUidByUserslug = function (userslug, callback) {
|
||||
if (!userslug) {
|
||||
return callback(null, 0);
|
||||
}
|
||||
db.sortedSetScore('userslug:uid', userslug, callback);
|
||||
};
|
||||
User.existsBySlug = function (userslug, callback) {
|
||||
User.getUidByUserslug(userslug, function (err, exists) {
|
||||
callback(err, !!exists);
|
||||
});
|
||||
};
|
||||
|
||||
User.getUsernamesByUids = function (uids, callback) {
|
||||
User.getUsersFields(uids, ['username'], function (err, users) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
User.getUidByUsername = function (username, callback) {
|
||||
if (!username) {
|
||||
return callback(null, 0);
|
||||
}
|
||||
db.sortedSetScore('username:uid', username, callback);
|
||||
};
|
||||
|
||||
User.getUidsByUsernames = function (usernames, callback) {
|
||||
db.sortedSetScores('username:uid', usernames, callback);
|
||||
};
|
||||
|
||||
User.getUidByUserslug = function (userslug, callback) {
|
||||
if (!userslug) {
|
||||
return callback(null, 0);
|
||||
}
|
||||
db.sortedSetScore('userslug:uid', userslug, callback);
|
||||
};
|
||||
|
||||
User.getUsernamesByUids = function (uids, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUsersFields(uids, ['username'], next);
|
||||
},
|
||||
function (users, next) {
|
||||
users = users.map(function (user) {
|
||||
return user.username;
|
||||
});
|
||||
|
||||
callback(null, users);
|
||||
});
|
||||
};
|
||||
next(null, users);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getUsernameByUserslug = function (slug, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUidByUserslug(slug, next);
|
||||
},
|
||||
function (uid, next) {
|
||||
User.getUserField(uid, 'username', next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
User.getUsernameByUserslug = function (slug, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUidByUserslug(slug, next);
|
||||
},
|
||||
function (uid, next) {
|
||||
User.getUserField(uid, 'username', next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getUidByEmail = function (email, callback) {
|
||||
db.sortedSetScore('email:uid', email.toLowerCase(), callback);
|
||||
};
|
||||
User.getUidByEmail = function (email, callback) {
|
||||
db.sortedSetScore('email:uid', email.toLowerCase(), callback);
|
||||
};
|
||||
|
||||
User.getUidsByEmails = function (emails, callback) {
|
||||
emails = emails.map(function (email) {
|
||||
return email && email.toLowerCase();
|
||||
});
|
||||
db.sortedSetScores('email:uid', emails, callback);
|
||||
};
|
||||
User.getUidsByEmails = function (emails, callback) {
|
||||
emails = emails.map(function (email) {
|
||||
return email && email.toLowerCase();
|
||||
});
|
||||
db.sortedSetScores('email:uid', emails, callback);
|
||||
};
|
||||
|
||||
User.getUsernameByEmail = function (email, callback) {
|
||||
db.sortedSetScore('email:uid', email.toLowerCase(), function (err, uid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
User.getUsernameByEmail = function (email, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sortedSetScore('email:uid', email.toLowerCase(), next);
|
||||
},
|
||||
function (uid, next) {
|
||||
User.getUserField(uid, 'username', next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.isModerator = function (uid, cid, callback) {
|
||||
privileges.users.isModerator(uid, cid, callback);
|
||||
};
|
||||
|
||||
User.isModeratorOfAnyCategory = function (uid, callback) {
|
||||
User.getModeratedCids(uid, function (err, cids) {
|
||||
callback(err, Array.isArray(cids) ? !!cids.length : false);
|
||||
});
|
||||
};
|
||||
|
||||
User.isAdministrator = function (uid, callback) {
|
||||
privileges.users.isAdministrator(uid, callback);
|
||||
};
|
||||
|
||||
User.isGlobalModerator = function (uid, callback) {
|
||||
privileges.users.isGlobalModerator(uid, callback);
|
||||
};
|
||||
|
||||
User.isAdminOrGlobalMod = function (uid, callback) {
|
||||
async.parallel({
|
||||
isAdmin: async.apply(User.isAdministrator, uid),
|
||||
isGlobalMod: async.apply(User.isGlobalModerator, uid),
|
||||
}, function (err, results) {
|
||||
callback(err, results ? (results.isAdmin || results.isGlobalMod) : false);
|
||||
});
|
||||
};
|
||||
|
||||
User.isAdminOrSelf = function (callerUid, uid, callback) {
|
||||
isSelfOrMethod(callerUid, uid, User.isAdministrator, callback);
|
||||
};
|
||||
|
||||
User.isAdminOrGlobalModOrSelf = function (callerUid, uid, callback) {
|
||||
isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod, callback);
|
||||
};
|
||||
|
||||
function isSelfOrMethod(callerUid, uid, method, callback) {
|
||||
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
method(callerUid, next);
|
||||
},
|
||||
function (isPass, next) {
|
||||
if (!isPass) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
User.getUserField(uid, 'username', callback);
|
||||
});
|
||||
};
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
User.isModerator = function (uid, cid, callback) {
|
||||
privileges.users.isModerator(uid, cid, callback);
|
||||
};
|
||||
User.getAdminsandGlobalMods = function (callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel([
|
||||
async.apply(groups.getMembers, 'administrators', 0, -1),
|
||||
async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
||||
], next);
|
||||
},
|
||||
function (results, next) {
|
||||
User.getUsersData(_.union(results), next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.isModeratorOfAnyCategory = function (uid, callback) {
|
||||
User.getModeratedCids(uid, function (err, cids) {
|
||||
callback(err, Array.isArray(cids) ? !!cids.length : false);
|
||||
});
|
||||
};
|
||||
User.getAdminsandGlobalModsandModerators = function (callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel([
|
||||
async.apply(groups.getMembers, 'administrators', 0, -1),
|
||||
async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
||||
async.apply(User.getModeratorUids),
|
||||
], next);
|
||||
},
|
||||
function (results, next) {
|
||||
User.getUsersData(_.union.apply(_, results), next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.isAdministrator = function (uid, callback) {
|
||||
privileges.users.isAdministrator(uid, callback);
|
||||
};
|
||||
|
||||
User.isGlobalModerator = function (uid, callback) {
|
||||
privileges.users.isGlobalModerator(uid, callback);
|
||||
};
|
||||
|
||||
User.isPrivileged = function (uid, callback) {
|
||||
async.parallel([
|
||||
async.apply(User.isAdministrator, uid),
|
||||
async.apply(User.isGlobalModerator, uid),
|
||||
async.apply(User.isModeratorOfAnyCategory, uid),
|
||||
], function (err, results) {
|
||||
callback(err, results ? results.some(Boolean) : false);
|
||||
});
|
||||
};
|
||||
|
||||
User.isAdminOrGlobalMod = function (uid, callback) {
|
||||
async.parallel({
|
||||
isAdmin: async.apply(User.isAdministrator, uid),
|
||||
isGlobalMod: async.apply(User.isGlobalModerator, uid),
|
||||
}, function (err, results) {
|
||||
callback(err, results ? (results.isAdmin || results.isGlobalMod) : false);
|
||||
});
|
||||
};
|
||||
|
||||
User.isAdminOrSelf = function (callerUid, uid, callback) {
|
||||
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
User.isAdministrator(callerUid, function (err, isAdmin) {
|
||||
if (err || !isAdmin) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
User.isAdminOrGlobalModOrSelf = function (callerUid, uid, callback) {
|
||||
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
User.isAdminOrGlobalMod(callerUid, function (err, isAdminOrGlobalMod) {
|
||||
if (err || !isAdminOrGlobalMod) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
User.getAdminsandGlobalMods = function (callback) {
|
||||
async.parallel({
|
||||
admins: async.apply(groups.getMembers, 'administrators', 0, -1),
|
||||
mods: async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var uids = results.admins.concat(results.mods).filter(function (uid, index, array) {
|
||||
return uid && array.indexOf(uid) === index;
|
||||
User.getModeratorUids = function (callback) {
|
||||
async.waterfall([
|
||||
async.apply(db.getSortedSetRange, 'categories:cid', 0, -1),
|
||||
function (cids, next) {
|
||||
var groupNames = cids.map(function (cid) {
|
||||
return 'cid:' + cid + ':privileges:mods';
|
||||
});
|
||||
User.getUsersData(uids, callback);
|
||||
});
|
||||
};
|
||||
|
||||
User.getAdminsandGlobalModsandModerators = function (callback) {
|
||||
async.parallel([
|
||||
async.apply(groups.getMembers, 'administrators', 0, -1),
|
||||
async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
||||
async.apply(User.getModeratorUids),
|
||||
], function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
groups.getMembersOfGroups(groupNames, next);
|
||||
},
|
||||
function (memberSets, next) {
|
||||
next(null, _.union.apply(_, memberSets));
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getModeratedCids = function (uid, callback) {
|
||||
var cids;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('categories:cid', 0, -1, next);
|
||||
},
|
||||
function (_cids, next) {
|
||||
cids = _cids;
|
||||
User.isModerator(uid, cids, next);
|
||||
},
|
||||
function (isMods, next) {
|
||||
cids = cids.filter(function (cid, index) {
|
||||
return cid && isMods[index];
|
||||
});
|
||||
next(null, cids);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.addInterstitials = function (callback) {
|
||||
plugins.registerHook('core', {
|
||||
hook: 'filter:register.interstitial',
|
||||
method: function (data, callback) {
|
||||
if (meta.config.termsOfUse && !data.userData.acceptTos) {
|
||||
data.interstitials.push({
|
||||
template: 'partials/acceptTos',
|
||||
data: {
|
||||
termsOfUse: meta.config.termsOfUse,
|
||||
},
|
||||
callback: function (userData, formData, next) {
|
||||
if (formData['agree-terms'] === 'on') {
|
||||
userData.acceptTos = true;
|
||||
}
|
||||
|
||||
next(userData.acceptTos ? null : new Error('[[register:terms_of_use_error]]'));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
User.getUsersData(_.union.apply(_, results), callback);
|
||||
});
|
||||
};
|
||||
callback(null, data);
|
||||
},
|
||||
});
|
||||
|
||||
User.getModeratorUids = function (callback) {
|
||||
async.waterfall([
|
||||
async.apply(db.getSortedSetRange, 'categories:cid', 0, -1),
|
||||
function (cids, next) {
|
||||
var groupNames = cids.map(function (cid) {
|
||||
return 'cid:' + cid + ':privileges:mods';
|
||||
});
|
||||
callback();
|
||||
};
|
||||
|
||||
groups.getMembersOfGroups(groupNames, function (err, memberSets) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next(null, _.union.apply(_, memberSets));
|
||||
});
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.getModeratedCids = function (uid, callback) {
|
||||
var cids;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('categories:cid', 0, -1, next);
|
||||
},
|
||||
function (_cids, next) {
|
||||
cids = _cids;
|
||||
User.isModerator(uid, cids, next);
|
||||
},
|
||||
function (isMods, next) {
|
||||
cids = cids.filter(function (cid, index) {
|
||||
return cid && isMods[index];
|
||||
});
|
||||
next(null, cids);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.addInterstitials = function (callback) {
|
||||
plugins.registerHook('core', {
|
||||
hook: 'filter:register.interstitial',
|
||||
method: function (data, callback) {
|
||||
if (meta.config.termsOfUse && !data.userData.acceptTos) {
|
||||
data.interstitials.push({
|
||||
template: 'partials/acceptTos',
|
||||
data: {
|
||||
termsOfUse: meta.config.termsOfUse,
|
||||
},
|
||||
callback: function (userData, formData, next) {
|
||||
if (formData['agree-terms'] === 'on') {
|
||||
userData.acceptTos = true;
|
||||
}
|
||||
|
||||
next(userData.acceptTos ? null : new Error('[[register:terms_of_use_error]]'));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
},
|
||||
});
|
||||
|
||||
callback();
|
||||
};
|
||||
}(exports));
|
||||
|
@@ -16,13 +16,7 @@ module.exports = function (User) {
|
||||
};
|
||||
|
||||
User.getIPs = function (uid, stop, callback) {
|
||||
db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, function (err, ips) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, ips);
|
||||
});
|
||||
db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, callback);
|
||||
};
|
||||
|
||||
User.getUsersCSV = function (callback) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var validator = require('validator');
|
||||
var nconf = require('nconf');
|
||||
var winston = require('winston');
|
||||
@@ -63,6 +64,7 @@ module.exports = function (User) {
|
||||
addField('lastonline');
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
db.getObjectsFields(keys, fields, function (err, users) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -74,6 +76,16 @@ module.exports = function (User) {
|
||||
|
||||
modifyUserData(users, fieldsToRemove, callback);
|
||||
});
|
||||
=======
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObjectsFields(keys, fields, next);
|
||||
},
|
||||
function (users, next) {
|
||||
modifyUserData(users, fieldsToRemove, next);
|
||||
},
|
||||
], callback);
|
||||
>>>>>>> master
|
||||
};
|
||||
|
||||
User.getMultipleUserFields = function (uids, fields, callback) {
|
||||
@@ -105,6 +117,7 @@ module.exports = function (User) {
|
||||
return 'user:' + uid;
|
||||
});
|
||||
|
||||
<<<<<<< HEAD
|
||||
db.getObjects(keys, function (err, users) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -116,6 +129,16 @@ module.exports = function (User) {
|
||||
|
||||
modifyUserData(users, [], callback);
|
||||
});
|
||||
=======
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObjects(keys, next);
|
||||
},
|
||||
function (users, next) {
|
||||
modifyUserData(users, [], next);
|
||||
},
|
||||
], callback);
|
||||
>>>>>>> master
|
||||
};
|
||||
|
||||
function modifyUserData(users, fieldsToRemove, callback) {
|
||||
@@ -178,51 +201,53 @@ module.exports = function (User) {
|
||||
|
||||
User.setUserField = function (uid, field, value, callback) {
|
||||
callback = callback || function () {};
|
||||
db.setObjectField('user:' + uid, field, value, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'set' });
|
||||
callback();
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.setObjectField('user:' + uid, field, value, next);
|
||||
},
|
||||
function (next) {
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'set' });
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.setUserFields = function (uid, data, callback) {
|
||||
callback = callback || function () {};
|
||||
db.setObject('user:' + uid, data, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
for (var field in data) {
|
||||
if (data.hasOwnProperty(field)) {
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: data[field], type: 'set' });
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.setObject('user:' + uid, data, next);
|
||||
},
|
||||
function (next) {
|
||||
for (var field in data) {
|
||||
if (data.hasOwnProperty(field)) {
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: data[field], type: 'set' });
|
||||
}
|
||||
}
|
||||
}
|
||||
callback();
|
||||
});
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.incrementUserFieldBy = function (uid, field, value, callback) {
|
||||
callback = callback || function () {};
|
||||
db.incrObjectFieldBy('user:' + uid, field, value, function (err, value) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'increment' });
|
||||
|
||||
callback(null, value);
|
||||
});
|
||||
incrDecrUserFieldBy(uid, field, value, 'increment', callback);
|
||||
};
|
||||
|
||||
User.decrementUserFieldBy = function (uid, field, value, callback) {
|
||||
callback = callback || function () {};
|
||||
db.incrObjectFieldBy('user:' + uid, field, -value, function (err, value) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'decrement' });
|
||||
|
||||
callback(null, value);
|
||||
});
|
||||
incrDecrUserFieldBy(uid, field, -value, 'decrement', callback);
|
||||
};
|
||||
|
||||
function incrDecrUserFieldBy(uid, field, value, type, callback) {
|
||||
callback = callback || function () {};
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.incrObjectFieldBy('user:' + uid, field, value, next);
|
||||
},
|
||||
function (value, next) {
|
||||
plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: type });
|
||||
|
||||
next(null, value);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
};
|
||||
|
70
src/user/online.js
Normal file
70
src/user/online.js
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var db = require('../database');
|
||||
var topics = require('../topics');
|
||||
var plugins = require('../plugins');
|
||||
|
||||
module.exports = function (User) {
|
||||
User.updateLastOnlineTime = function (uid, callback) {
|
||||
callback = callback || function () {};
|
||||
db.getObjectFields('user:' + uid, ['status', 'lastonline'], function (err, userData) {
|
||||
var now = Date.now();
|
||||
if (err || userData.status === 'offline' || now - parseInt(userData.lastonline, 10) < 300000) {
|
||||
return callback(err);
|
||||
}
|
||||
User.setUserField(uid, 'lastonline', now, callback);
|
||||
});
|
||||
};
|
||||
|
||||
User.updateOnlineUsers = function (uid, callback) {
|
||||
callback = callback || function () {};
|
||||
|
||||
var now = Date.now();
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sortedSetScore('users:online', uid, next);
|
||||
},
|
||||
function (userOnlineTime, next) {
|
||||
if (now - parseInt(userOnlineTime, 10) < 300000) {
|
||||
return callback();
|
||||
}
|
||||
db.sortedSetAdd('users:online', now, uid, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.pushUnreadCount(uid);
|
||||
plugins.fireHook('action:user.online', { uid: uid, timestamp: now });
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.isOnline = function (uid, callback) {
|
||||
var now = Date.now();
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (Array.isArray(uid)) {
|
||||
db.sortedSetScores('users:online', uid, next);
|
||||
} else {
|
||||
db.sortedSetScore('users:online', uid, next);
|
||||
}
|
||||
},
|
||||
function (lastonline, next) {
|
||||
function checkOnline(lastonline) {
|
||||
return now - lastonline < 300000;
|
||||
}
|
||||
|
||||
var isOnline;
|
||||
if (Array.isArray(uid)) {
|
||||
isOnline = uid.map(function (uid, index) {
|
||||
return checkOnline(lastonline[index]);
|
||||
});
|
||||
} else {
|
||||
isOnline = checkOnline(lastonline);
|
||||
}
|
||||
next(null, isOnline);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
};
|
@@ -50,12 +50,7 @@ module.exports = function (User) {
|
||||
}, next);
|
||||
},
|
||||
function (image, next) {
|
||||
User.setUserFields(uid, {
|
||||
uploadedpicture: image.url,
|
||||
picture: image.url,
|
||||
}, function (err) {
|
||||
next(err, image);
|
||||
});
|
||||
next(null, image);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
@@ -18,6 +18,14 @@ module.exports = function (User) {
|
||||
var updateUid = data.uid;
|
||||
var oldData;
|
||||
|
||||
if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) {
|
||||
return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]'));
|
||||
}
|
||||
|
||||
if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
|
||||
return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]'));
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
plugins.fireHook('filter:user.updateProfile', { uid: uid, data: data, fields: fields }, next);
|
||||
@@ -27,8 +35,6 @@ module.exports = function (User) {
|
||||
data = data.data;
|
||||
|
||||
async.series([
|
||||
async.apply(isAboutMeValid, data),
|
||||
async.apply(isSignatureValid, data),
|
||||
async.apply(isEmailAvailable, data, updateUid),
|
||||
async.apply(isUsernameAvailable, data, updateUid),
|
||||
async.apply(isGroupTitleValid, data),
|
||||
@@ -68,22 +74,6 @@ module.exports = function (User) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
function isAboutMeValid(data, callback) {
|
||||
if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) {
|
||||
callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function isSignatureValid(data, callback) {
|
||||
if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
|
||||
callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function isEmailAvailable(data, uid, callback) {
|
||||
if (!data.email) {
|
||||
return callback();
|
||||
|
@@ -74,7 +74,7 @@ module.exports = function (User) {
|
||||
settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1;
|
||||
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
|
||||
settings.delayImageLoading = parseInt(getSetting(settings, 'delayImageLoading', 1), 10) === 1;
|
||||
settings.bootswatchSkin = settings.bootswatchSkin || 'default';
|
||||
settings.bootswatchSkin = settings.bootswatchSkin || meta.config.bootswatchSkin || 'default';
|
||||
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
|
||||
|
||||
callback(null, settings);
|
||||
|
@@ -36,13 +36,7 @@
|
||||
<li data-plugin-id="{installed.id}" class="clearfix">
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default disabled"><i class="fa fa-exclamation-triangle"></i> [[admin/extend/plugins:plugin-item.unknown]]</button>
|
||||
|
||||
<<<<<<< HEAD
|
||||
<button data-action="toggleInstall" data-installed="1" class="btn btn-danger"><i class="fa fa-trash-o"></i> Uninstall</button>
|
||||
|
||||
=======
|
||||
<button data-action="toggleInstall" data-installed="1" class="btn btn-danger"><i class="fa fa-trash-o"></i> [[admin/extend/plugins:plugin-item.uninstall]]</button>
|
||||
>>>>>>> `admin/extend` translations
|
||||
</div>
|
||||
|
||||
<h2><strong>{installed.id}</strong></h2>
|
||||
|
@@ -61,21 +61,20 @@ module.exports.listen = function (callback) {
|
||||
|
||||
logger.init(app);
|
||||
|
||||
initializeNodeBB(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.waterfall([
|
||||
initializeNodeBB,
|
||||
function (next) {
|
||||
winston.info('NodeBB Ready');
|
||||
|
||||
winston.info('NodeBB Ready');
|
||||
require('./socket.io').server.emit('event:nodebb.ready', {
|
||||
'cache-buster': meta.config['cache-buster'],
|
||||
});
|
||||
|
||||
require('./socket.io').server.emit('event:nodebb.ready', {
|
||||
'cache-buster': meta.config['cache-buster'],
|
||||
});
|
||||
plugins.fireHook('action:nodebb.ready');
|
||||
|
||||
plugins.fireHook('action:nodebb.ready');
|
||||
|
||||
listen(callback);
|
||||
});
|
||||
listen(next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
function initializeNodeBB(callback) {
|
||||
@@ -107,7 +106,9 @@ function initializeNodeBB(callback) {
|
||||
meta.blacklist.load,
|
||||
], next);
|
||||
},
|
||||
], callback);
|
||||
], function (err) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
function setupExpressApp(app) {
|
||||
|
@@ -390,6 +390,7 @@ describe('Categories', function () {
|
||||
it('should get all categories', function (done) {
|
||||
socketCategories.getAll({ uid: adminUid }, {}, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert(data);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -615,6 +616,72 @@ describe('Categories', function () {
|
||||
});
|
||||
|
||||
|
||||
describe('privileges', function () {
|
||||
var privileges = require('../src/privileges');
|
||||
|
||||
it('should return empty array if uids is empty array', function (done) {
|
||||
privileges.categories.filterUids('find', categoryObj.cid, [], function (err, uids) {
|
||||
assert.ifError(err);
|
||||
assert.equal(uids.length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter uids by privilege', function (done) {
|
||||
privileges.categories.filterUids('find', categoryObj.cid, [1, 2, 3, 4], function (err, uids) {
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(uids, [1, 2]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load user privileges', function (done) {
|
||||
privileges.categories.userPrivileges(categoryObj.cid, 1, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(data, {
|
||||
find: false,
|
||||
mods: false,
|
||||
'posts:delete': false,
|
||||
read: false,
|
||||
'topics:reply': false,
|
||||
'topics:read': false,
|
||||
'topics:create': false,
|
||||
'topics:delete': false,
|
||||
'posts:edit': false,
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load group privileges', function (done) {
|
||||
privileges.categories.groupPrivileges(categoryObj.cid, 'registered-users', function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(data, {
|
||||
'groups:find': true,
|
||||
'groups:posts:edit': true,
|
||||
'groups:topics:delete': false,
|
||||
'groups:topics:create': true,
|
||||
'groups:topics:reply': true,
|
||||
'groups:posts:delete': true,
|
||||
'groups:read': true,
|
||||
'groups:topics:read': true,
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if cid is falsy', function (done) {
|
||||
privileges.categories.isUserAllowedTo('find', null, adminUid, function (err, isAllowed) {
|
||||
assert.ifError(err);
|
||||
assert.equal(isAllowed, false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
after(function (done) {
|
||||
db.emptydb(done);
|
||||
});
|
||||
|
@@ -21,6 +21,7 @@ describe('Admin Controllers', function () {
|
||||
var jar;
|
||||
|
||||
before(function (done) {
|
||||
groups.resetCache();
|
||||
async.series({
|
||||
category: function (next) {
|
||||
categories.create({
|
||||
@@ -43,9 +44,10 @@ describe('Admin Controllers', function () {
|
||||
cid = results.category.cid;
|
||||
|
||||
topics.post({ uid: adminUid, title: 'test topic title', content: 'test topic content', cid: results.category.cid }, function (err, result) {
|
||||
assert.ifError(err);
|
||||
tid = result.topicData.tid;
|
||||
pid = result.postData.pid;
|
||||
done(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -27,7 +27,7 @@ describe('Controllers', function () {
|
||||
}, next);
|
||||
},
|
||||
user: function (next) {
|
||||
user.create({ username: 'foo', password: 'barbar' }, next);
|
||||
user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' }, next);
|
||||
},
|
||||
navigation: function (next) {
|
||||
var navigation = require('../src/navigation/admin');
|
||||
@@ -498,11 +498,23 @@ describe('Controllers', function () {
|
||||
hidden: 0,
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
request(nconf.get('url') + '/groups/group-details', function (err, res, body) {
|
||||
groups.join('group-details', fooUid, function (err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
topics.post({
|
||||
uid: fooUid,
|
||||
title: 'topic title',
|
||||
content: 'test topic content',
|
||||
cid: cid,
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
request(nconf.get('url') + '/api/groups/group-details', { json: true }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
assert.equal(body.posts[0].content, 'test topic content');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -532,6 +544,15 @@ describe('Controllers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should get recent posts', function (done) {
|
||||
request(nconf.get('url') + '/api/recent/posts/month', function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get post data', function (done) {
|
||||
request(nconf.get('url') + '/api/post/pid/' + pid, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
@@ -890,6 +911,42 @@ describe('Controllers', function () {
|
||||
},
|
||||
], done);
|
||||
});
|
||||
|
||||
it('should 404 if user does not exist', function (done) {
|
||||
request(nconf.get('url') + '/api/user/email/doesnotexist', function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 404);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load user by uid', function (done) {
|
||||
request(nconf.get('url') + '/api/user/uid/' + fooUid, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load user by username', function (done) {
|
||||
request(nconf.get('url') + '/api/user/username/foo', function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load user by email', function (done) {
|
||||
request(nconf.get('url') + '/api/user/email/foo@test.com', function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('account follow page', function () {
|
||||
@@ -943,7 +1000,7 @@ describe('Controllers', function () {
|
||||
|
||||
describe('post redirect', function () {
|
||||
it('should 404 for invalid pid', function (done) {
|
||||
request(nconf.get('url') + '/post/fail', function (err, res) {
|
||||
request(nconf.get('url') + '/api/post/fail', function (err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 404);
|
||||
done();
|
||||
|
@@ -11,6 +11,7 @@ var categories = require('../src/categories');
|
||||
var privileges = require('../src/privileges');
|
||||
var user = require('../src/user');
|
||||
var groups = require('../src/groups');
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
|
||||
describe('Post\'s', function () {
|
||||
var voterUid;
|
||||
@@ -66,7 +67,6 @@ describe('Post\'s', function () {
|
||||
});
|
||||
|
||||
describe('voting', function () {
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
it('should upvote a post', function (done) {
|
||||
socketPosts.upvote({ uid: voterUid }, { pid: postData.pid, room_id: 'topic_1' }, function (err, result) {
|
||||
assert.ifError(err);
|
||||
@@ -138,7 +138,7 @@ describe('Post\'s', function () {
|
||||
|
||||
describe('bookmarking', function () {
|
||||
it('should bookmark a post', function (done) {
|
||||
posts.bookmark(postData.pid, voterUid, function (err, data) {
|
||||
socketPosts.bookmark({ uid: voterUid }, { pid: postData.pid, room_id: 'topic_' + postData.tid }, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert.equal(data.isBookmarked, true);
|
||||
posts.hasBookmarked(postData.pid, voterUid, function (err, hasBookmarked) {
|
||||
@@ -150,7 +150,7 @@ describe('Post\'s', function () {
|
||||
});
|
||||
|
||||
it('should unbookmark a post', function (done) {
|
||||
posts.unbookmark(postData.pid, voterUid, function (err, data) {
|
||||
socketPosts.unbookmark({ uid: voterUid }, { pid: postData.pid, room_id: 'topic_' + postData.tid }, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert.equal(data.isBookmarked, false);
|
||||
posts.hasBookmarked([postData.pid], voterUid, function (err, hasBookmarked) {
|
||||
@@ -163,8 +163,6 @@ describe('Post\'s', function () {
|
||||
});
|
||||
|
||||
describe('post tools', function () {
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
|
||||
it('should error if data is invalid', function (done) {
|
||||
socketPosts.loadPostTools({ uid: globalModUid }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
@@ -209,7 +207,6 @@ describe('Post\'s', function () {
|
||||
var mainPid;
|
||||
var replyPid;
|
||||
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
before(function (done) {
|
||||
createTopicWithReply(function (topicPostData, replyData) {
|
||||
tid = topicPostData.topicData.tid;
|
||||
@@ -299,7 +296,6 @@ describe('Post\'s', function () {
|
||||
var pid;
|
||||
var replyPid;
|
||||
var tid;
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
var meta = require('../src/meta');
|
||||
before(function (done) {
|
||||
topics.post({
|
||||
@@ -430,7 +426,6 @@ describe('Post\'s', function () {
|
||||
var replyPid;
|
||||
var tid;
|
||||
var moveTid;
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
|
||||
before(function (done) {
|
||||
async.waterfall([
|
||||
@@ -554,7 +549,6 @@ describe('Post\'s', function () {
|
||||
});
|
||||
});
|
||||
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
it('should error with invalid data', function (done) {
|
||||
socketPosts.reply({ uid: 0 }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
|
@@ -419,11 +419,16 @@ describe('socket.io', function () {
|
||||
});
|
||||
|
||||
it('should set theme to bootswatch', function (done) {
|
||||
socketAdmin.themes.set({ uid: adminUid }, { type: 'bootswatch', src: 'darkly' }, function (err) {
|
||||
socketAdmin.themes.set({ uid: adminUid }, {
|
||||
type: 'bootswatch',
|
||||
src: '//maxcdn.bootstrapcdn.com/bootswatch/latest/darkly/bootstrap.min.css',
|
||||
id: 'darkly',
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
meta.configs.get('theme:src', function (err, id) {
|
||||
meta.configs.getFields(['theme:src', 'bootswatchSkin'], function (err, fields) {
|
||||
assert.ifError(err);
|
||||
assert.equal(id, 'darkly');
|
||||
assert.equal(fields['theme:src'], '//maxcdn.bootstrapcdn.com/bootswatch/latest/darkly/bootstrap.min.css');
|
||||
assert.equal(fields.bootswatchSkin, 'darkly');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
114
test/topics.js
114
test/topics.js
@@ -12,6 +12,7 @@ var User = require('../src/user');
|
||||
var groups = require('../src/groups');
|
||||
var helpers = require('./helpers');
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
var socketTopics = require('../src/socket.io/topics');
|
||||
|
||||
describe('Topic\'s', function () {
|
||||
var topic;
|
||||
@@ -49,11 +50,34 @@ describe('Topic\'s', function () {
|
||||
});
|
||||
|
||||
describe('.post', function () {
|
||||
it('should fail to create topic with invalid data', function (done) {
|
||||
socketTopics.post({ uid: 0 }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a new topic with proper parameters', function (done) {
|
||||
topics.post({ uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId }, function (err, result) {
|
||||
assert.equal(err, null, 'was created with error');
|
||||
assert.ok(result);
|
||||
assert.ifError(err);
|
||||
assert(result);
|
||||
topic.tid = result.topicData.tid;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get post count', function (done) {
|
||||
socketTopics.postcount({ uid: adminUid }, topic.tid, function (err, count) {
|
||||
assert.ifError(err);
|
||||
assert.equal(count, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load topic', function (done) {
|
||||
socketTopics.getTopic({ uid: adminUid }, topic.tid, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert.equal(data.tid, topic.tid);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -246,7 +270,7 @@ describe('Topic\'s', function () {
|
||||
var newTopic;
|
||||
var followerUid;
|
||||
var moveCid;
|
||||
var socketTopics = require('../src/socket.io/topics');
|
||||
|
||||
before(function (done) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
@@ -589,8 +613,7 @@ describe('Topic\'s', function () {
|
||||
assert.ok(result);
|
||||
replies.push(result);
|
||||
next();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
before(function (done) {
|
||||
@@ -619,25 +642,45 @@ describe('Topic\'s', function () {
|
||||
function (next) { postReply(next); },
|
||||
function (next) {
|
||||
topicPids = replies.map(function (reply) { return reply.pid; });
|
||||
topics.setUserBookmark(newTopic.tid, topic.userId, originalBookmark, next);
|
||||
socketTopics.bookmark({ uid: topic.userId }, { tid: newTopic.tid, index: originalBookmark }, next);
|
||||
}],
|
||||
done);
|
||||
});
|
||||
|
||||
it('should fail with invalid data', function (done) {
|
||||
socketTopics.bookmark({ uid: topic.userId }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have 12 replies', function (done) {
|
||||
assert.equal(12, replies.length);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail with invalid data', function (done) {
|
||||
socketTopics.createTopicFromPosts({ uid: 0 }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:not-logged-in]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail with invalid data', function (done) {
|
||||
socketTopics.createTopicFromPosts({ uid: 1 }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update the user\'s bookmark', function (done) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.createTopicFromPosts(
|
||||
topic.userId,
|
||||
'Fork test, no bookmark update',
|
||||
topicPids.slice(-2),
|
||||
newTopic.tid,
|
||||
next);
|
||||
socketTopics.createTopicFromPosts({ uid: topic.userId }, {
|
||||
title: 'Fork test, no bookmark update',
|
||||
pids: topicPids.slice(-2),
|
||||
fromTid: newTopic.tid,
|
||||
}, next);
|
||||
},
|
||||
function (forkedTopicData, next) {
|
||||
topics.getUserBookmark(newTopic.tid, topic.userId, next);
|
||||
@@ -1388,6 +1431,13 @@ describe('Topic\'s', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if not logged in', function (done) {
|
||||
socketTopics.changeWatching({ uid: 0 }, { tid: tid, type: 'ignore' }, function (err) {
|
||||
assert.equal(err.message, '[[error:not-logged-in]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter ignoring uids', function (done) {
|
||||
socketTopics.changeWatching({ uid: followerUid }, { tid: tid, type: 'ignore' }, function (err) {
|
||||
assert.ifError(err);
|
||||
@@ -1418,7 +1468,7 @@ describe('Topic\'s', function () {
|
||||
topics.toggleFollow(tid, followerUid, function (err, isFollowing) {
|
||||
assert.ifError(err);
|
||||
assert(isFollowing);
|
||||
topics.isFollowing([tid], followerUid, function (err, isFollowing) {
|
||||
socketTopics.isFollowed({ uid: followerUid }, tid, function (err, isFollowing) {
|
||||
assert.ifError(err);
|
||||
assert(isFollowing);
|
||||
done();
|
||||
@@ -1427,6 +1477,44 @@ describe('Topic\'s', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('topics search', function () {
|
||||
it('should error with invalid data', function (done) {
|
||||
socketTopics.search({ uid: adminUid }, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if no search plugin', function (done) {
|
||||
socketTopics.search({ uid: adminUid }, { tid: topic.tid, term: 'test' }, function (err) {
|
||||
assert.equal(err.message, '[[error:no-plugins-available]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return results', function (done) {
|
||||
var plugins = require('../src/plugins');
|
||||
plugins.registerHook('myTestPlugin', {
|
||||
hook: 'filter:topic.search',
|
||||
method: function (data, callback) {
|
||||
callback(null, [1, 2, 3]);
|
||||
},
|
||||
});
|
||||
socketTopics.search({ uid: adminUid }, { tid: topic.tid, term: 'test' }, function (err, results) {
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(results, [1, 2, 3]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should check if user is moderator', function (done) {
|
||||
socketTopics.isModerator({ uid: adminUid }, topic.tid, function (err, isModerator) {
|
||||
assert.ifError(err);
|
||||
assert(!isModerator);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
db.emptydb(done);
|
||||
|
Reference in New Issue
Block a user