mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-06 06:27:09 +02:00
Merge remote-tracking branch 'refs/remotes/origin/master' into private-feeds
This commit is contained in:
5
app.js
5
app.js
@@ -26,7 +26,10 @@ if (require.main !== module) {
|
||||
}
|
||||
|
||||
var nconf = require('nconf');
|
||||
nconf.argv().env('__');
|
||||
nconf.argv().env({
|
||||
separator: '__',
|
||||
lowerCase: true,
|
||||
});
|
||||
|
||||
var url = require('url');
|
||||
var async = require('async');
|
||||
|
||||
@@ -142,7 +142,7 @@ function getPorts() {
|
||||
process.exit();
|
||||
}
|
||||
var urlObject = url.parse(_url);
|
||||
var port = nconf.get('port') || nconf.get('PORT') || urlObject.port || 4567;
|
||||
var port = nconf.get('port') || urlObject.port || 4567;
|
||||
if (!Array.isArray(port)) {
|
||||
port = [port];
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||
"nodebb-plugin-emoji-one": "1.2.1",
|
||||
"nodebb-plugin-markdown": "7.1.1",
|
||||
"nodebb-plugin-mentions": "2.0.3",
|
||||
"nodebb-plugin-mentions": "2.1.1",
|
||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||
"nodebb-plugin-spam-be-gone": "0.5.0",
|
||||
"nodebb-rewards-essentials": "0.0.9",
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
"stats.all": "全て",
|
||||
|
||||
"updates": "更新",
|
||||
"running-version": "<strong>NodeBB v <span id = \"version\">%1 </ span> </ strong>を実行しています。",
|
||||
"running-version": "<strong>NodeBB v<span id=\"version\">%1</span></strong> を実行しています。",
|
||||
"keep-updated": "常に最新のセキュリティパッチとバグ修正のためにNodeBBが最新であることを確認してください。",
|
||||
"up-to-date": "<p>あなたは<strong>最新の状態</ strong>です。<i class = \"fa fa-check\"> </ i> </ p>",
|
||||
"up-to-date": "<p>あなたは<strong>最新の状態</strong>です。<i class=\"fa fa-check\"></i></p>",
|
||||
"upgrade-available": "<p>新しいバージョン (v%1) がリリースされました。<a href=\"https://docs.nodebb.org/en/latest/upgrading/index.html\">NodeBBのアップグレード</a>を検討してください。</p>",
|
||||
"prerelease-upgrade-available": "<p>これはNodeBBの旧リリースのバージョンです。新しいバージョン(v%1)がリリースされました。<a href=\"https://docs.nodebb.org/en/latest/upgrading/index.html\"> NodeBBのアップグレード</a>を検討してください。</ p>",
|
||||
"prerelease-warning": "<p>これはNodeBBの<strong>プレリリース版</ strong>です。意図しないバグが発生することがあります。<i class=\"fa fa-exclamation-triangle\"></i></p>",
|
||||
"prerelease-warning": "<p>これはNodeBBの<strong>プレリリース版</strong>です。意図しないバグが発生することがあります。<i class=\"fa fa-exclamation-triangle\"></i></p>",
|
||||
"running-in-development": "<span>フォーラムが開発モードで動作しています。フォーラムの動作が脆弱かもしれませんので、管理者に問い合わせてください。</span>",
|
||||
|
||||
"notices": "通知",
|
||||
|
||||
@@ -15,8 +15,8 @@ define('forum/category', [
|
||||
], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination, storage) {
|
||||
var Category = {};
|
||||
|
||||
$(window).on('action:ajaxify.end', function (ev, data) {
|
||||
if (data.tpl_url !== 'category') {
|
||||
$(window).on('action:ajaxify.start', function (ev, data) {
|
||||
if (data.url && !data.url.startsWith('category/')) {
|
||||
navigator.disable();
|
||||
|
||||
removeListeners();
|
||||
|
||||
@@ -23,16 +23,14 @@ define('forum/topic', [
|
||||
Topic.replaceURLTimeout = 0;
|
||||
}
|
||||
|
||||
if (ajaxify.currentPage !== data.url) {
|
||||
if (data.url && !data.url.startsWith('topic/')) {
|
||||
navigator.disable();
|
||||
components.get('navbar/title').find('span').text('').hide();
|
||||
app.removeAlert('bookmark');
|
||||
|
||||
events.removeListeners();
|
||||
$(window).off('keydown', onKeyDown);
|
||||
}
|
||||
|
||||
if (data.url && !data.url.startsWith('topic/')) {
|
||||
require(['search'], function (search) {
|
||||
if (search.topicDOM.active) {
|
||||
search.topicDOM.end();
|
||||
|
||||
@@ -199,7 +199,7 @@ define('forum/topic/postTools', [
|
||||
var selectedNode = getSelectedNode();
|
||||
|
||||
showStaleWarning(function () {
|
||||
var username = getUserName(button);
|
||||
var username = getUserSlug(button);
|
||||
if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) {
|
||||
username = '';
|
||||
}
|
||||
@@ -231,7 +231,7 @@ define('forum/topic/postTools', [
|
||||
var selectedNode = getSelectedNode();
|
||||
|
||||
showStaleWarning(function () {
|
||||
var username = getUserName(button);
|
||||
var username = getUserSlug(button);
|
||||
var toPid = getData(button, 'data-pid');
|
||||
|
||||
function quote(text) {
|
||||
@@ -284,7 +284,7 @@ define('forum/topic/postTools', [
|
||||
selectedText = range.toString();
|
||||
var postEl = $(content).parents('[component="post"]');
|
||||
selectedPid = postEl.attr('data-pid');
|
||||
username = getUserName($(content));
|
||||
username = getUserSlug($(content));
|
||||
range.detach();
|
||||
}
|
||||
return { text: selectedText, pid: selectedPid, username: username };
|
||||
@@ -309,22 +309,22 @@ define('forum/topic/postTools', [
|
||||
return button.parents('[data-pid]').attr(data);
|
||||
}
|
||||
|
||||
function getUserName(button) {
|
||||
var username = '';
|
||||
function getUserSlug(button) {
|
||||
var slug = '';
|
||||
var post = button.parents('[data-pid]');
|
||||
|
||||
if (button.attr('component') === 'topic/reply') {
|
||||
return username;
|
||||
return slug;
|
||||
}
|
||||
|
||||
if (post.length) {
|
||||
username = post.attr('data-username').replace(/\s/g, '-');
|
||||
slug = post.attr('data-userslug');
|
||||
}
|
||||
if (post.length && post.attr('data-uid') !== '0') {
|
||||
username = '@' + username;
|
||||
slug = '@' + slug;
|
||||
}
|
||||
|
||||
return username;
|
||||
return slug;
|
||||
}
|
||||
|
||||
function togglePostDelete(button, tid) {
|
||||
|
||||
@@ -470,6 +470,9 @@ define('settings', function () {
|
||||
}
|
||||
}
|
||||
|
||||
// Save loaded settings into ajaxify.data for use client-side
|
||||
ajaxify.data.settings = values;
|
||||
|
||||
$(formEl).deserialize(values);
|
||||
$(formEl).find('input[type="checkbox"]').each(function () {
|
||||
$(this).parents('.mdl-switch').toggleClass('is-checked', $(this).is(':checked'));
|
||||
@@ -510,6 +513,9 @@ define('settings', function () {
|
||||
// Remove unsaved flag to re-enable ajaxify
|
||||
app.flags._unsaved = false;
|
||||
|
||||
// Also save to local ajaxify.data
|
||||
ajaxify.data.settings = values;
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else if (err) {
|
||||
|
||||
@@ -154,12 +154,10 @@ categoryController.get = function (req, res, callback) {
|
||||
categoryData.description = translator.escape(categoryData.description);
|
||||
categoryData.privileges = userPrivileges;
|
||||
categoryData.showSelect = categoryData.privileges.editable;
|
||||
|
||||
categoryData.rssFeedUrl = nconf.get('url') + '/category/' + categoryData.cid + '.rss';
|
||||
if (parseInt(req.uid, 10)) {
|
||||
categories.markAsRead([cid], req.uid);
|
||||
categoryData.rssFeedUrl = nconf.get('url') + '/category/' + categoryData.cid + '.rss?uid=' + req.uid + '&token=' + rssToken;
|
||||
} else {
|
||||
categoryData.rssFeedUrl = nconf.get('url') + '/category/' + categoryData.cid + '.rss';
|
||||
categoryData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken;
|
||||
}
|
||||
|
||||
addTags(categoryData, res);
|
||||
|
||||
@@ -15,7 +15,7 @@ var helpers = require('./helpers');
|
||||
var pagination = require('../pagination');
|
||||
var utils = require('../utils');
|
||||
|
||||
var topicsController = {};
|
||||
var topicsController = module.exports;
|
||||
|
||||
topicsController.get = function (req, res, callback) {
|
||||
var tid = req.params.topic_id;
|
||||
@@ -23,6 +23,7 @@ topicsController.get = function (req, res, callback) {
|
||||
var pageCount = 1;
|
||||
var userPrivileges;
|
||||
var settings;
|
||||
var rssToken;
|
||||
|
||||
if ((req.params.post_index && !utils.isNumber(req.params.post_index)) || !utils.isNumber(tid)) {
|
||||
return callback();
|
||||
@@ -40,6 +41,9 @@ topicsController.get = function (req, res, callback) {
|
||||
topic: function (next) {
|
||||
topics.getTopicData(tid, next);
|
||||
},
|
||||
rssToken: function (next) {
|
||||
user.auth.getFeedToken(req.uid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
@@ -48,6 +52,7 @@ topicsController.get = function (req, res, callback) {
|
||||
}
|
||||
|
||||
userPrivileges = results.privileges;
|
||||
rssToken = results.rssToken;
|
||||
|
||||
if (!userPrivileges['topics:read'] || (parseInt(results.topic.deleted, 10) && !userPrivileges.view_deleted)) {
|
||||
return helpers.notAllowed(req, res);
|
||||
@@ -129,167 +134,173 @@ topicsController.get = function (req, res, callback) {
|
||||
plugins.fireHook('filter:controllers.topic.get', { topicData: topicData, uid: req.uid }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
var breadcrumbs = [
|
||||
{
|
||||
text: data.topicData.category.name,
|
||||
url: nconf.get('relative_path') + '/category/' + data.topicData.category.slug,
|
||||
},
|
||||
{
|
||||
text: data.topicData.title,
|
||||
},
|
||||
];
|
||||
|
||||
helpers.buildCategoryBreadcrumbs(data.topicData.category.parentCid, function (err, crumbs) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
data.topicData.breadcrumbs = crumbs.concat(breadcrumbs);
|
||||
next(null, data.topicData);
|
||||
});
|
||||
buildBreadcrumbs(data.topicData, next);
|
||||
},
|
||||
function (topicData, next) {
|
||||
function findPost(index) {
|
||||
for (var i = 0; i < topicData.posts.length; i += 1) {
|
||||
if (parseInt(topicData.posts[i].index, 10) === parseInt(index, 10)) {
|
||||
return topicData.posts[i];
|
||||
function (topicData) {
|
||||
topicData.privileges = userPrivileges;
|
||||
topicData.topicStaleDays = parseInt(meta.config.topicStaleDays, 10) || 60;
|
||||
topicData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
|
||||
topicData['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1;
|
||||
topicData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
|
||||
topicData.bookmarkThreshold = parseInt(meta.config.bookmarkThreshold, 10) || 5;
|
||||
topicData.postEditDuration = parseInt(meta.config.postEditDuration, 10) || 0;
|
||||
topicData.postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10) || 0;
|
||||
topicData.scrollToMyPost = settings.scrollToMyPost;
|
||||
topicData.rssFeedUrl = nconf.get('relative_path') + '/topic/' + topicData.tid + '.rss';
|
||||
if (req.uid) {
|
||||
topicData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken;
|
||||
}
|
||||
topicData.postIndex = req.params.post_index;
|
||||
topicData.pagination = pagination.create(currentPage, pageCount, req.query);
|
||||
topicData.pagination.rel.forEach(function (rel) {
|
||||
rel.href = nconf.get('url') + '/topic/' + topicData.slug + rel.href;
|
||||
res.locals.linkTags.push(rel);
|
||||
});
|
||||
|
||||
req.session.tids_viewed = req.session.tids_viewed || {};
|
||||
if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < Date.now() - 3600000) {
|
||||
topics.increaseViewCount(tid);
|
||||
req.session.tids_viewed[tid] = Date.now();
|
||||
}
|
||||
|
||||
addTags(topicData, req, res);
|
||||
|
||||
if (req.uid) {
|
||||
topics.markAsRead([tid], req.uid, function (err, markedRead) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (markedRead) {
|
||||
topics.pushUnreadCount(req.uid);
|
||||
topics.markTopicNotificationsRead([tid], req.uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
var description = '';
|
||||
var postAtIndex = findPost(Math.max(0, req.params.post_index - 1));
|
||||
|
||||
if (postAtIndex && postAtIndex.content) {
|
||||
description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s;
|
||||
}
|
||||
|
||||
if (description.length > 255) {
|
||||
description = description.substr(0, 255) + '...';
|
||||
}
|
||||
|
||||
var ogImageUrl = '';
|
||||
if (topicData.thumb) {
|
||||
ogImageUrl = topicData.thumb;
|
||||
} else if (postAtIndex && postAtIndex.user && postAtIndex.user.picture) {
|
||||
ogImageUrl = postAtIndex.user.picture;
|
||||
} else if (meta.config['og:image']) {
|
||||
ogImageUrl = meta.config['og:image'];
|
||||
} else if (meta.config['brand:logo']) {
|
||||
ogImageUrl = meta.config['brand:logo'];
|
||||
} else {
|
||||
ogImageUrl = '/logo.png';
|
||||
}
|
||||
|
||||
if (typeof ogImageUrl === 'string' && ogImageUrl.indexOf('http') === -1) {
|
||||
ogImageUrl = nconf.get('url') + ogImageUrl;
|
||||
}
|
||||
|
||||
description = description.replace(/\n/g, ' ');
|
||||
|
||||
res.locals.metaTags = [
|
||||
{
|
||||
name: 'title',
|
||||
content: topicData.titleRaw,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
property: 'og:title',
|
||||
content: topicData.titleRaw,
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'article',
|
||||
},
|
||||
{
|
||||
property: 'og:image',
|
||||
content: ogImageUrl,
|
||||
noEscape: true,
|
||||
},
|
||||
{
|
||||
property: 'og:image:url',
|
||||
content: ogImageUrl,
|
||||
noEscape: true,
|
||||
},
|
||||
{
|
||||
property: 'article:published_time',
|
||||
content: utils.toISOString(topicData.timestamp),
|
||||
},
|
||||
{
|
||||
property: 'article:modified_time',
|
||||
content: utils.toISOString(topicData.lastposttime),
|
||||
},
|
||||
{
|
||||
property: 'article:section',
|
||||
content: topicData.category ? topicData.category.name : '',
|
||||
},
|
||||
];
|
||||
|
||||
res.locals.linkTags = [
|
||||
{
|
||||
rel: 'alternate',
|
||||
type: 'application/rss+xml',
|
||||
href: nconf.get('url') + '/topic/' + tid + '.rss',
|
||||
},
|
||||
];
|
||||
|
||||
if (topicData.category) {
|
||||
res.locals.linkTags.push({
|
||||
rel: 'up',
|
||||
href: nconf.get('url') + '/category/' + topicData.category.slug,
|
||||
});
|
||||
}
|
||||
|
||||
res.render('topic', topicData);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
function buildBreadcrumbs(topicData, callback) {
|
||||
var breadcrumbs = [
|
||||
{
|
||||
text: topicData.category.name,
|
||||
url: nconf.get('relative_path') + '/category/' + topicData.category.slug,
|
||||
},
|
||||
{
|
||||
text: topicData.title,
|
||||
},
|
||||
];
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
helpers.buildCategoryBreadcrumbs(topicData.category.parentCid, next);
|
||||
},
|
||||
function (crumbs, next) {
|
||||
topicData.breadcrumbs = crumbs.concat(breadcrumbs);
|
||||
next(null, topicData);
|
||||
},
|
||||
], function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
data.privileges = userPrivileges;
|
||||
data.topicStaleDays = parseInt(meta.config.topicStaleDays, 10) || 60;
|
||||
data['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
|
||||
data['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1;
|
||||
data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
|
||||
data.bookmarkThreshold = parseInt(meta.config.bookmarkThreshold, 10) || 5;
|
||||
data.postEditDuration = parseInt(meta.config.postEditDuration, 10) || 0;
|
||||
data.postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10) || 0;
|
||||
data.scrollToMyPost = settings.scrollToMyPost;
|
||||
data.rssFeedUrl = nconf.get('relative_path') + '/topic/' + data.tid + '.rss';
|
||||
data.postIndex = req.params.post_index;
|
||||
data.pagination = pagination.create(currentPage, pageCount, req.query);
|
||||
data.pagination.rel.forEach(function (rel) {
|
||||
rel.href = nconf.get('url') + '/topic/' + data.slug + rel.href;
|
||||
res.locals.linkTags.push(rel);
|
||||
function addTags(topicData, req, res) {
|
||||
function findPost(index) {
|
||||
for (var i = 0; i < topicData.posts.length; i += 1) {
|
||||
if (parseInt(topicData.posts[i].index, 10) === parseInt(index, 10)) {
|
||||
return topicData.posts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
var description = '';
|
||||
var postAtIndex = findPost(Math.max(0, req.params.post_index - 1));
|
||||
|
||||
if (postAtIndex && postAtIndex.content) {
|
||||
description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s;
|
||||
}
|
||||
|
||||
if (description.length > 255) {
|
||||
description = description.substr(0, 255) + '...';
|
||||
}
|
||||
|
||||
var ogImageUrl = '';
|
||||
if (topicData.thumb) {
|
||||
ogImageUrl = topicData.thumb;
|
||||
} else if (postAtIndex && postAtIndex.user && postAtIndex.user.picture) {
|
||||
ogImageUrl = postAtIndex.user.picture;
|
||||
} else if (meta.config['og:image']) {
|
||||
ogImageUrl = meta.config['og:image'];
|
||||
} else if (meta.config['brand:logo']) {
|
||||
ogImageUrl = meta.config['brand:logo'];
|
||||
} else {
|
||||
ogImageUrl = '/logo.png';
|
||||
}
|
||||
|
||||
if (typeof ogImageUrl === 'string' && ogImageUrl.indexOf('http') === -1) {
|
||||
ogImageUrl = nconf.get('url') + ogImageUrl;
|
||||
}
|
||||
|
||||
description = description.replace(/\n/g, ' ');
|
||||
res.locals.metaTags = [
|
||||
{
|
||||
name: 'title',
|
||||
content: topicData.titleRaw,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
property: 'og:title',
|
||||
content: topicData.titleRaw,
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'article',
|
||||
},
|
||||
{
|
||||
property: 'og:image',
|
||||
content: ogImageUrl,
|
||||
noEscape: true,
|
||||
},
|
||||
{
|
||||
property: 'og:image:url',
|
||||
content: ogImageUrl,
|
||||
noEscape: true,
|
||||
},
|
||||
{
|
||||
property: 'article:published_time',
|
||||
content: utils.toISOString(topicData.timestamp),
|
||||
},
|
||||
{
|
||||
property: 'article:modified_time',
|
||||
content: utils.toISOString(topicData.lastposttime),
|
||||
},
|
||||
{
|
||||
property: 'article:section',
|
||||
content: topicData.category ? topicData.category.name : '',
|
||||
},
|
||||
];
|
||||
|
||||
res.locals.linkTags = [
|
||||
{
|
||||
rel: 'alternate',
|
||||
type: 'application/rss+xml',
|
||||
href: topicData.rssFeedUrl,
|
||||
},
|
||||
];
|
||||
|
||||
if (topicData.category) {
|
||||
res.locals.linkTags.push({
|
||||
rel: 'up',
|
||||
href: nconf.get('url') + '/category/' + topicData.category.slug,
|
||||
});
|
||||
|
||||
req.session.tids_viewed = req.session.tids_viewed || {};
|
||||
if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < Date.now() - 3600000) {
|
||||
topics.increaseViewCount(tid);
|
||||
req.session.tids_viewed[tid] = Date.now();
|
||||
}
|
||||
|
||||
if (req.uid) {
|
||||
topics.markAsRead([tid], req.uid, function (err, markedRead) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (markedRead) {
|
||||
topics.pushUnreadCount(req.uid);
|
||||
topics.markTopicNotificationsRead([tid], req.uid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
res.render('topic', data);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
topicsController.teaser = function (req, res, next) {
|
||||
var tid = req.params.topic_id;
|
||||
@@ -355,5 +366,3 @@ topicsController.pagination = function (req, res, callback) {
|
||||
res.json(paginationData);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = topicsController;
|
||||
|
||||
@@ -206,18 +206,18 @@ mongoModule.info = function (db, callback) {
|
||||
|
||||
stats.mem = results.serverStatus.mem;
|
||||
stats.mem = results.serverStatus.mem;
|
||||
stats.mem.resident = (stats.mem.resident / 1024).toFixed(2);
|
||||
stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(2);
|
||||
stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(2);
|
||||
stats.mem.resident = (stats.mem.resident / 1024).toFixed(3);
|
||||
stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(3);
|
||||
stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(3);
|
||||
stats.collectionData = results.listCollections;
|
||||
stats.network = results.serverStatus.network;
|
||||
stats.raw = JSON.stringify(stats, null, 4);
|
||||
|
||||
stats.avgObjSize = stats.avgObjSize.toFixed(2);
|
||||
stats.dataSize = (stats.dataSize / scale).toFixed(2);
|
||||
stats.storageSize = (stats.storageSize / scale).toFixed(2);
|
||||
stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0;
|
||||
stats.indexSize = (stats.indexSize / scale).toFixed(2);
|
||||
stats.dataSize = (stats.dataSize / scale).toFixed(3);
|
||||
stats.storageSize = (stats.storageSize / scale).toFixed(3);
|
||||
stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(3) : 0;
|
||||
stats.indexSize = (stats.indexSize / scale).toFixed(3);
|
||||
stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1';
|
||||
stats.host = results.serverStatus.host;
|
||||
stats.version = results.serverStatus.version;
|
||||
|
||||
@@ -152,7 +152,7 @@ redisModule.info = function (cxn, callback) {
|
||||
redisData[parts[0]] = parts[1];
|
||||
}
|
||||
});
|
||||
redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(2);
|
||||
redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(3);
|
||||
redisData.raw = JSON.stringify(redisData, null, 4);
|
||||
redisData.redis = true;
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ Settings.set = function (hash, values, callback) {
|
||||
};
|
||||
|
||||
Settings.setOne = function (hash, field, value, callback) {
|
||||
db.setObjectField('settings:' + hash, field, value, callback);
|
||||
var data = {};
|
||||
data[field] = value;
|
||||
Settings.set(hash, data, callback);
|
||||
};
|
||||
|
||||
Settings.setOnEmpty = function (hash, values, callback) {
|
||||
@@ -54,7 +56,7 @@ Settings.setOnEmpty = function (hash, values, callback) {
|
||||
});
|
||||
|
||||
if (Object.keys(empty).length) {
|
||||
db.setObject('settings:' + hash, empty, next);
|
||||
Settings.set(hash, empty, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = function (app, middleware) {
|
||||
app.get('/tags/:tag.rss', middleware.maintenanceMode, generateForTag);
|
||||
};
|
||||
|
||||
function validateTokenIfRequiresLogin(requiresLogin, req, res, callback) {
|
||||
function validateTokenIfRequiresLogin(requiresLogin, cid, req, res, callback) {
|
||||
var uid = req.query.uid;
|
||||
var token = req.query.token;
|
||||
|
||||
@@ -38,23 +38,31 @@ function validateTokenIfRequiresLogin(requiresLogin, req, res, callback) {
|
||||
return helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'rss_token', function (err, _token) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (token === _token) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
user.auth.logAttempt(uid, req.ip, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUserField(uid, 'rss_token', next);
|
||||
},
|
||||
function (_token, next) {
|
||||
if (token === _token) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.categories.get(cid, uid, next);
|
||||
},
|
||||
function (privileges, next) {
|
||||
if (!privileges.read) {
|
||||
return helpers.notAllowed(req, res);
|
||||
}
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
return;
|
||||
}
|
||||
|
||||
return helpers.notAllowed(req, res);
|
||||
});
|
||||
});
|
||||
user.auth.logAttempt(uid, req.ip, next);
|
||||
},
|
||||
function () {
|
||||
helpers.notAllowed(req, res);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
function generateForTopic(req, res, callback) {
|
||||
@@ -64,6 +72,7 @@ function generateForTopic(req, res, callback) {
|
||||
|
||||
var tid = req.params.topic_id;
|
||||
var userPrivileges;
|
||||
var topic;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
@@ -79,15 +88,12 @@ function generateForTopic(req, res, callback) {
|
||||
if (!results.topic || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) {
|
||||
return controllers404.send404(req, res);
|
||||
}
|
||||
|
||||
validateTokenIfRequiresLogin(!results.privileges['topics:read'], req, res, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
userPrivileges = results.privileges;
|
||||
topics.getTopicWithPosts(results.topic, 'tid:' + tid + ':posts', req.uid, 0, 25, false, next);
|
||||
});
|
||||
userPrivileges = results.privileges;
|
||||
topic = results.topic;
|
||||
validateTokenIfRequiresLogin(!results.privileges['topics:read'], results.topic.cid, req, res, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.getTopicWithPosts(topic, 'tid:' + tid + ':posts', req.uid || req.query.uid || 0, 0, 25, false, next);
|
||||
},
|
||||
function (topicData) {
|
||||
topics.modifyPostsByPrivilege(topicData, userPrivileges);
|
||||
@@ -130,40 +136,12 @@ function generateForTopic(req, res, callback) {
|
||||
], callback);
|
||||
}
|
||||
|
||||
function generateForUserTopics(req, res, callback) {
|
||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||
return controllers404.send404(req, res);
|
||||
}
|
||||
|
||||
var userslug = req.params.userslug;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUidByUserslug(userslug, next);
|
||||
},
|
||||
function (uid, next) {
|
||||
if (!uid) {
|
||||
return callback();
|
||||
}
|
||||
user.getUserFields(uid, ['uid', 'username'], next);
|
||||
},
|
||||
function (userData, next) {
|
||||
generateForTopics({
|
||||
uid: req.uid,
|
||||
title: 'Topics by ' + userData.username,
|
||||
description: 'A list of topics that are posted by ' + userData.username,
|
||||
feed_url: '/user/' + userslug + '/topics.rss',
|
||||
site_url: '/user/' + userslug + '/topics',
|
||||
}, 'uid:' + userData.uid + ':topics', req, res, next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
function generateForCategory(req, res, next) {
|
||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||
return controllers404.send404(req, res);
|
||||
}
|
||||
var cid = req.params.category_id;
|
||||
var category;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
@@ -178,25 +156,23 @@ function generateForCategory(req, res, next) {
|
||||
reverse: true,
|
||||
start: 0,
|
||||
stop: 25,
|
||||
uid: req.uid,
|
||||
uid: req.uid || req.query.uid || 0,
|
||||
}, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
validateTokenIfRequiresLogin(!results.privileges.read, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
generateTopicsFeed({
|
||||
uid: req.uid,
|
||||
title: results.category.name,
|
||||
description: results.category.description,
|
||||
feed_url: '/category/' + cid + '.rss',
|
||||
site_url: '/category/' + results.category.cid,
|
||||
}, results.category.topics, next);
|
||||
});
|
||||
category = results.category;
|
||||
validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, next);
|
||||
},
|
||||
function (next) {
|
||||
generateTopicsFeed({
|
||||
uid: req.uid || req.query.uid || 0,
|
||||
title: category.name,
|
||||
description: category.description,
|
||||
feed_url: '/category/' + cid + '.rss',
|
||||
site_url: '/category/' + category.cid,
|
||||
}, category.topics, next);
|
||||
},
|
||||
function (feed) {
|
||||
sendFeed(feed, res);
|
||||
@@ -330,12 +306,13 @@ function generateForRecentPosts(req, res, next) {
|
||||
], next);
|
||||
}
|
||||
|
||||
function generateForCategoryRecentPosts(req, res, next) {
|
||||
function generateForCategoryRecentPosts(req, res, callback) {
|
||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||
return controllers404.send404(req, res);
|
||||
}
|
||||
var cid = req.params.category_id;
|
||||
|
||||
var category;
|
||||
var posts;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
@@ -346,31 +323,29 @@ function generateForCategoryRecentPosts(req, res, next) {
|
||||
categories.getCategoryData(cid, next);
|
||||
},
|
||||
posts: function (next) {
|
||||
categories.getRecentReplies(cid, req.uid, 20, next);
|
||||
categories.getRecentReplies(cid, req.uid || req.query.uid || 0, 20, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
if (!results.category) {
|
||||
return next();
|
||||
return controllers404.send404(req, res);
|
||||
}
|
||||
|
||||
validateTokenIfRequiresLogin(!results.privileges.read, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var feed = generateForPostsFeed({
|
||||
title: results.category.name + ' Recent Posts',
|
||||
description: 'A list of recent posts from ' + results.category.name,
|
||||
feed_url: '/category/' + cid + '/recentposts.rss',
|
||||
site_url: '/category/' + cid + '/recentposts',
|
||||
}, results.posts);
|
||||
|
||||
sendFeed(feed, res);
|
||||
});
|
||||
category = results.category;
|
||||
posts = results.posts;
|
||||
validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, next);
|
||||
},
|
||||
], next);
|
||||
function () {
|
||||
var feed = generateForPostsFeed({
|
||||
title: category.name + ' Recent Posts',
|
||||
description: 'A list of recent posts from ' + category.name,
|
||||
feed_url: '/category/' + cid + '/recentposts.rss',
|
||||
site_url: '/category/' + cid + '/recentposts',
|
||||
}, posts);
|
||||
|
||||
sendFeed(feed, res);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
function generateForPostsFeed(feedOptions, posts) {
|
||||
@@ -397,6 +372,35 @@ function generateForPostsFeed(feedOptions, posts) {
|
||||
return feed;
|
||||
}
|
||||
|
||||
function generateForUserTopics(req, res, callback) {
|
||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||
return controllers404.send404(req, res);
|
||||
}
|
||||
|
||||
var userslug = req.params.userslug;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUidByUserslug(userslug, next);
|
||||
},
|
||||
function (uid, next) {
|
||||
if (!uid) {
|
||||
return callback();
|
||||
}
|
||||
user.getUserFields(uid, ['uid', 'username'], next);
|
||||
},
|
||||
function (userData, next) {
|
||||
generateForTopics({
|
||||
uid: req.uid,
|
||||
title: 'Topics by ' + userData.username,
|
||||
description: 'A list of topics that are posted by ' + userData.username,
|
||||
feed_url: '/user/' + userslug + '/topics.rss',
|
||||
site_url: '/user/' + userslug + '/topics',
|
||||
}, 'uid:' + userData.uid + ':topics', req, res, next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
function generateForTag(req, res, next) {
|
||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||
return controllers404.send404(req, res);
|
||||
|
||||
@@ -187,7 +187,10 @@ SocketAdmin.config.setMultiple = function (socket, data, callback) {
|
||||
logger.monitorConfig({ io: index.server }, setting);
|
||||
}
|
||||
}
|
||||
setImmediate(next);
|
||||
data.type = 'config-change';
|
||||
data.uid = socket.uid;
|
||||
data.ip = socket.ip;
|
||||
events.log(data, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
@@ -201,7 +204,19 @@ SocketAdmin.settings.get = function (socket, data, callback) {
|
||||
};
|
||||
|
||||
SocketAdmin.settings.set = function (socket, data, callback) {
|
||||
meta.settings.set(data.hash, data.values, callback);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
meta.settings.set(data.hash, data.values, next);
|
||||
},
|
||||
function (next) {
|
||||
var eventData = data.values;
|
||||
eventData.type = 'settings-change';
|
||||
eventData.uid = socket.uid;
|
||||
eventData.ip = socket.ip;
|
||||
eventData.hash = data.hash;
|
||||
events.log(eventData, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketAdmin.settings.clearSitemapCache = function (socket, data, callback) {
|
||||
|
||||
@@ -98,7 +98,7 @@ function setupConfigs() {
|
||||
nconf.set('secure', urlObject.protocol === 'https:');
|
||||
nconf.set('use_port', !!urlObject.port);
|
||||
nconf.set('relative_path', relativePath);
|
||||
nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
|
||||
nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
|
||||
nconf.set('upload_url', '/assets/uploads');
|
||||
}
|
||||
|
||||
|
||||
@@ -52,19 +52,23 @@ module.exports = function (User) {
|
||||
if (!uid) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
User.getUserField(uid, 'rss_token', function (err, token) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
token = utils.generateUUID();
|
||||
User.setUserField(uid, 'rss_token', token);
|
||||
}
|
||||
|
||||
callback(false, token);
|
||||
});
|
||||
var token;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUserField(uid, 'rss_token', next);
|
||||
},
|
||||
function (_token, next) {
|
||||
token = _token || utils.generateUUID();
|
||||
if (!_token) {
|
||||
User.setUserField(uid, 'rss_token', token, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
next(null, token);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.auth.clearLoginAttempts = function (uid) {
|
||||
|
||||
@@ -133,8 +133,8 @@ function setupExpressApp(app, callback) {
|
||||
|
||||
app.use(compression());
|
||||
|
||||
app.get('/ping', ping);
|
||||
app.get('/sping', ping);
|
||||
app.get(relativePath + '/ping', ping);
|
||||
app.get(relativePath + '/sping', ping);
|
||||
|
||||
setupFavicon(app);
|
||||
|
||||
@@ -231,6 +231,7 @@ function setupAutoLocale(app, callback) {
|
||||
|
||||
function listen(callback) {
|
||||
callback = callback || function () { };
|
||||
console.log('derp', nconf.get('port'));
|
||||
var port = parseInt(nconf.get('port'), 10);
|
||||
var isSocket = isNaN(port);
|
||||
var socketPath = isSocket ? nconf.get('port') : '';
|
||||
|
||||
@@ -12,6 +12,7 @@ var groups = require('../src/groups');
|
||||
var user = require('../src/user');
|
||||
var meta = require('../src/meta');
|
||||
var privileges = require('../src/privileges');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
describe('feeds', function () {
|
||||
var tid;
|
||||
@@ -113,4 +114,81 @@ describe('feeds', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('private feeds and tokens', function () {
|
||||
var jar;
|
||||
var rssToken;
|
||||
before(function (done) {
|
||||
helpers.loginUser('foo', 'barbar', function (err, _jar) {
|
||||
assert.ifError(err);
|
||||
jar = _jar;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load feed if its not private', function (done) {
|
||||
request(nconf.get('url') + '/category/' + cid + '.rss', { }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not allow access if uid or token is missing', function (done) {
|
||||
privileges.categories.rescind(['read'], cid, 'guests', function (err) {
|
||||
assert.ifError(err);
|
||||
async.parallel({
|
||||
test1: function (next) {
|
||||
request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid, { }, next);
|
||||
},
|
||||
test2: function (next) {
|
||||
request(nconf.get('url') + '/category/' + cid + '.rss?token=sometoken', { }, next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
assert.ifError(err);
|
||||
assert.equal(results.test1[0].statusCode, 200);
|
||||
assert.equal(results.test2[0].statusCode, 200);
|
||||
assert(results.test1[0].body.indexOf('Login to your account') !== -1);
|
||||
assert(results.test2[0].body.indexOf('Login to your account') !== -1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow access if token is wrong', function (done) {
|
||||
request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=sometoken', { }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body.indexOf('Login to your account') !== -1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow access if token is correct', function (done) {
|
||||
request(nconf.get('url') + '/api/category/' + cid, { jar: jar, json: true }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
rssToken = body.rssFeedUrl.split('token')[1].slice(1);
|
||||
request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=' + rssToken, { }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow access if token is correct but has no privilege', function (done) {
|
||||
privileges.categories.rescind(['read'], cid, 'registered-users', function (err) {
|
||||
assert.ifError(err);
|
||||
request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=' + rssToken, { }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body.indexOf('Login to your account') !== -1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -110,7 +110,7 @@ before(function (done) {
|
||||
nconf.set('secure', urlObject.protocol === 'https:');
|
||||
nconf.set('use_port', !!urlObject.port);
|
||||
nconf.set('relative_path', relativePath);
|
||||
nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
|
||||
nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
|
||||
nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path')));
|
||||
|
||||
nconf.set('core_templates_path', path.join(__dirname, '../../src/views'));
|
||||
|
||||
Reference in New Issue
Block a user