Merge remote-tracking branch 'origin/master' into develop

This commit is contained in:
Julian Lam
2018-02-23 14:48:38 -05:00
453 changed files with 1073 additions and 513 deletions

View File

@@ -89,7 +89,7 @@ function renderRoute(name, req, res, next) {
if (name === 'password') {
userData.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
userData.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10);
userData.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 1, 10);
}
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';

View File

@@ -33,11 +33,18 @@ pluginsController.get = function (req, res, next) {
var compatiblePkgNames = payload.compatible.map(function (pkgData) {
return pkgData.name;
});
var installedPlugins = payload.compatible.filter(function (plugin) {
return plugin && plugin.installed;
});
var activePlugins = payload.all.filter(function (plugin) {
return plugin && plugin.installed && plugin.active;
});
res.render('admin/extend/plugins', {
installed: payload.compatible.filter(function (plugin) {
return plugin.installed;
}),
installed: installedPlugins,
installedCount: installedPlugins.length,
activeCount: activePlugins.length,
inactiveCount: Math.max(0, installedPlugins.length - activePlugins.length),
upgradeCount: payload.compatible.reduce(function (count, current) {
if (current.installed && current.outdated) {
count += 1;

View File

@@ -166,7 +166,7 @@ Controllers.register = function (req, res, next) {
data.minimumUsernameLength = parseInt(meta.config.minimumUsernameLength, 10);
data.maximumUsernameLength = parseInt(meta.config.maximumUsernameLength, 10);
data.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
data.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10);
data.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 1, 10);
data.termsOfUse = termsOfUse.postData.content;
data.breadcrumbs = helpers.buildBreadcrumbs([{
text: '[[register:register]]',

View File

@@ -1,5 +1,7 @@
'use strict';
var os = require('os');
var meta = require('../meta');
module.exports = function (middleware) {
@@ -15,6 +17,10 @@ module.exports = function (middleware) {
headers['Access-Control-Allow-Origin'] = encodeURI(meta.config['access-control-allow-origin']);
}
if (process.env.NODE_ENV === 'development') {
headers['X-Upstream-Hostname'] = os.hostname();
}
for (var key in headers) {
if (headers.hasOwnProperty(key) && headers[key]) {
res.setHeader(key, headers[key]);

View File

@@ -211,7 +211,7 @@ middleware.templatesOnDemand = function (req, res, next) {
if (!filePath.endsWith('.js')) {
return next();
}
var tplPath = filePath.replace(/\.js$/, '.tpl');
if (workingCache[filePath]) {
workingCache[filePath].push(next);
return;
@@ -234,12 +234,9 @@ middleware.templatesOnDemand = function (req, res, next) {
}
workingCache[filePath] = [next];
fs.readFile(filePath.replace(/\.js$/, '.tpl'), 'utf8', cb);
fs.readFile(tplPath, 'utf8', cb);
},
function (source, cb) {
if (!source) {
return cb(new Error('[[error:templatesOnDemand.source-template-empty]]'));
}
Benchpress.precompile({
source: source,
minify: global.env !== 'development',
@@ -247,7 +244,7 @@ middleware.templatesOnDemand = function (req, res, next) {
},
function (compiled, cb) {
if (!compiled) {
return cb(new Error('[[error:templatesOnDemand.compiled-template-empty]]'));
return cb(new Error('[[error:templatesOnDemand.compiled-template-empty, ' + tplPath + ']]'));
}
fs.writeFile(filePath, compiled, cb);
},

View File

@@ -15,17 +15,13 @@ pubsub.on('admin:navigation:save', function () {
admin.save = function (data, callback) {
var order = Object.keys(data);
var items = data.map(function (item, idx) {
var data = {};
var items = data.map(function (item) {
for (var i in item) {
if (item.hasOwnProperty(i)) {
item[i] = typeof item[i] === 'string' ? translator.escape(item[i]) : item[i];
if (item.hasOwnProperty(i) && typeof item[i] === 'string' && (i === 'title' || i === 'text')) {
item[i] = translator.escape(item[i]);
}
}
data[idx] = item;
return JSON.stringify(data);
return JSON.stringify(item);
});
admin.cache = null;
@@ -53,8 +49,8 @@ admin.get = function (callback) {
db.getSortedSetRange('navigation:enabled', 0, -1, next);
},
function (data, next) {
data = data.map(function (item, idx) {
return JSON.parse(item)[idx];
data = data.map(function (item) {
return JSON.parse(item);
});
next(null, data);

View File

@@ -38,6 +38,3 @@ navigation.get = function (callback) {
},
], callback);
};
module.exports = navigation;

View File

@@ -1,5 +1,6 @@
'use strict';
var os = require('os');
var async = require('async');
var nconf = require('nconf');
var winston = require('winston');
@@ -84,6 +85,7 @@ function onConnect(socket) {
socket.join('sess_' + socket.request.signedCookies[nconf.get('sessionKey')]);
io.sockets.sockets[socket.id].emit('checkSession', socket.uid);
io.sockets.sockets[socket.id].emit('setHostname', os.hostname());
}
function onMessage(socket, payload) {

View File

@@ -30,7 +30,7 @@ module.exports = function (Topics) {
};
Topics.getTopicBookmarks = function (tid, callback) {
db.getSortedSetRangeWithScores(['tid:' + tid + ':bookmarks'], 0, -1, callback);
db.getSortedSetRangeWithScores('tid:' + tid + ':bookmarks', 0, -1, callback);
};
Topics.updateTopicBookmarks = function (tid, pids, callback) {

View File

@@ -0,0 +1,38 @@
'use strict';
var async = require('async');
var db = require('../../database');
module.exports = {
name: 'Flatten navigation data',
timestamp: Date.UTC(2018, 1, 17),
method: function (callback) {
async.waterfall([
function (next) {
db.getSortedSetRangeWithScores('navigation:enabled', 0, -1, next);
},
function (data, next) {
var order = [];
var items = [];
data.forEach(function (item) {
var navItem = JSON.parse(item.value);
var keys = Object.keys(navItem);
if (keys.length && parseInt(keys[0], 10) >= 0) {
navItem = navItem[keys[0]];
}
order.push(item.score);
items.push(JSON.stringify(navItem));
});
async.series([
function (next) {
db.delete('navigation:enabled', next);
},
function (next) {
db.sortedSetAdd('navigation:enabled', order, items, next);
},
], next);
},
], callback);
},
};

View File

@@ -0,0 +1,22 @@
'use strict';
var db = require('../../database');
var async = require('async');
module.exports = {
name: 'Revising minimum password strength to 1 (from 0)',
timestamp: Date.UTC(2017, 1, 21),
method: function (callback) {
async.waterfall([
async.apply(db.getObjectField.bind(db), 'config', 'minimumPasswordStrength'),
function (strength, next) {
if (!strength) {
return db.setObjectField('config', 'minimumPasswordStrength', 1, next);
}
setImmediate(next);
},
], callback);
},
};

View File

@@ -106,14 +106,14 @@ Digest.send = function (data, callback) {
function (next) {
async.parallel({
notifications: async.apply(user.notifications.getDailyUnread, userObj.uid),
popular: async.apply(topics.getPopularTopics, data.interval, userObj.uid, 0, 9),
topics: async.apply(getTermTopics, data.interval, userObj.uid, 0, 9),
}, next);
},
function (data, next) {
var notifications = data.notifications.filter(Boolean);
// If there are no notifications and no new topics, don't bother sending a digest
if (!notifications.length && !data.popular.topics.length) {
if (!notifications.length && !data.topics.length) {
return next();
}
@@ -124,7 +124,7 @@ Digest.send = function (data, callback) {
});
// Fix relative paths in topic data
data.popular.topics = data.popular.topics.map(function (topicObj) {
data.topics = data.topics.map(function (topicObj) {
var user = topicObj.hasOwnProperty('teaser') && topicObj.teaser !== undefined ? topicObj.teaser.user : topicObj.user;
if (user && user.picture && utils.isRelativeUrl(user.picture)) {
user.picture = nconf.get('base_url') + user.picture;
@@ -138,7 +138,7 @@ Digest.send = function (data, callback) {
username: userObj.username,
userslug: userObj.userslug,
notifications: notifications,
recent: data.popular.topics,
recent: data.topics,
interval: data.interval,
showUnsubscribe: true,
}, function (err) {
@@ -154,4 +154,22 @@ Digest.send = function (data, callback) {
], function (err) {
callback(err, emailsSent);
});
function getTermTopics(term, uid, start, stop, callback) {
async.waterfall([
function (next) {
topics.getPopularTopics(term, uid, start, stop, next);
},
function (data, next) {
if (!data.topics.length) {
topics.getLatestTopics(uid, start, stop, term, next);
} else {
next(null, data);
}
},
function (data, next) {
next(null, data.topics);
},
], callback);
}
};

View File

@@ -1,7 +1,16 @@
<ul class="nav nav-pills">
<li class="active"><a href="#installed" data-toggle="tab">[[admin/extend/plugins:installed]]</a></li>
<li><a href="#active" data-toggle="tab">[[admin/extend/plugins:active]]</a></li>
<li><a href="#deactive" data-toggle="tab">[[admin/extend/plugins:inactive]]</a></li>
<li class="active"><a href="#installed" data-toggle="tab">
[[admin/extend/plugins:installed]]
<span class="badge">{installedCount}</span>
</a></li>
<li><a href="#active" data-toggle="tab">
[[admin/extend/plugins:active]]
<span class="badge">{activeCount}</span>
</a></li>
<li><a href="#deactive" data-toggle="tab">
[[admin/extend/plugins:inactive]]
<span class="badge">{inactiveCount}</span>
</a></li>
<li><a href="#upgrade" data-toggle="tab">
[[admin/extend/plugins:out-of-date]]
<span class="badge">{upgradeCount}</span>

View File

@@ -3,6 +3,7 @@
var fs = require('fs');
var path = require('path');
var os = require('os');
var nconf = require('nconf');
var express = require('express');
var app = express();
@@ -17,6 +18,7 @@ var session = require('express-session');
var useragent = require('express-useragent');
var favicon = require('serve-favicon');
var detector = require('spider-detector');
var helmet = require('helmet');
var db = require('./database');
var file = require('./file');
@@ -73,6 +75,7 @@ module.exports.listen = function (callback) {
require('./socket.io').server.emit('event:nodebb.ready', {
'cache-buster': meta.config['cache-buster'],
hostname: os.hostname(),
});
plugins.fireHook('action:nodebb.ready');
@@ -171,6 +174,8 @@ function setupExpressApp(app, callback) {
saveUninitialized: true,
}));
app.use(helmet());
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }));
app.use(middleware.addHeaders);
app.use(middleware.processRender);
auth.initialize(app, middleware);