mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-03-03 19:11:22 +01:00
Merge remote-tracking branch 'origin/master' into flagging-refactor
This commit is contained in:
35
app.js
35
app.js
@@ -37,7 +37,7 @@ winston.add(winston.transports.Console, {
|
||||
colorize: true,
|
||||
timestamp: function () {
|
||||
var date = new Date();
|
||||
return (!!nconf.get('json-logging')) ? date.toJSON() : date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']';
|
||||
return (!!nconf.get('json-logging')) ? date.toJSON() : date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,8) + ' [' + global.process.pid + ']';
|
||||
},
|
||||
level: nconf.get('log-level') || (global.env === 'production' ? 'info' : 'verbose'),
|
||||
json: (!!nconf.get('json-logging')),
|
||||
@@ -180,21 +180,26 @@ function start() {
|
||||
});
|
||||
|
||||
async.waterfall([
|
||||
async.apply(db.init),
|
||||
async.apply(db.checkCompatibility),
|
||||
async.apply(db.init),
|
||||
function (next) {
|
||||
require('./src/meta').configs.init(next);
|
||||
},
|
||||
function (next) {
|
||||
if (nconf.get('dep-check') === undefined || nconf.get('dep-check') !== false) {
|
||||
require('./src/meta').dependencies.check(next);
|
||||
} else {
|
||||
winston.warn('[init] Dependency checking skipped!');
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
require('./src/upgrade').check(next);
|
||||
var meta = require('./src/meta');
|
||||
async.parallel([
|
||||
async.apply(db.checkCompatibility),
|
||||
async.apply(meta.configs.init),
|
||||
function (next) {
|
||||
if (nconf.get('dep-check') === undefined || nconf.get('dep-check') !== false) {
|
||||
meta.dependencies.check(next);
|
||||
} else {
|
||||
winston.warn('[init] Dependency checking skipped!');
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
require('./src/upgrade').check(next);
|
||||
}
|
||||
], function (err) {
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
var webserver = require('./src/webserver');
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"morgan": "^1.3.2",
|
||||
"mousetrap": "^1.5.3",
|
||||
"nconf": "~0.8.2",
|
||||
"nodebb-plugin-composer-default": "4.3.2",
|
||||
"nodebb-plugin-composer-default": "4.3.3",
|
||||
"nodebb-plugin-dbsearch": "1.0.4",
|
||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||
"nodebb-plugin-emoji-one": "1.1.5",
|
||||
|
||||
@@ -8,44 +8,9 @@ define('admin/manage/category', [
|
||||
'autocomplete'
|
||||
], function (uploader, iconSelect, colorpicker, autocomplete) {
|
||||
var Category = {};
|
||||
var modified_categories = {};
|
||||
|
||||
Category.init = function () {
|
||||
var modified_categories = {};
|
||||
|
||||
function modified(el) {
|
||||
var cid = $(el).parents('form').attr('data-cid');
|
||||
|
||||
if (cid) {
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid][$(el).attr('data-name')] = $(el).val();
|
||||
|
||||
app.flags = app.flags || {};
|
||||
app.flags._unsaved = true;
|
||||
}
|
||||
}
|
||||
|
||||
function save(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if(Object.keys(modified_categories).length) {
|
||||
socket.emit('admin.categories.update', modified_categories, function (err, result) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (result && result.length) {
|
||||
app.flags._unsaved = false;
|
||||
app.alert({
|
||||
title: 'Updated Categories',
|
||||
message: 'Category IDs ' + result.join(', ') + ' was successfully updated.',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
modified_categories = {};
|
||||
}
|
||||
}
|
||||
|
||||
$('.blockclass, form.category select').each(function () {
|
||||
var $this = $(this);
|
||||
@@ -85,7 +50,28 @@ define('admin/manage/category', [
|
||||
|
||||
$('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker);
|
||||
|
||||
$('#save').on('click', save);
|
||||
$('#save').on('click', function () {
|
||||
if (Object.keys(modified_categories).length) {
|
||||
socket.emit('admin.categories.update', modified_categories, function (err, result) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (result && result.length) {
|
||||
app.flags._unsaved = false;
|
||||
app.alert({
|
||||
title: 'Updated Categories',
|
||||
message: 'Category IDs ' + result.join(', ') + ' was successfully updated.',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
modified_categories = {};
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.purge').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -171,8 +157,37 @@ define('admin/manage/category', [
|
||||
});
|
||||
|
||||
Category.setupPrivilegeTable();
|
||||
|
||||
handleTags();
|
||||
};
|
||||
|
||||
function modified(el) {
|
||||
var cid = ajaxify.data.category.cid;
|
||||
|
||||
if (cid) {
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid][$(el).attr('data-name')] = $(el).val();
|
||||
|
||||
app.flags = app.flags || {};
|
||||
app.flags._unsaved = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTags() {
|
||||
var tagEl = $('#tag-whitelist');
|
||||
tagEl.tagsinput({
|
||||
confirmKeys: [13, 44],
|
||||
trimValue: true
|
||||
});
|
||||
|
||||
ajaxify.data.category.tagWhitelist.forEach(function (tag) {
|
||||
tagEl.tagsinput('add', tag);
|
||||
});
|
||||
tagEl.on('itemAdded itemRemoved', function (event) {
|
||||
modified(tagEl);
|
||||
});
|
||||
}
|
||||
|
||||
Category.setupPrivilegeTable = function () {
|
||||
$('.privilege-table-container').on('change', 'input[type="checkbox"]', function () {
|
||||
var checkboxEl = $(this),
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
},
|
||||
|
||||
cleanUpTag: function (tag, maxLength) {
|
||||
if (typeof tag !== 'string' || !tag.length ) {
|
||||
if (typeof tag !== 'string' || !tag.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +141,9 @@ var privileges = require('./privileges');
|
||||
parents: function (next) {
|
||||
Categories.getParents(cids, next);
|
||||
},
|
||||
tagWhitelist: function (next) {
|
||||
Categories.getTagWhitelist(cids, next);
|
||||
},
|
||||
hasRead: function (next) {
|
||||
Categories.hasReadCategories(cids, uid, next);
|
||||
}
|
||||
@@ -149,22 +152,28 @@ var privileges = require('./privileges');
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var categories = results.categories;
|
||||
var hasRead = results.hasRead;
|
||||
uid = parseInt(uid, 10);
|
||||
for(var i = 0; i < results.categories.length; ++i) {
|
||||
if (categories[i]) {
|
||||
categories[i]['unread-class'] = (parseInt(categories[i].topic_count, 10) === 0 || (hasRead[i] && uid !== 0)) ? '' : 'unread';
|
||||
categories[i].children = results.children[i];
|
||||
categories[i].parent = results.parents[i] || undefined;
|
||||
calculateTopicPostCount(categories[i]);
|
||||
results.categories.forEach(function (category, i) {
|
||||
if (category) {
|
||||
category['unread-class'] = (parseInt(category.topic_count, 10) === 0 || (results.hasRead[i] && uid !== 0)) ? '' : 'unread';
|
||||
category.children = results.children[i];
|
||||
category.parent = results.parents[i] || undefined;
|
||||
category.tagWhitelist = results.tagWhitelist[i];
|
||||
calculateTopicPostCount(category);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, categories);
|
||||
callback(null, results.categories);
|
||||
});
|
||||
};
|
||||
|
||||
Categories.getTagWhitelist = function (cids, callback) {
|
||||
var keys = cids.map(function (cid) {
|
||||
return 'cid:' + cid + ':tag:whitelist';
|
||||
});
|
||||
db.getSortedSetsMembers(keys, callback);
|
||||
};
|
||||
|
||||
function calculateTopicPostCount(category) {
|
||||
if (!category) {
|
||||
return;
|
||||
|
||||
@@ -54,6 +54,7 @@ module.exports = function (Categories) {
|
||||
'cid:' + cid + ':read_by_uid',
|
||||
'cid:' + cid + ':ignorers',
|
||||
'cid:' + cid + ':children',
|
||||
'cid:' + cid + ':tag:whitelist',
|
||||
'category:' + cid
|
||||
], next);
|
||||
},
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var db = require('../database');
|
||||
var meta = require('../meta');
|
||||
var utils = require('../../public/src/utils');
|
||||
var translator = require('../../public/src/modules/translator');
|
||||
var plugins = require('../plugins');
|
||||
@@ -66,6 +68,8 @@ module.exports = function (Categories) {
|
||||
function updateCategoryField(cid, key, value, callback) {
|
||||
if (key === 'parentCid') {
|
||||
return updateParent(cid, value, callback);
|
||||
} else if (key === 'tagWhitelist') {
|
||||
return updateTagWhitelist(cid, value, callback);
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
@@ -112,6 +116,25 @@ module.exports = function (Categories) {
|
||||
});
|
||||
}
|
||||
|
||||
function updateTagWhitelist(cid, tags, callback) {
|
||||
tags = tags.split(',');
|
||||
tags = tags.map(function (tag) {
|
||||
return utils.cleanUpTag(tag, meta.config.maximumTagLength);
|
||||
}).filter(Boolean);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.delete('cid:' + cid + ':tag:whitelist', next);
|
||||
},
|
||||
function (next) {
|
||||
var scores = tags.map(function (tag, index) {
|
||||
return index;
|
||||
});
|
||||
db.sortedSetAdd('cid:' + cid + ':tag:whitelist', scores, tags, next);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
function updateOrder(cid, order, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
|
||||
@@ -111,13 +111,11 @@
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
createSessionStore();
|
||||
createIndices();
|
||||
createSessionStore();
|
||||
});
|
||||
} else {
|
||||
winston.warn('You have no mongo password setup!');
|
||||
createSessionStore();
|
||||
createIndices();
|
||||
}
|
||||
|
||||
function createSessionStore() {
|
||||
@@ -137,37 +135,44 @@
|
||||
db: db
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createIndices() {
|
||||
winston.info('[database] Checking database indices.');
|
||||
async.series([
|
||||
async.apply(createIndex, 'objects', {_key: 1, score: -1}, {background: true}),
|
||||
async.apply(createIndex, 'objects', {_key: 1, value: -1}, {background: true, unique: true, sparse: true}),
|
||||
async.apply(createIndex, 'objects', {expireAt: 1}, {expireAfterSeconds: 0, background: true})
|
||||
], function (err) {
|
||||
if (err) {
|
||||
winston.error('Error creating index ' + err.message);
|
||||
}
|
||||
winston.info('[database] Checking database indices done!');
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
function createIndex(collection, index, options, callback) {
|
||||
db.collection(collection).createIndex(index, options, callback);
|
||||
}
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.checkCompatibility = function (callback) {
|
||||
var mongoPkg = require.main.require('./node_modules/mongodb/package.json'),
|
||||
err = semver.lt(mongoPkg.version, '2.0.0') ? new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.') : null;
|
||||
|
||||
if (err) {
|
||||
err.stacktrace = false;
|
||||
module.createIndices = function (callback) {
|
||||
function createIndex(collection, index, options, callback) {
|
||||
module.client.collection(collection).createIndex(index, options, callback);
|
||||
}
|
||||
callback(err);
|
||||
|
||||
if (!module.client) {
|
||||
winston.warn('[database/createIndices] database not initialized');
|
||||
return callback();
|
||||
}
|
||||
|
||||
winston.info('[database] Checking database indices.');
|
||||
async.series([
|
||||
async.apply(createIndex, 'objects', {_key: 1, score: -1}, {background: true}),
|
||||
async.apply(createIndex, 'objects', {_key: 1, value: -1}, {background: true, unique: true, sparse: true}),
|
||||
async.apply(createIndex, 'objects', {expireAt: 1}, {expireAfterSeconds: 0, background: true})
|
||||
], function (err) {
|
||||
if (err) {
|
||||
winston.error('Error creating index ' + err.message);
|
||||
return callback(err);
|
||||
}
|
||||
winston.info('[database] Checking database indices done!');
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
module.checkCompatibility = function (callback) {
|
||||
var mongoPkg = require.main.require('./node_modules/mongodb/package.json');
|
||||
|
||||
if (semver.lt(mongoPkg.version, '2.0.0')) {
|
||||
return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.'));
|
||||
}
|
||||
|
||||
callback();
|
||||
};
|
||||
|
||||
module.info = function (db, callback) {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
(function (module) {
|
||||
|
||||
var winston = require('winston'),
|
||||
nconf = require('nconf'),
|
||||
semver = require('semver'),
|
||||
session = require('express-session'),
|
||||
redis,
|
||||
connectRedis,
|
||||
redisClient;
|
||||
var winston = require('winston');
|
||||
var nconf = require('nconf');
|
||||
var semver = require('semver');
|
||||
var session = require('express-session');
|
||||
var redis;
|
||||
var connectRedis;
|
||||
var redisClient;
|
||||
|
||||
module.questions = [
|
||||
{
|
||||
@@ -107,6 +107,10 @@
|
||||
return cxn;
|
||||
};
|
||||
|
||||
module.createIndices = function (callback) {
|
||||
setImmediate(callback);
|
||||
};
|
||||
|
||||
module.checkCompatibility = function (callback) {
|
||||
module.info(module.client, function (err, info) {
|
||||
if (err) {
|
||||
@@ -114,11 +118,10 @@
|
||||
}
|
||||
|
||||
if (semver.lt(info.redis_version, '2.8.9')) {
|
||||
err = new Error('Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.');
|
||||
err.stacktrace = false;
|
||||
return callback(new Error('Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.'));
|
||||
}
|
||||
|
||||
callback(err);
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
prompt = require('prompt'),
|
||||
winston = require('winston'),
|
||||
nconf = require('nconf'),
|
||||
utils = require('../public/src/utils.js');
|
||||
var async = require('async');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var prompt = require('prompt');
|
||||
var winston = require('winston');
|
||||
var nconf = require('nconf');
|
||||
var utils = require('../public/src/utils.js');
|
||||
|
||||
|
||||
var install = {},
|
||||
questions = {};
|
||||
var install = {};
|
||||
var questions = {};
|
||||
|
||||
questions.main = [
|
||||
{
|
||||
@@ -124,40 +123,33 @@ function setupConfig(next) {
|
||||
prompt.delimiter = '';
|
||||
prompt.colors = false;
|
||||
|
||||
if (!install.values) {
|
||||
prompt.get(questions.main, function (err, config) {
|
||||
if (err) {
|
||||
process.stdout.write('\n\n');
|
||||
winston.warn('NodeBB setup ' + err.message);
|
||||
process.exit();
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (install.values) {
|
||||
// Use provided values, fall back to defaults
|
||||
var config = {};
|
||||
var redisQuestions = require('./database/redis').questions;
|
||||
var mongoQuestions = require('./database/mongo').questions;
|
||||
var allQuestions = questions.main.concat(questions.optional).concat(redisQuestions).concat(mongoQuestions);
|
||||
|
||||
allQuestions.forEach(function (question) {
|
||||
config[question.name] = install.values[question.name] || question['default'] || undefined;
|
||||
});
|
||||
setImmediate(next, null, config);
|
||||
} else {
|
||||
prompt.get(questions.main, next);
|
||||
}
|
||||
|
||||
configureDatabases(config, function (err, config) {
|
||||
completeConfigSetup(err, config, next);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Use provided values, fall back to defaults
|
||||
var config = {},
|
||||
redisQuestions = require('./database/redis').questions,
|
||||
mongoQuestions = require('./database/mongo').questions,
|
||||
allQuestions = questions.main.concat(questions.optional).concat(redisQuestions).concat(mongoQuestions);
|
||||
|
||||
allQuestions.forEach(function (question) {
|
||||
config[question.name] = install.values[question.name] || question['default'] || undefined;
|
||||
});
|
||||
|
||||
configureDatabases(config, function (err, config) {
|
||||
completeConfigSetup(err, config, next);
|
||||
});
|
||||
}
|
||||
},
|
||||
function (config, next) {
|
||||
configureDatabases(config, next);
|
||||
},
|
||||
function (config, next) {
|
||||
completeConfigSetup(config, next);
|
||||
}
|
||||
], next);
|
||||
}
|
||||
|
||||
function completeConfigSetup(err, config, next) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
function completeConfigSetup(config, next) {
|
||||
// Add CI object
|
||||
if (install.ciVals) {
|
||||
config.test_database = {};
|
||||
@@ -168,13 +160,17 @@ function completeConfigSetup(err, config, next) {
|
||||
}
|
||||
}
|
||||
|
||||
install.save(config, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
install.save(config, next);
|
||||
},
|
||||
function (next) {
|
||||
require('./database').init(next);
|
||||
},
|
||||
function (next) {
|
||||
require('./database').createIndices(next);
|
||||
}
|
||||
|
||||
require('./database').init(next);
|
||||
});
|
||||
], next);
|
||||
}
|
||||
|
||||
function setupDefaultConfigs(next) {
|
||||
@@ -491,7 +487,6 @@ function setCopyrightWidget(next) {
|
||||
|
||||
install.setup = function (callback) {
|
||||
|
||||
|
||||
async.series([
|
||||
checkSetupFlag,
|
||||
checkCIFlag,
|
||||
|
||||
@@ -78,6 +78,7 @@ module.exports = function (Meta) {
|
||||
acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";\n';
|
||||
acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";\n';
|
||||
acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui.css";';
|
||||
acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";';
|
||||
|
||||
minify(acpSource, paths, 'acpCache', callback);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var db = require('../../database');
|
||||
var topics = require('../../topics');
|
||||
var utils = require('../../../public/src/utils');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
|
||||
SocketTopics.isTagAllowed = function (socket, data, callback) {
|
||||
if (!data || !data.cid || !data.tag) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('cid:' + data.cid + ':tag:whitelist', 0, -1, next);
|
||||
},
|
||||
function (tagWhitelist, next) {
|
||||
if (!tagWhitelist.length) {
|
||||
return next(null, true);
|
||||
}
|
||||
next(null, tagWhitelist.indexOf(data.tag) !== -1);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketTopics.autocompleteTags = function (socket, data, callback) {
|
||||
topics.autocompleteTags(data, callback);
|
||||
};
|
||||
|
||||
@@ -31,6 +31,10 @@ module.exports = function (Topics) {
|
||||
return tag && tag.length >= (meta.config.minimumTagLength || 3) && array.indexOf(tag) === index;
|
||||
});
|
||||
|
||||
filterCategoryTags(tags, tid, next);
|
||||
},
|
||||
function (_tags, next) {
|
||||
tags = _tags;
|
||||
var keys = tags.map(function (tag) {
|
||||
return 'tag:' + tag + ':topics';
|
||||
});
|
||||
@@ -39,15 +43,35 @@ module.exports = function (Topics) {
|
||||
async.apply(db.setAdd, 'topic:' + tid + ':tags', tags),
|
||||
async.apply(db.sortedSetsAdd, keys, timestamp, tid)
|
||||
], function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.each(tags, updateTagCount, next);
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
async.each(tags, updateTagCount, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
function filterCategoryTags(tags, tid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.getTopicField(tid, 'cid', next);
|
||||
},
|
||||
function (cid, next) {
|
||||
db.getSortedSetRange('cid:' + cid + ':tag:whitelist', 0, -1, next);
|
||||
},
|
||||
function (tagWhitelist, next) {
|
||||
if (!tagWhitelist.length) {
|
||||
return next(null, tags);
|
||||
}
|
||||
tags = tags.filter(function (tag) {
|
||||
return tagWhitelist.indexOf(tag) !== -1;
|
||||
});
|
||||
next(null, tags);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
Topics.createEmptyTag = function (tag, callback) {
|
||||
if (!tag) {
|
||||
return callback(new Error('[[error:invalid-tag]]'));
|
||||
@@ -273,7 +297,7 @@ module.exports = function (Topics) {
|
||||
if (plugins.hasListeners('filter:topics.searchTags')) {
|
||||
plugins.fireHook('filter:topics.searchTags', {data: data}, next);
|
||||
} else {
|
||||
findMatches(data.query, next);
|
||||
findMatches(data.query, 0, next);
|
||||
}
|
||||
},
|
||||
function (result, next) {
|
||||
@@ -295,7 +319,7 @@ module.exports = function (Topics) {
|
||||
if (plugins.hasListeners('filter:topics.autocompleteTags')) {
|
||||
plugins.fireHook('filter:topics.autocompleteTags', {data: data}, next);
|
||||
} else {
|
||||
findMatches(data.query, next);
|
||||
findMatches(data.query, data.cid, next);
|
||||
}
|
||||
},
|
||||
function (result, next) {
|
||||
@@ -304,10 +328,21 @@ module.exports = function (Topics) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
function findMatches(query, callback) {
|
||||
function findMatches(query, cid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange('tags:topic:count', 0, -1, next);
|
||||
if (parseInt(cid, 10)) {
|
||||
db.getSortedSetRange('cid:' + cid + ':tag:whitelist', 0, -1, next);
|
||||
} else {
|
||||
setImmediate(next, null, []);
|
||||
}
|
||||
},
|
||||
function (tagWhitelist, next) {
|
||||
if (tagWhitelist.length) {
|
||||
setImmediate(next, null, tagWhitelist);
|
||||
} else {
|
||||
db.getSortedSetRevRange('tags:topic:count', 0, -1, next);
|
||||
}
|
||||
},
|
||||
function (tags, next) {
|
||||
query = query.toLowerCase();
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label for="tag-whitelist">Tag Whitelist</label><br />
|
||||
<input id="tag-whitelist" type="text" class="form-control" placeholder="Enter category tags here" data-name="tagWhitelist" value="" />
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -541,7 +541,80 @@ describe('Categories', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('tag whitelist', function () {
|
||||
var cid;
|
||||
var socketTopics = require('../src/socket.io/topics');
|
||||
before(function (done) {
|
||||
Categories.create({
|
||||
name: 'test'
|
||||
}, function (err, category) {
|
||||
assert.ifError(err);
|
||||
cid = category.cid;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if data is invalid', function (done) {
|
||||
socketTopics.isTagAllowed({uid: posterUid}, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true if category whitelist is empty', function (done) {
|
||||
socketTopics.isTagAllowed({uid: posterUid}, {tag: 'notallowed', cid: cid}, function (err, allowed) {
|
||||
assert.ifError(err);
|
||||
assert(allowed);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should add tags to category whitelist', function (done) {
|
||||
var data = {};
|
||||
data[cid] = {
|
||||
tagWhitelist: 'nodebb,jquery,javascript'
|
||||
};
|
||||
Categories.update(data, function (err) {
|
||||
assert.ifError(err);
|
||||
db.getSortedSetRange('cid:' + cid + ':tag:whitelist', 0, -1, function (err, tagWhitelist) {
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(['nodebb', 'jquery', 'javascript'], tagWhitelist);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if category whitelist does not have tag', function (done) {
|
||||
socketTopics.isTagAllowed({uid: posterUid}, {tag: 'notallowed', cid: cid}, function (err, allowed) {
|
||||
assert.ifError(err);
|
||||
assert(!allowed);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true if category whitelist has tag', function (done) {
|
||||
socketTopics.isTagAllowed({uid: posterUid}, {tag: 'nodebb', cid: cid}, function (err, allowed) {
|
||||
assert.ifError(err);
|
||||
assert(allowed);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should post a topic with only allowed tags', function (done) {
|
||||
Topics.post({
|
||||
uid: posterUid,
|
||||
cid: cid,
|
||||
title: 'Test Topic Title',
|
||||
content: 'The content of test topic',
|
||||
tags: ['nodebb', 'jquery', 'notallowed']
|
||||
}, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert.equal(data.topicData.tags.length, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user