Merge remote-tracking branch 'refs/remotes/origin/master' into private-feeds

v1.18.x
psychobunny 8 years ago
commit b744f8ce5a

@ -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;
}
var description = '';
var postAtIndex = findPost(Math.max(0, req.params.post_index - 1));
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);
});
if (postAtIndex && postAtIndex.content) {
description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s;
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 (description.length > 255) {
description = description.substr(0, 255) + '...';
}
addTags(topicData, req, res);
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;
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);
}
});
}
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',
},
];
res.render('topic', topicData);
},
], callback);
};
if (topicData.category) {
res.locals.linkTags.push({
rel: 'up',
href: nconf.get('url') + '/category/' + topicData.category.slug,
});
}
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);
}
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));
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);
});
if (postAtIndex && postAtIndex.content) {
description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s;
}
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 (description.length > 255) {
description = description.substr(0, 255) + '...';
}
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 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';
}
res.render('topic', data);
});
};
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,
});
}
}
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);
}
category = results.category;
posts = results.posts;
validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, 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);
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);
});
sendFeed(feed, res);
},
], next);
], 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'));

Loading…
Cancel
Save