Merge branch 'master' into acp-paper

v1.18.x
psychobunny 10 years ago
commit 2da3b34798

@ -0,0 +1,8 @@
FROM node:0.10-onbuild
ENV NODE_ENV=production \
daemon=false \
silent=false
CMD node app --setup && npm start
EXPOSE 4567

@ -5,8 +5,7 @@ var colors = require('colors'),
argv = require('minimist')(process.argv.slice(2)), argv = require('minimist')(process.argv.slice(2)),
fs = require('fs'), fs = require('fs'),
async = require('async'), async = require('async'),
touch = require('touch'), touch = require('touch');
npm = require('npm');
var getRunningPid = function(callback) { var getRunningPid = function(callback) {
fs.readFile(__dirname + '/pidfile', { fs.readFile(__dirname + '/pidfile', {
@ -119,15 +118,12 @@ switch(process.argv[2]) {
case 'upgrade': case 'upgrade':
async.series([ async.series([
function(next) { function(next) {
process.stdout.write('1. '.bold + 'Bringing base dependencies up to date\n'.yellow); process.stdout.write('1. '.bold + 'Bringing base dependencies up to date... '.yellow);
npm.load({ require('child_process').execFile('/usr/bin/env', ['npm', 'i', '--production'], next);
loglevel: 'silent'
}, function() {
npm.commands.install(next);
});
}, },
function(next) { function(next) {
process.stdout.write('2. '.bold + 'Updating NodeBB data store schema\n'.yellow); process.stdout.write('OK\n'.green);
process.stdout.write('2. '.bold + 'Updating NodeBB data store schema.\n'.yellow);
var upgradeProc = cproc.fork('app.js', ['--upgrade'], { var upgradeProc = cproc.fork('app.js', ['--upgrade'], {
cwd: __dirname, cwd: __dirname,
silent: false silent: false
@ -136,7 +132,7 @@ switch(process.argv[2]) {
upgradeProc.on('close', next) upgradeProc.on('close', next)
}, },
function(next) { function(next) {
process.stdout.write('3. '.bold + 'Storing upgrade date in "package.json"\n'.yellow); process.stdout.write('3. '.bold + 'Storing upgrade date in "package.json"... '.yellow);
touch(__dirname + '/package.json', {}, next); touch(__dirname + '/package.json', {}, next);
} }
], function(err) { ], function(err) {
@ -145,6 +141,8 @@ switch(process.argv[2]) {
} else { } else {
var message = 'NodeBB Upgrade Complete!', var message = 'NodeBB Upgrade Complete!',
spaces = new Array(Math.floor(process.stdout.columns / 2) - (message.length / 2) + 1).join(' '); spaces = new Array(Math.floor(process.stdout.columns / 2) - (message.length / 2) + 1).join(' ');
process.stdout.write('OK\n'.green);
process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset); process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset);
} }
}); });

@ -14,14 +14,14 @@
"test": "mocha ./tests -t 10000" "test": "mocha ./tests -t 10000"
}, },
"dependencies": { "dependencies": {
"async": "~0.9.0", "async": "~1.4.2",
"bcryptjs": "~2.1.0", "bcryptjs": "~2.2.1",
"body-parser": "^1.9.0", "body-parser": "^1.9.0",
"colors": "^1.1.0", "colors": "^1.1.0",
"compression": "^1.1.0", "compression": "^1.1.0",
"connect-ensure-login": "^0.1.1", "connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"connect-multiparty": "^1.2.4", "connect-multiparty": "^2.0.0",
"cookie-parser": "^1.3.3", "cookie-parser": "^1.3.3",
"cron": "^1.0.5", "cron": "^1.0.5",
"csurf": "^1.6.1", "csurf": "^1.6.1",
@ -43,37 +43,37 @@
"nodebb-plugin-composer-default": "1.0.9", "nodebb-plugin-composer-default": "1.0.9",
"nodebb-plugin-dbsearch": "0.2.15", "nodebb-plugin-dbsearch": "0.2.15",
"nodebb-plugin-emoji-extended": "0.4.9", "nodebb-plugin-emoji-extended": "0.4.9",
"nodebb-plugin-markdown": "4.0.2", "nodebb-plugin-markdown": "4.0.4",
"nodebb-plugin-mentions": "1.0.0", "nodebb-plugin-mentions": "1.0.0",
"nodebb-plugin-soundpack-default": "0.1.2", "nodebb-plugin-soundpack-default": "0.1.2",
"nodebb-plugin-spam-be-gone": "0.4.1", "nodebb-plugin-spam-be-gone": "0.4.1",
"nodebb-rewards-essentials": "0.0.3", "nodebb-rewards-essentials": "0.0.3",
"nodebb-theme-lavender": "1.0.49", "nodebb-theme-lavender": "1.0.49",
"nodebb-theme-persona": "2.0.13", "nodebb-theme-persona": "2.0.15",
"nodebb-theme-vanilla": "3.0.7", "nodebb-theme-vanilla": "3.0.8",
"nodebb-widget-essentials": "1.0.4", "nodebb-widget-essentials": "1.0.4",
"npm": "^2.1.4", "npm": "^2.1.4",
"passport": "^0.2.1", "passport": "^0.2.1",
"passport-local": "1.0.0", "passport-local": "1.0.0",
"prompt": "^0.2.14", "prompt": "^0.2.14",
"request": "^2.44.0", "request": "^2.44.0",
"rimraf": "~2.3.2", "rimraf": "~2.4.2",
"rss": "^1.0.0", "rss": "^1.0.0",
"semver": "^4.3.3", "semver": "^5.0.1",
"serve-favicon": "^2.1.5", "serve-favicon": "^2.1.5",
"sitemap": "^0.8.1", "sitemap": "^1.0.0",
"socket.io": "^1.2.1", "socket.io": "^1.2.1",
"socket.io-client": "^1.2.1", "socket.io-client": "^1.2.1",
"socket.io-redis": "^0.1.3", "socket.io-redis": "^0.1.3",
"socketio-wildcard": "~0.1.1", "socketio-wildcard": "~0.1.1",
"string": "^3.0.0", "string": "^3.0.0",
"templates.js": "0.2.8", "templates.js": "0.2.10",
"touch": "0.0.3", "touch": "1.0.0",
"uglify-js": "^2.4.23", "uglify-js": "^2.4.23",
"underscore": "~1.8.3", "underscore": "~1.8.3",
"underscore.deep": "^0.5.1", "underscore.deep": "^0.5.1",
"validator": "^3.30.0", "validator": "^4.0.5",
"winston": "^0.9.0", "winston": "^1.0.1",
"xregexp": "~2.0.0" "xregexp": "~2.0.0"
}, },
"devDependencies": { "devDependencies": {

@ -50,11 +50,11 @@ define('admin/manage/category', [
function enableColorPicker(idx, inputEl) { function enableColorPicker(idx, inputEl) {
var $inputEl = $(inputEl), var $inputEl = $(inputEl),
previewEl = $inputEl.parents('[data-cid]').find('.preview-box'); previewEl = $inputEl.parents('[data-cid]').find('.category-preview');
colorpicker.enable($inputEl, function(hsb, hex) { colorpicker.enable($inputEl, function(hsb, hex) {
if ($inputEl.attr('data-name') === 'bgColor') { if ($inputEl.attr('data-name') === 'bgColor') {
previewEl.css('background', '#' + hex); previewEl.css('background-color', '#' + hex);
} else if ($inputEl.attr('data-name') === 'color') { } else if ($inputEl.attr('data-name') === 'color') {
previewEl.css('color', '#' + hex); previewEl.css('color', '#' + hex);
} }
@ -83,6 +83,11 @@ define('admin/manage/category', [
modified(ev.target); modified(ev.target);
}); });
// Update preview image size on change
$('[data-name="imageClass"]').on('change', function(ev) {
$('.category-preview').css('background-size', $(this).val());
});
// Colour Picker // Colour Picker
$('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker);
@ -113,8 +118,7 @@ define('admin/manage/category', [
uploader.open(RELATIVE_PATH + '/api/admin/category/uploadpicture', { cid: cid }, 0, function(imageUrlOnServer) { uploader.open(RELATIVE_PATH + '/api/admin/category/uploadpicture', { cid: cid }, 0, function(imageUrlOnServer) {
inputEl.val(imageUrlOnServer); inputEl.val(imageUrlOnServer);
var previewBox = inputEl.parent().parent().siblings('.category-preview'); var previewBox = inputEl.parent().parent().siblings('.category-preview');
previewBox.css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')') previewBox.css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')');
.css('background-size', 'cover');
modified(inputEl[0]); modified(inputEl[0]);
}); });
}); });

@ -92,7 +92,7 @@ app.cacheBuster = null;
switch(url_parts[0]) { switch(url_parts[0]) {
case 'user': case 'user':
room = 'user/' + ajaxify.data ? ajaxify.data.theirid : 0; room = 'user/' + (ajaxify.data ? ajaxify.data.theirid : 0);
break; break;
case 'topic': case 'topic':
room = 'topic_' + url_parts[1]; room = 'topic_' + url_parts[1];
@ -503,7 +503,7 @@ app.cacheBuster = null;
app.load = function() { app.load = function() {
$('document').ready(function () { $('document').ready(function () {
var url = ajaxify.start(window.location.pathname.slice(1), true, window.location.search); var url = ajaxify.start(window.location.pathname.slice(1) + window.location.search, true);
ajaxify.end(url, app.template); ajaxify.end(url, app.template);
handleStatusChange(); handleStatusChange();

@ -103,10 +103,10 @@ var async = require('async'),
}); });
}; };
Categories.getCategoriesByPrivilege = function(uid, privilege, callback) { Categories.getCategoriesByPrivilege = function(set, uid, privilege, callback) {
async.waterfall([ async.waterfall([
function(next) { function(next) {
db.getSortedSetRange('categories:cid', 0, -1, next); db.getSortedSetRange(set, 0, -1, next);
}, },
function(cids, next) { function(cids, next) {
privileges.categories.filterCids(privilege, cids, uid, next); privileges.categories.filterCids(privilege, cids, uid, next);
@ -273,6 +273,7 @@ var async = require('async'),
if (!category) { if (!category) {
return; return;
} }
var postCount = parseInt(category.post_count, 10) || 0; var postCount = parseInt(category.post_count, 10) || 0;
var topicCount = parseInt(category.topic_count, 10) || 0; var topicCount = parseInt(category.topic_count, 10) || 0;
if (!Array.isArray(category.children) || !category.children.length) { if (!Array.isArray(category.children) || !category.children.length) {
@ -282,9 +283,11 @@ var async = require('async'),
} }
category.children.forEach(function(child) { category.children.forEach(function(child) {
postCount += parseInt(child.post_count, 10) || 0; calculateTopicPostCount(child);
topicCount += parseInt(child.topic_count, 10) || 0; postCount += parseInt(child.totalPostCount, 10) || 0;
topicCount += parseInt(child.totalTopicCount, 10) || 0;
}); });
category.totalPostCount = postCount; category.totalPostCount = postCount;
category.totalTopicCount = topicCount; category.totalTopicCount = topicCount;
} }
@ -308,22 +311,57 @@ var async = require('async'),
}; };
Categories.getChildren = function(cids, uid, callback) { Categories.getChildren = function(cids, uid, callback) {
var categories = cids.map(function(cid) {
return {cid: cid};
});
async.each(categories, function(category, next) {
getChildrenRecursive(category, uid, next);
}, function (err) {
callback(err, categories.map(function(c) {
return c && c.children;
}));
});
};
function getChildrenRecursive(category, uid, callback) {
async.waterfall([ async.waterfall([
async.apply(db.getSortedSetRange, 'categories:cid', 0, -1), function (next) {
function(cids, next) { db.getSortedSetRange('cid:' + category.cid + ':children', 0, -1, next);
privileges.categories.filterCids('find', cids, uid, next);
}, },
function (cids, next) { function (children, next) {
Categories.getCategoriesData(cids, next); privileges.categories.filterCids('find', children, uid, next);
}, },
function (categories, next) { function (children, next) {
async.map(cids, function(cid, next) { if (!children.length) {
next(null, categories.filter(function(category) { category.children = [];
return category && parseInt(category.parentCid, 10) === parseInt(cid, 10); return callback();
})); }
Categories.getCategoriesData(children, next);
},
function (childrenData, next) {
category.children = childrenData;
async.each(category.children, function(child, next) {
getChildrenRecursive(child, uid, next);
}, next); }, next);
} }
], callback); ], callback);
}
Categories.flattenCategories = function(allCategories, categoryData) {
categoryData.forEach(function(category) {
if (!category) {
return;
}
if (!category.parent) {
allCategories.push(category);
}
if (Array.isArray(category.children) && category.children.length) {
Categories.flattenCategories(allCategories, category.children);
}
});
}; };
/** /**

@ -10,6 +10,8 @@ module.exports = function(Categories) {
Categories.create = function(data, callback) { Categories.create = function(data, callback) {
var category; var category;
var parentCid = data.parentCid ? data.parentCid : 0;
async.waterfall([ async.waterfall([
function(next) { function(next) {
db.incrObjectField('global', 'nextCid', next); db.incrObjectField('global', 'nextCid', next);
@ -27,7 +29,7 @@ module.exports = function(Categories) {
bgColor: data.bgColor || colours[0], bgColor: data.bgColor || colours[0],
color: data.color || colours[1], color: data.color || colours[1],
slug: slug, slug: slug,
parentCid: ( data.parentCid ? data.parentCid : 0 ), parentCid: parentCid,
topic_count: 0, topic_count: 0,
post_count: 0, post_count: 0,
disabled: 0, disabled: 0,
@ -48,6 +50,7 @@ module.exports = function(Categories) {
async.series([ async.series([
async.apply(db.setObject, 'category:' + category.cid, category), async.apply(db.setObject, 'category:' + category.cid, category),
async.apply(db.sortedSetAdd, 'categories:cid', category.order, category.cid), async.apply(db.sortedSetAdd, 'categories:cid', category.order, category.cid),
async.apply(db.sortedSetAdd, 'cid:' + parentCid + ':children', category.order, category.cid),
async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'administrators'), async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'administrators'),
async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'registered-users'), async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'registered-users'),
async.apply(privileges.categories.give, ['find', 'read'], category.cid, 'guests') async.apply(privileges.categories.give, ['find', 'read'], category.cid, 'guests')

@ -30,15 +30,32 @@ module.exports = function(Categories) {
function(next) { function(next) {
db.sortedSetRemove('categories:cid', cid, next); db.sortedSetRemove('categories:cid', cid, next);
}, },
function(next) {
removeFromParent(cid, next);
},
function(next) { function(next) {
db.deleteAll([ db.deleteAll([
'cid:' + cid + ':tids', 'cid:' + cid + ':tids',
'cid:' + cid + ':tids:posts', 'cid:' + cid + ':tids:posts',
'cid:' + cid + ':pids', 'cid:' + cid + ':pids',
'cid:' + cid + ':read_by_uid', 'cid:' + cid + ':read_by_uid',
'cid:' + cid + ':children',
'category:' + cid 'category:' + cid
], next); ], next);
} }
], callback); ], callback);
} }
function removeFromParent(cid, callback) {
async.waterfall([
function(next) {
Categories.getCategoryField(cid, 'parentCid', next);
},
function(parentCid, next) {
parentCid = parseInt(parentCid, 10) || 0;
db.sortedSetRemove('cid:' + parentCid + ':children', cid, next);
}
], callback);
}
}; };

@ -56,9 +56,10 @@ module.exports = function(Categories) {
} }
}, },
function(posts, next) { function(posts, next) {
categoryData.forEach(function(category) { assignPostsToCategories(categoryData, posts);
assignPostsToCategory(category, posts);
}); bubbleUpChildrenPosts(categoryData);
next(); next();
} }
], callback); ], callback);
@ -87,13 +88,33 @@ module.exports = function(Categories) {
], callback); ], callback);
} }
function assignPostsToCategory(category, posts) { function bubbleUpChildrenPosts(categoryData) {
categoryData.forEach(function(category) {
if (category.posts.length) {
return;
}
var latestPost;
category.children.forEach(function(children) {
if (children.posts.length && (!latestPost || (latestPost && latestPost.timestamp < children.posts[0].timestamp))) {
latestPost = children.posts[0];
}
});
if (latestPost) {
category.posts = [latestPost];
}
});
}
function assignPostsToCategories(categories, posts) {
categories.forEach(function(category) {
category.posts = posts.filter(function(post) { category.posts = posts.filter(function(post) {
return post.category && (parseInt(post.category.cid, 10) === parseInt(category.cid, 10) || return post.category && (parseInt(post.category.cid, 10) === parseInt(category.cid, 10) ||
parseInt(post.category.parentCid, 10) === parseInt(category.cid, 10)); parseInt(post.category.parentCid, 10) === parseInt(category.cid, 10));
}).sort(function(a, b) { }).sort(function(a, b) {
return b.timestamp - a.timestamp; return b.timestamp - a.timestamp;
}).slice(0, parseInt(category.numRecentReplies, 10)); }).slice(0, parseInt(category.numRecentReplies, 10));
});
} }
function getRecentTopicPids(category, callback) { function getRecentTopicPids(category, callback) {

@ -50,17 +50,63 @@ module.exports = function(Categories) {
}; };
function updateCategoryField(cid, key, value, callback) { function updateCategoryField(cid, key, value, callback) {
if (key === 'parentCid') {
return updateParent(cid, value, callback);
}
db.setObjectField('category:' + cid, key, value, function(err) { db.setObjectField('category:' + cid, key, value, function(err) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
if (key === 'order') { if (key === 'order') {
db.sortedSetAdd('categories:cid', value, cid, callback); updateOrder(cid, value, callback);
} else { } else {
callback(); callback();
} }
}); });
} }
function updateParent(cid, newParent, callback) {
Categories.getCategoryField(cid, 'parentCid', function(err, oldParent) {
if (err) {
return callback(err);
}
async.series([
function (next) {
oldParent = parseInt(oldParent, 10) || 0;
db.sortedSetRemove('cid:' + oldParent + ':children', cid, next);
},
function (next) {
newParent = parseInt(newParent, 10) || 0;
db.sortedSetAdd('cid:' + newParent + ':children', cid, cid, next);
},
function (next) {
db.setObjectField('category:' + cid, 'parentCid', newParent, next);
}
], function(err, results) {
callback(err);
});
});
}
function updateOrder(cid, order, callback) {
Categories.getCategoryField(cid, 'parentCid', function(err, parentCid) {
if (err) {
return callback(err);
}
async.parallel([
function (next) {
db.sortedSetAdd('categories:cid', order, cid, next);
},
function (next) {
parentCid = parseInt(parentCid, 10) || 0;
db.sortedSetAdd('cid:' + parentCid + ':children', order, cid, next);
}
], callback);
});
}
}; };

@ -46,22 +46,13 @@ categoriesController.list = function(req, res, next) {
var categoryData; var categoryData;
async.waterfall([ async.waterfall([
function(next) { function(next) {
categories.getCategoriesByPrivilege(req.uid, 'find', next); categories.getCategoriesByPrivilege('cid:0:children', req.uid, 'find', next);
}, },
function(_categoryData, next) { function(_categoryData, next) {
categoryData = _categoryData; categoryData = _categoryData;
var allCategories = [];
categoryData = categoryData.filter(function(category) {
if (!category.parent) {
allCategories.push(category);
}
if (Array.isArray(category.children) && category.children.length) { var allCategories = [];
allCategories.push.apply(allCategories, category.children); categories.flattenCategories(allCategories, categoryData);
}
return category && !category.parent;
});
categories.getRecentTopicReplies(allCategories, req.uid, next); categories.getRecentTopicReplies(allCategories, req.uid, next);
} }
@ -200,7 +191,9 @@ categoriesController.get = function(req, res, next) {
}); });
}, },
function(categoryData, next) { function(categoryData, next) {
categories.getRecentTopicReplies(categoryData.children, req.uid, function(err) { var allCategories = [];
categories.flattenCategories(allCategories, [categoryData]);
categories.getRecentTopicReplies(allCategories, req.uid, function(err) {
next(err, categoryData); next(err, categoryData);
}); });
}, },

@ -42,9 +42,9 @@ Controllers.home = function(req, res, next) {
if (route === 'categories') { if (route === 'categories') {
Controllers.categories.list(req, res, next); Controllers.categories.list(req, res, next);
} else if (route === 'recent') { } else if (route === 'recent') {
Controllers.categories.recent(req, res, next); Controllers.recent.get(req, res, next);
} else if (route === 'popular') { } else if (route === 'popular') {
Controllers.categories.popular(req, res, next); Controllers.popular.get(req, res, next);
} else { } else {
next(); next();
} }

@ -17,7 +17,7 @@ searchController.search = function(req, res, next) {
var breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); var breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]);
categories.getCategoriesByPrivilege(req.uid, 'read', function(err, categories) { categories.getCategoriesByPrivilege('categories:cid', req.uid, 'read', function(err, categories) {
if (err) { if (err) {
return next(err); return next(err);
} }

@ -157,6 +157,7 @@ usersController.getMap = function(req, res, next) {
} }
data.room = validator.escape(categoryData.name); data.room = validator.escape(categoryData.name);
data.link = '/category/' + categoryData.slug; data.link = '/category/' + categoryData.slug;
data.core = false;
next(null, data); next(null, data);
}); });
} else if (roomName.startsWith('topic_')) { } else if (roomName.startsWith('topic_')) {
@ -167,13 +168,18 @@ usersController.getMap = function(req, res, next) {
} }
data.room = validator.escape(topicData.title); data.room = validator.escape(topicData.title);
data.link = '/topic/' + topicData.slug; data.link = '/topic/' + topicData.slug;
data.core = false;
next(null, data); next(null, data);
}); });
} else { } else {
data.core = true;
next(null, data); next(null, data);
} }
}); });
}, function(err, data) { }, function(err, data) {
if (err) {
return next(err);
}
data.sort(function(a, b) { data.sort(function(a, b) {
return b.total - a.total; return b.total - a.total;
}); });

@ -71,6 +71,7 @@ var fs = require('fs'),
Plugins.fireHook('action:email.send', { Plugins.fireHook('action:email.send', {
to: email, to: email,
from: meta.config['email:from'] || 'no-reply@localhost.lan', from: meta.config['email:from'] || 'no-reply@localhost.lan',
from_name: meta.config['email:from_name'] || 'NodeBB',
subject: translated[2], subject: translated[2],
html: translated[0], html: translated[0],
plaintext: translated[1], plaintext: translated[1],

@ -18,12 +18,16 @@ image.resizeImage = function(path, extension, width, height, callback) {
}); });
} else { } else {
lwip.open(path, function(err, image) { lwip.open(path, function(err, image) {
if (err) {
return callback(err);
}
image.batch() image.batch()
.cover(width, height) .cover(width, height)
.crop(width, height) .crop(width, height)
.writeFile(path, function(err) { .writeFile(path, function(err) {
callback(err) callback(err);
}) });
}); });
} }
}; };
@ -41,7 +45,7 @@ image.normalise = function(path, extension, callback) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
image.writeFile(path, 'png', callback) image.writeFile(path, 'png', callback);
}); });
} }
}; };

@ -29,7 +29,7 @@ module.exports = function(Meta) {
var pkgData = JSON.parse(pkgData), var pkgData = JSON.parse(pkgData),
ok = semver.satisfies(pkgData.version, pkg.dependencies[module]); ok = semver.satisfies(pkgData.version, pkg.dependencies[module]);
if (ok) { if (ok || pkgData._resolved.indexOf('//github.com') != -1) {
next(true); next(true);
} else { } else {
process.stdout.write('[' + 'outdated'.yellow + '] ' + module.bold + ' v' + pkgData.version + ', requires ' + pkg.dependencies[module] + '\n') process.stdout.write('[' + 'outdated'.yellow + '] ' + module.bold + ' v' + pkgData.version + ', requires ' + pkg.dependencies[module] + '\n')

@ -111,7 +111,24 @@ module.exports = function(Meta) {
Meta.js.minify = function(minify, callback) { Meta.js.minify = function(minify, callback) {
if (nconf.get('isPrimary') === 'true') { if (nconf.get('isPrimary') === 'true') {
var minifier = Meta.js.minifierProc = fork('minifier.js'), /**
* Check if the parent process is running with the debug option --debug (or --debug-brk)
*/
var forkProcessParams = {};
if(global.v8debug) {
/**
* use the line below if you want to debug minifier.js script too (or even --debug-brk option, but
* you'll have to setup your debugger and connect to the forked process)
*/
//forkProcessParams = {execArgv: ['--debug=' + (global.process.debugPort + 1), '--nolazy']};
/**
* otherwise, just clean up --debug/--debug-brk options which are set up by default from the parent one
*/
forkProcessParams = {execArgv: []};
}
var minifier = Meta.js.minifierProc = fork('minifier.js', [], forkProcessParams),
onComplete = function(err) { onComplete = function(err) {
if (err) { if (err) {
winston.error('[meta/js] Minification failed: ' + err.message); winston.error('[meta/js] Minification failed: ' + err.message);

@ -186,6 +186,9 @@ middleware.buildHeader = function(req, res, next) {
}, },
footer: function(next) { footer: function(next) {
app.render('footer', {loggedIn: (req.user ? parseInt(req.user.uid, 10) !== 0 : false)}, next); app.render('footer', {loggedIn: (req.user ? parseInt(req.user.uid, 10) !== 0 : false)}, next);
},
plugins: function(next) {
plugins.fireHook('filter:middleware.buildHeader', {req: req, locals: res.locals}, next);
} }
}, function(err, results) { }, function(err, results) {
if (err) { if (err) {
@ -297,12 +300,18 @@ middleware.renderHeader = function(req, res, callback) {
templateValues.customCSS = results.customCSS; templateValues.customCSS = results.customCSS;
templateValues.customJS = results.customJS; templateValues.customJS = results.customJS;
templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin; templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin;
templateValues.defaultLang = res.locals.config.defaultLang; templateValues.defaultLang = meta.config.defaultLang || 'en_GB';
templateValues.template = {name: res.locals.template}; templateValues.template = {name: res.locals.template};
templateValues.template[res.locals.template] = true; templateValues.template[res.locals.template] = true;
app.render('header', templateValues, callback); plugins.fireHook('filter:middleware.renderHeader', {templateValues: templateValues, req: req, res: res}, function(err, data) {
if (err) {
return callback(err);
}
app.render('header', data.templateValues, callback);
});
}); });
}; };

@ -417,8 +417,11 @@ function getChildrenCids(cids, uid, callback) {
} }
var childrenCids = []; var childrenCids = [];
var allCategories = [];
childrenCategories.forEach(function(childrens) { childrenCategories.forEach(function(childrens) {
childrenCids = childrenCids.concat(childrens.map(function(category) { categories.flattenCategories(allCategories, childrens);
childrenCids = childrenCids.concat(allCategories.map(function(category) {
return category && category.cid; return category && category.cid;
})); }));
}); });

@ -61,7 +61,7 @@ sitemap.getDynamicUrls = function(callback) {
async.parallel({ async.parallel({
categoryUrls: function(next) { categoryUrls: function(next) {
var categoryUrls = []; var categoryUrls = [];
categories.getCategoriesByPrivilege(0, 'find', function(err, categoriesData) { categories.getCategoriesByPrivilege('categories:cid', 0, 'find', function(err, categoriesData) {
if (err) { if (err) {
return next(err); return next(err);
} }

@ -20,7 +20,7 @@ Categories.create = function(socket, data, callback) {
Categories.getAll = function(socket, data, callback) { Categories.getAll = function(socket, data, callback) {
async.waterfall([ async.waterfall([
async.apply(db.getSortedSetRangeByScore, 'categories:cid', 0, -1, 0, Date.now()), async.apply(db.getSortedSetRange, 'categories:cid', 0, -1),
async.apply(categories.getCategoriesData), async.apply(categories.getCategoriesData),
function(categories, next) { function(categories, next) {
//Hook changes, there is no req, and res //Hook changes, there is no req, and res

@ -15,7 +15,7 @@ SocketCategories.getRecentReplies = function(socket, cid, callback) {
}; };
SocketCategories.get = function(socket, data, callback) { SocketCategories.get = function(socket, data, callback) {
categories.getCategoriesByPrivilege(socket.uid, 'find', callback); categories.getCategoriesByPrivilege('categories:cid', socket.uid, 'find', callback);
}; };
SocketCategories.getWatchedCategories = function(socket, data, callback) { SocketCategories.getWatchedCategories = function(socket, data, callback) {
@ -117,7 +117,7 @@ SocketCategories.getUsersInCategory = function(socket, cid, callback) {
}; };
SocketCategories.getCategoriesByPrivilege = function(socket, privilege, callback) { SocketCategories.getCategoriesByPrivilege = function(socket, privilege, callback) {
categories.getCategoriesByPrivilege(socket.uid, privilege, callback); categories.getCategoriesByPrivilege('categories:cid', socket.uid, privilege, callback);
}; };
SocketCategories.watch = function(socket, cid, callback) { SocketCategories.watch = function(socket, cid, callback) {

@ -59,6 +59,10 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
return callback(new Error('[[error:invalid-data]]')); return callback(new Error('[[error:invalid-data]]'));
} }
if (data.enter) {
data.enter = data.enter.toString();
}
if (data.enter && data.enter.startsWith('uid_') && data.enter !== 'uid_' + socket.uid) { if (data.enter && data.enter.startsWith('uid_') && data.enter !== 'uid_' + socket.uid) {
return callback(new Error('[[error:not-allowed]]')); return callback(new Error('[[error:not-allowed]]'));
} }

@ -21,7 +21,7 @@ var db = require('./database'),
schemaDate, thisSchemaDate, schemaDate, thisSchemaDate,
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
latestSchema = Date.UTC(2015, 6, 3); latestSchema = Date.UTC(2015, 7, 18);
Upgrade.check = function(callback) { Upgrade.check = function(callback) {
db.get('schemaDate', function(err, value) { db.get('schemaDate', function(err, value) {
@ -446,6 +446,46 @@ Upgrade.upgrade = function(callback) {
winston.info('[2015/07/03] Enabling default composer plugin skipped'); winston.info('[2015/07/03] Enabling default composer plugin skipped');
next(); next();
} }
},
function(next) {
thisSchemaDate = Date.UTC(2015, 7, 18);
if (schemaDate < thisSchemaDate) {
updatesMade = true;
winston.info('[2015/08/18] Creating children category sorted sets');
db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) {
if (err) {
return next(err);
}
async.each(cids, function(cid, next) {
db.getObjectFields('category:' + cid, ['parentCid', 'order'], function(err, category) {
if (err) {
return next(err);
}
if (!category) {
return next();
}
if (parseInt(category.parentCid, 10)) {
db.sortedSetAdd('cid:' + category.parentCid + ':children', parseInt(category.order, 10), cid, next);
} else {
db.sortedSetAdd('cid:0:children', parseInt(category.order, 10), cid, next);
}
});
}, function(err) {
if (err) {
return next(err);
}
winston.info('[2015/08/18] Creating children category sorted sets done');
Upgrade.update(thisSchemaDate, next);
});
});
} else {
winston.info('[2015/08/18] Creating children category sorted sets skipped');
next();
}
} }

@ -41,7 +41,7 @@
</div> </div>
<div class="col-sm-4 col-xs-12"> <div class="col-sm-4 col-xs-12">
<div class="form-group"> <div class="form-group">
<label for="cid-{category.cid}-imageClass">Image Class</label> <label for="cid-{category.cid}-imageClass">Background Image Size</label>
<select id="cid-{category.cid}-imageClass" class="form-control" data-name="imageClass" data-value="{category.imageClass}"> <select id="cid-{category.cid}-imageClass" class="form-control" data-name="imageClass" data-value="{category.imageClass}">
<option value="auto">auto</option> <option value="auto">auto</option>
<option value="cover">cover</option> <option value="cover">cover</option>
@ -97,8 +97,8 @@
<div class="category-preview" style=" <div class="category-preview" style="
<!-- IF category.backgroundImage -->background-image: url({category.backgroundImage});<!-- ENDIF category.backgroundImage --> <!-- IF category.backgroundImage -->background-image: url({category.backgroundImage});<!-- ENDIF category.backgroundImage -->
<!-- IF category.bgColor -->background-color: {category.bgColor};<!-- ENDIF category.bgColor --> <!-- IF category.bgColor -->background-color: {category.bgColor};<!-- ENDIF category.bgColor -->
<!-- IF category.imageClass -->background-size: {category.imageClass};<!-- ENDIF category.imageClass -->
color: {category.color}; color: {category.color};
background-size:cover;
"> ">
<div class="icon"> <div class="icon">
<i data-name="icon" value="{category.icon}" class="fa {category.icon} fa-2x"></i> <i data-name="icon" value="{category.icon}" class="fa {category.icon} fa-2x"></i>

@ -14,6 +14,13 @@
</p> </p>
<input type="text" class="form-control input-lg" id="email:from" data-field="email:from" placeholder="info@example.org" /><br /> <input type="text" class="form-control input-lg" id="email:from" data-field="email:from" placeholder="info@example.org" /><br />
</div> </div>
<div class="form-group">
<label for="email:from_name"><strong>From Name</strong></label>
<p class="help-block">
The from name to display in the email.
</p>
<input type="text" class="form-control input-lg" id="email:from_name" data-field="email:from_name" placeholder="NodeBB" /><br />
</div>
<button class="btn btn-block btn-default" type="button" data-action="email.test">Send Test Email</button> <button class="btn btn-block btn-default" type="button" data-action="email.test">Send Test Email</button>
<p class="help-block"> <p class="help-block">
The test email will be sent to the currently logged in user's email address. The test email will be sent to the currently logged in user's email address.

Loading…
Cancel
Save