mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-03 20:45:58 +01:00
Merge remote-tracking branch 'origin/master' into develop
This commit is contained in:
@@ -59,10 +59,10 @@
|
|||||||
"nodebb-plugin-markdown": "7.1.1",
|
"nodebb-plugin-markdown": "7.1.1",
|
||||||
"nodebb-plugin-mentions": "2.0.1",
|
"nodebb-plugin-mentions": "2.0.1",
|
||||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||||
"nodebb-plugin-spam-be-gone": "0.4.10",
|
"nodebb-plugin-spam-be-gone": "0.4.13",
|
||||||
"nodebb-rewards-essentials": "0.0.9",
|
"nodebb-rewards-essentials": "0.0.9",
|
||||||
"nodebb-theme-lavender": "3.0.15",
|
"nodebb-theme-lavender": "3.0.15",
|
||||||
"nodebb-theme-persona": "4.2.4",
|
"nodebb-theme-persona": "4.2.6",
|
||||||
"nodebb-theme-vanilla": "5.2.0",
|
"nodebb-theme-vanilla": "5.2.0",
|
||||||
"nodebb-widget-essentials": "2.0.13",
|
"nodebb-widget-essentials": "2.0.13",
|
||||||
"nodemailer": "2.6.4",
|
"nodemailer": "2.6.4",
|
||||||
|
|||||||
@@ -27,5 +27,6 @@
|
|||||||
"touch-icon.help": "Recommended size and format: 192x192, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.",
|
"touch-icon.help": "Recommended size and format: 192x192, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.",
|
||||||
"outgoing-links": "Outgoing Links",
|
"outgoing-links": "Outgoing Links",
|
||||||
"outgoing-links.warning-page": "Use Outgoing Links Warning Page",
|
"outgoing-links.warning-page": "Use Outgoing Links Warning Page",
|
||||||
"search-default-sort-by": "Search default sort by"
|
"search-default-sort-by": "Search default sort by",
|
||||||
|
"outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page"
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
"details.disableJoinRequests": "Disable join requests",
|
"details.disableJoinRequests": "Disable join requests",
|
||||||
"details.grant": "Grant/Rescind Ownership",
|
"details.grant": "Grant/Rescind Ownership",
|
||||||
"details.kick": "Kick",
|
"details.kick": "Kick",
|
||||||
|
"details.kick_confirm": "Are you sure you want to remove this member from the group?",
|
||||||
|
|
||||||
"details.owner_options": "Group Administration",
|
"details.owner_options": "Group Administration",
|
||||||
"details.group_name": "Group Name",
|
"details.group_name": "Group Name",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"general": "General",
|
"general": "Ogólne",
|
||||||
"private-groups": "Prywatne Grupy",
|
"private-groups": "Prywatne Grupy",
|
||||||
"private-groups.help": "If enabled, joining of groups requires the approval of the group owner <em>(Default: enabled)</em>",
|
"private-groups.help": "If enabled, joining of groups requires the approval of the group owner <em>(Default: enabled)</em>",
|
||||||
"private-groups.warning": "<strong>Beware!</strong> If this option is disabled and you have private groups, they automatically become public.",
|
"private-groups.warning": "<strong>Beware!</strong> If this option is disabled and you have private groups, they automatically become public.",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"tag": "Ustawienia Tagów",
|
"tag": "Ustawienia Tagów",
|
||||||
"min-per-topic": "Minimum Tags per Topic",
|
"min-per-topic": "Minimalna ilość Tagów na Temat",
|
||||||
"max-per-topic": "Maximum Tags per Topic",
|
"max-per-topic": "Maximum Tags per Topic",
|
||||||
"min-length": "Minimum Tag Length",
|
"min-length": "Minimum Tag Length",
|
||||||
"max-length": "Maximum Tag Length",
|
"max-length": "Maximum Tag Length",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"posts": "Posty",
|
"posts": "Posty",
|
||||||
"allow-files": "Allow users to upload regular files",
|
"allow-files": "Pozwolić użytkownikom wgrywać pliki",
|
||||||
"private": "Make uploaded files private",
|
"private": "Make uploaded files private",
|
||||||
"max-image-width": "Resize images down to specified width (in pixels)",
|
"max-image-width": "Resize images down to specified width (in pixels)",
|
||||||
"max-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
|
"max-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
|
||||||
|
|||||||
@@ -273,3 +273,7 @@ body {
|
|||||||
background: lighten(@brand-success, 10%);
|
background: lighten(@brand-success, 10%);
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form small {
|
||||||
|
color: @gray-light;
|
||||||
|
}
|
||||||
@@ -16,4 +16,20 @@
|
|||||||
[data-action="upload"][type="text"] {
|
[data-action="upload"][type="text"] {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bootstrap-tagsinput {
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 1px;
|
||||||
|
margin-top: 9px;
|
||||||
|
border-bottom: 1px dotted #ccc !important;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -102,6 +102,7 @@ define('admin/settings', ['uploader'], function (uploader) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
handleUploads();
|
handleUploads();
|
||||||
|
setupTagsInput();
|
||||||
|
|
||||||
$('#clear-sitemap-cache').off('click').on('click', function () {
|
$('#clear-sitemap-cache').off('click').on('click', function () {
|
||||||
socket.emit('admin.settings.clearSitemapCache', function () {
|
socket.emit('admin.settings.clearSitemapCache', function () {
|
||||||
@@ -142,6 +143,14 @@ define('admin/settings', ['uploader'], function (uploader) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupTagsInput() {
|
||||||
|
$('[data-field-type="tagsinput"]').tagsinput({
|
||||||
|
confirmKeys: [13, 44],
|
||||||
|
trimValue: true,
|
||||||
|
});
|
||||||
|
app.flags._unsaved = false;
|
||||||
|
}
|
||||||
|
|
||||||
Settings.remove = function (key) {
|
Settings.remove = function (key) {
|
||||||
socket.emit('admin.config.remove', key);
|
socket.emit('admin.config.remove', key);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -366,11 +366,16 @@ $(document).ready(function () {
|
|||||||
window.open(this.href, '_blank');
|
window.open(this.href, '_blank');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
} else if (config.useOutgoingLinksPage) {
|
} else if (config.useOutgoingLinksPage) {
|
||||||
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
|
var safeUrls = config.outgoingLinksWhitelist.trim().split(/[\s,]+/g);
|
||||||
|
var href = this.href;
|
||||||
|
|
||||||
|
if (!safeUrls.some(function (url) { return href.indexOf(url) !== -1; })) {
|
||||||
|
ajaxify.go('outgoing?url=' + encodeURIComponent(href));
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($(this).attr('data-ajaxify') === 'false') {
|
if ($(this).attr('data-ajaxify') === 'false') {
|
||||||
|
|||||||
@@ -75,6 +75,12 @@ define('forum/groups/details', [
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'kick':
|
case 'kick':
|
||||||
|
translator.translate('[[groups:details.kick_confirm]]', function (translated) {
|
||||||
|
bootbox.confirm(translated, function (confirm) {
|
||||||
|
if (!confirm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
socket.emit('groups.kick', {
|
socket.emit('groups.kick', {
|
||||||
uid: uid,
|
uid: uid,
|
||||||
groupName: groupName,
|
groupName: groupName,
|
||||||
@@ -85,6 +91,8 @@ define('forum/groups/details', [
|
|||||||
app.alertError(err.message);
|
app.alertError(err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'update':
|
case 'update':
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ apiController.getConfig = function (req, res, next) {
|
|||||||
config.bootswatchSkin = meta.config.bootswatchSkin || 'noskin';
|
config.bootswatchSkin = meta.config.bootswatchSkin || 'noskin';
|
||||||
config.defaultBootswatchSkin = meta.config.bootswatchSkin || 'noskin';
|
config.defaultBootswatchSkin = meta.config.bootswatchSkin || 'noskin';
|
||||||
|
|
||||||
|
if (config.useOutgoingLinksPage) {
|
||||||
|
config.outgoingLinksWhitelist = meta.config['outgoingLinks:whitelist'];
|
||||||
|
}
|
||||||
|
|
||||||
var timeagoCutoff = meta.config.timeagoCutoff === undefined ? 30 : meta.config.timeagoCutoff;
|
var timeagoCutoff = meta.config.timeagoCutoff === undefined ? 30 : meta.config.timeagoCutoff;
|
||||||
config.timeagoCutoff = timeagoCutoff !== '' ? Math.max(0, parseInt(timeagoCutoff, 10)) : timeagoCutoff;
|
config.timeagoCutoff = timeagoCutoff !== '' ? Math.max(0, parseInt(timeagoCutoff, 10)) : timeagoCutoff;
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,8 @@
|
|||||||
|
|
||||||
module.init = function (callback) {
|
module.init = function (callback) {
|
||||||
callback = callback || function () { };
|
callback = callback || function () { };
|
||||||
var mongoClient;
|
|
||||||
try {
|
var mongoClient = require('mongodb').MongoClient;
|
||||||
mongoClient = require('mongodb').MongoClient;
|
|
||||||
} catch (err) {
|
|
||||||
winston.error('Unable to initialize MongoDB! Is MongoDB installed? Error :' + err.message);
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var usernamePassword = '';
|
var usernamePassword = '';
|
||||||
if (nconf.get('mongo:username') && nconf.get('mongo:password')) {
|
if (nconf.get('mongo:username') && nconf.get('mongo:password')) {
|
||||||
@@ -84,10 +79,13 @@
|
|||||||
var connOptions = {
|
var connOptions = {
|
||||||
server: {
|
server: {
|
||||||
poolSize: parseInt(nconf.get('mongo:poolSize'), 10) || 10,
|
poolSize: parseInt(nconf.get('mongo:poolSize'), 10) || 10,
|
||||||
|
socketOptions: { autoReconnect: true, keepAlive: nconf.get('mongo:keepAlive') || 0 },
|
||||||
|
reconnectTries: 3600,
|
||||||
|
reconnectInterval: 1000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
connOptions = _.deepExtend((nconf.get('mongo:options') || {}), connOptions);
|
connOptions = _.deepExtend(connOptions, nconf.get('mongo:options') || {});
|
||||||
|
|
||||||
mongoClient.connect(connString, connOptions, function (err, _db) {
|
mongoClient.connect(connString, connOptions, function (err, _db) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -107,10 +105,7 @@
|
|||||||
|
|
||||||
if (nconf.get('mongo:password') && nconf.get('mongo:username')) {
|
if (nconf.get('mongo:password') && nconf.get('mongo:username')) {
|
||||||
db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) {
|
db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) {
|
||||||
if (err) {
|
callback(err);
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
winston.warn('You have no mongo password setup!');
|
winston.warn('You have no mongo password setup!');
|
||||||
|
|||||||
@@ -91,16 +91,18 @@ module.exports = function (Groups) {
|
|||||||
async.apply(db.sortedSetRemove, 'groups:visible:name', groupName.toLowerCase() + ':' + groupName),
|
async.apply(db.sortedSetRemove, 'groups:visible:name', groupName.toLowerCase() + ':' + groupName),
|
||||||
], callback);
|
], callback);
|
||||||
} else {
|
} else {
|
||||||
db.getObjectFields('group:' + groupName, ['createtime', 'memberCount'], function (err, groupData) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.getObjectFields('group:' + groupName, ['createtime', 'memberCount'], next);
|
||||||
}
|
},
|
||||||
|
function (groupData, next) {
|
||||||
async.parallel([
|
async.parallel([
|
||||||
async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName),
|
async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName),
|
||||||
async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName),
|
async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName),
|
||||||
async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName),
|
async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName),
|
||||||
|
], next);
|
||||||
|
},
|
||||||
], callback);
|
], callback);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,40 +157,48 @@ module.exports = function (Groups) {
|
|||||||
|
|
||||||
function checkNameChange(currentName, newName, callback) {
|
function checkNameChange(currentName, newName, callback) {
|
||||||
if (currentName === newName) {
|
if (currentName === newName) {
|
||||||
return callback();
|
return setImmediate(callback);
|
||||||
}
|
}
|
||||||
var currentSlug = utils.slugify(currentName);
|
var currentSlug = utils.slugify(currentName);
|
||||||
var newSlug = utils.slugify(newName);
|
var newSlug = utils.slugify(newName);
|
||||||
if (currentSlug === newSlug) {
|
if (currentSlug === newSlug) {
|
||||||
return callback();
|
return setImmediate(callback);
|
||||||
}
|
}
|
||||||
Groups.existsBySlug(newSlug, function (err, exists) {
|
async.waterfall([
|
||||||
if (err || exists) {
|
function (next) {
|
||||||
return callback(err || new Error('[[error:group-already-exists]]'));
|
Groups.existsBySlug(newSlug, next);
|
||||||
}
|
},
|
||||||
callback();
|
function (exists, next) {
|
||||||
});
|
next(exists ? new Error('[[error:group-already-exists]]') : null);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renameGroup(oldName, newName, callback) {
|
function renameGroup(oldName, newName, callback) {
|
||||||
if (oldName === newName || !newName || newName.length === 0) {
|
if (oldName === newName || !newName || newName.length === 0) {
|
||||||
return callback();
|
return setImmediate(callback);
|
||||||
}
|
}
|
||||||
|
var group;
|
||||||
db.getObject('group:' + oldName, function (err, group) {
|
async.waterfall([
|
||||||
if (err || !group) {
|
function (next) {
|
||||||
return callback(err);
|
db.getObject('group:' + oldName, next);
|
||||||
|
},
|
||||||
|
function (_group, next) {
|
||||||
|
group = _group;
|
||||||
|
if (!group) {
|
||||||
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parseInt(group.system, 10) === 1) {
|
if (parseInt(group.system, 10) === 1) {
|
||||||
return callback();
|
return callback(new Error('[[error:not-allowed-to-rename-system-group]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Groups.exists(newName, function (err, exists) {
|
Groups.exists(newName, next);
|
||||||
if (err || exists) {
|
},
|
||||||
return callback(err || new Error('[[error:group-already-exists]]'));
|
function (exists, next) {
|
||||||
|
if (exists) {
|
||||||
|
return callback(new Error('[[error:group-already-exists]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
async.apply(db.setObjectField, 'group:' + oldName, 'name', newName),
|
async.apply(db.setObjectField, 'group:' + oldName, 'name', newName),
|
||||||
async.apply(db.setObjectField, 'group:' + oldName, 'slug', utils.slugify(newName)),
|
async.apply(db.setObjectField, 'group:' + oldName, 'slug', utils.slugify(newName)),
|
||||||
@@ -222,19 +232,24 @@ module.exports = function (Groups) {
|
|||||||
|
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
], callback);
|
], next);
|
||||||
});
|
},
|
||||||
|
], function (err) {
|
||||||
|
callback(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renameGroupMember(group, oldName, newName, callback) {
|
function renameGroupMember(group, oldName, newName, callback) {
|
||||||
db.isSortedSetMember(group, oldName, function (err, isMember) {
|
|
||||||
if (err || !isMember) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
var score;
|
var score;
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
|
db.isSortedSetMember(group, oldName, next);
|
||||||
|
},
|
||||||
|
function (isMember, next) {
|
||||||
|
if (!isMember) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
db.sortedSetScore(group, oldName, next);
|
db.sortedSetScore(group, oldName, next);
|
||||||
},
|
},
|
||||||
function (_score, next) {
|
function (_score, next) {
|
||||||
@@ -245,6 +260,5 @@ module.exports = function (Groups) {
|
|||||||
db.sortedSetAdd(group, score, newName, next);
|
db.sortedSetAdd(group, score, newName, next);
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ module.exports = function (middleware) {
|
|||||||
winston.error(err.message);
|
winston.error(err.message);
|
||||||
p = '';
|
p = '';
|
||||||
}
|
}
|
||||||
|
p = validator.escape(String(p));
|
||||||
parts[index] = index ? parts[0] + '-' + p : 'page-' + (p || 'home');
|
parts[index] = index ? parts[0] + '-' + p : 'page-' + (p || 'home');
|
||||||
});
|
});
|
||||||
return parts.join(' ');
|
return parts.join(' ');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
var url = require('url');
|
var url = require('url');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
@@ -14,31 +15,26 @@ var urlRegex = /href="([^"]+)"/g;
|
|||||||
|
|
||||||
module.exports = function (Posts) {
|
module.exports = function (Posts) {
|
||||||
Posts.parsePost = function (postData, callback) {
|
Posts.parsePost = function (postData, callback) {
|
||||||
postData.content = postData.content || '';
|
postData.content = String(postData.content || '');
|
||||||
|
|
||||||
if (postData.pid && cache.has(String(postData.pid))) {
|
if (postData.pid && cache.has(String(postData.pid))) {
|
||||||
postData.content = cache.get(String(postData.pid));
|
postData.content = cache.get(String(postData.pid));
|
||||||
return callback(null, postData);
|
return callback(null, postData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Casting post content into a string, just in case
|
async.waterfall([
|
||||||
if (typeof postData.content !== 'string') {
|
function (next) {
|
||||||
postData.content = postData.content.toString();
|
plugins.fireHook('filter:parse.post', { postData: postData }, next);
|
||||||
}
|
},
|
||||||
|
function (data, next) {
|
||||||
plugins.fireHook('filter:parse.post', { postData: postData }, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.postData.content = translator.escape(data.postData.content);
|
data.postData.content = translator.escape(data.postData.content);
|
||||||
|
|
||||||
if (global.env === 'production' && data.postData.pid) {
|
if (global.env === 'production' && data.postData.pid) {
|
||||||
cache.set(String(data.postData.pid), data.postData.content);
|
cache.set(String(data.postData.pid), data.postData.content);
|
||||||
}
|
}
|
||||||
|
next(null, data.postData);
|
||||||
callback(null, data.postData);
|
},
|
||||||
});
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Posts.parseSignature = function (userData, uid, callback) {
|
Posts.parseSignature = function (userData, uid, callback) {
|
||||||
@@ -51,7 +47,6 @@ module.exports = function (Posts) {
|
|||||||
var parsed;
|
var parsed;
|
||||||
var current = urlRegex.exec(content);
|
var current = urlRegex.exec(content);
|
||||||
var absolute;
|
var absolute;
|
||||||
|
|
||||||
while (current !== null) {
|
while (current !== null) {
|
||||||
if (current[1]) {
|
if (current[1]) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ var social = require('./social');
|
|||||||
isIgnoring: async.apply(Topics.isIgnoring, [topicData.tid], uid),
|
isIgnoring: async.apply(Topics.isIgnoring, [topicData.tid], uid),
|
||||||
bookmark: async.apply(Topics.getUserBookmark, topicData.tid, uid),
|
bookmark: async.apply(Topics.getUserBookmark, topicData.tid, uid),
|
||||||
postSharing: async.apply(social.getActivePostSharing),
|
postSharing: async.apply(social.getActivePostSharing),
|
||||||
|
deleter: async.apply(getDeleter, topicData),
|
||||||
related: function (next) {
|
related: function (next) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
@@ -202,6 +203,8 @@ var social = require('./social');
|
|||||||
topicData.isIgnoring = results.isIgnoring[0];
|
topicData.isIgnoring = results.isIgnoring[0];
|
||||||
topicData.bookmark = results.bookmark;
|
topicData.bookmark = results.bookmark;
|
||||||
topicData.postSharing = results.postSharing;
|
topicData.postSharing = results.postSharing;
|
||||||
|
topicData.deleter = results.deleter;
|
||||||
|
topicData.deletedTimestampISO = utils.toISOString(topicData.deletedTimestamp);
|
||||||
topicData.related = results.related || [];
|
topicData.related = results.related || [];
|
||||||
|
|
||||||
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
|
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
|
||||||
@@ -258,6 +261,13 @@ var social = require('./social');
|
|||||||
], callback);
|
], callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDeleter(topicData, callback) {
|
||||||
|
if (!topicData.deleterUid) {
|
||||||
|
return setImmediate(callback, null, null);
|
||||||
|
}
|
||||||
|
user.getUserFields(topicData.deleterUid, ['username', 'userslug', 'picture'], callback);
|
||||||
|
}
|
||||||
|
|
||||||
Topics.getMainPost = function (tid, uid, callback) {
|
Topics.getMainPost = function (tid, uid, callback) {
|
||||||
Topics.getMainPosts([tid], uid, function (err, mainPosts) {
|
Topics.getMainPosts([tid], uid, function (err, mainPosts) {
|
||||||
callback(err, Array.isArray(mainPosts) && mainPosts.length ? mainPosts[0] : null);
|
callback(err, Array.isArray(mainPosts) && mainPosts.length ? mainPosts[0] : null);
|
||||||
|
|||||||
@@ -86,4 +86,8 @@ module.exports = function (Topics) {
|
|||||||
Topics.deleteTopicField = function (tid, field, callback) {
|
Topics.deleteTopicField = function (tid, field, callback) {
|
||||||
db.deleteObjectField('topic:' + tid, field, callback);
|
db.deleteObjectField('topic:' + tid, field, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Topics.deleteTopicFields = function (tid, fields, callback) {
|
||||||
|
db.deleteObjectFields('topic:' + tid, fields, callback);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ module.exports = function (Topics) {
|
|||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function (next) {
|
function (next) {
|
||||||
Topics.setTopicField(tid, 'deleted', 1, next);
|
Topics.setTopicFields(tid, {
|
||||||
|
deleted: 1,
|
||||||
|
deleterUid: uid,
|
||||||
|
deletedTimestamp: Date.now(),
|
||||||
|
}, next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
db.sortedSetsRemove(['topics:recent', 'topics:posts', 'topics:views'], tid, next);
|
db.sortedSetsRemove(['topics:recent', 'topics:posts', 'topics:views'], tid, next);
|
||||||
@@ -47,6 +51,9 @@ module.exports = function (Topics) {
|
|||||||
function (next) {
|
function (next) {
|
||||||
Topics.setTopicField(tid, 'deleted', 0, next);
|
Topics.setTopicField(tid, 'deleted', 0, next);
|
||||||
},
|
},
|
||||||
|
function (next) {
|
||||||
|
Topics.deleteTopicFields(tid, ['deleterUid', 'deletedTimestamp'], next);
|
||||||
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
Topics.updateRecent(tid, topicData.lastposttime, next);
|
Topics.updateRecent(tid, topicData.lastposttime, next);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ module.exports = function (Topics) {
|
|||||||
Topics.markAsRead = function (tids, uid, callback) {
|
Topics.markAsRead = function (tids, uid, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
if (!Array.isArray(tids) || !tids.length) {
|
if (!Array.isArray(tids) || !tids.length) {
|
||||||
return callback();
|
return setImmediate(callback, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
tids = tids.filter(function (tid, index, array) {
|
tids = tids.filter(function (tid, index, array) {
|
||||||
@@ -212,7 +212,7 @@ module.exports = function (Topics) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!tids.length) {
|
if (!tids.length) {
|
||||||
return callback(null, false);
|
return setImmediate(callback, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
<label>[[admin/settings/general:description]]</label>
|
<label>[[admin/settings/general:description]]</label>
|
||||||
<input type="text" class="form-control" placeholder="[[admin/settings/general:description.placeholder]]" data-field="description" /><br />
|
<input type="text" class="form-control" placeholder="[[admin/settings/general:description.placeholder]]" data-field="description" /><br />
|
||||||
|
|
||||||
<label>[[admin/settings/general:keywords]]</label>
|
<label>[[admin/settings/general:keywords]]</label><br />
|
||||||
<input type="text" class="form-control" placeholder="[[admin/settings/general:keywords-placeholder]]" data-field="keywords" /><br />
|
<input type="text" class="form-control" placeholder="[[admin/settings/general:keywords-placeholder]]" data-field="keywords" data-field-type="tagsinput" /><br />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,6 +140,11 @@
|
|||||||
<span class="mdl-switch__label"><strong>[[admin/settings/general:outgoing-links.warning-page]]</strong></span>
|
<span class="mdl-switch__label"><strong>[[admin/settings/general:outgoing-links.warning-page]]</strong></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="outgoingLinks:whitelist">[[admin/settings/general:outgoing-links.whitelist]]</label><br />
|
||||||
|
<input id="outgoingLinks:whitelist" type="text" class="form-control" placeholder="subdomain.domain.com" data-field="outgoingLinks:whitelist" data-field-type="tagsinput" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
[[admin/settings/group:default-cover-help]]
|
[[admin/settings/group:default-cover-help]]
|
||||||
</p>
|
</p>
|
||||||
<input type="text" class="form-control input-lg" id="groups:defaultCovers" data-field="groups:defaultCovers" value="{config.relative_path}/assets/images/cover-default.png" placeholder="https://example.com/group1.png, https://example.com/group2.png" /><br />
|
<input type="text" class="form-control input-lg" id="groups:defaultCovers" data-field="groups:defaultCovers" data-field-type="tagsinput" value="{config.relative_path}/assets/images/cover-default.png" placeholder="https://example.com/group1.png, https://example.com/group2.png" /><br />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="allowedFileExtensions">[[admin/settings/uploads:allowed-file-extensions]]</label>
|
<label for="allowedFileExtensions">[[admin/settings/uploads:allowed-file-extensions]]</label>
|
||||||
<input type="text" class="form-control" value="" data-field="allowedFileExtensions" />
|
<input type="text" class="form-control" value="" data-field="allowedFileExtensions" data-field-type="tagsinput" />
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
[[admin/settings/uploads:allowed-file-extensions-help]]
|
[[admin/settings/uploads:allowed-file-extensions-help]]
|
||||||
</p>
|
</p>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
[[admin/settings/uploads:default-covers-help]]
|
[[admin/settings/uploads:default-covers-help]]
|
||||||
</p>
|
</p>
|
||||||
<input type="text" class="form-control input-lg" id="profile:defaultCovers" data-field="profile:defaultCovers" value="{config.relative_path}/assets/images/cover-default.png" placeholder="https://example.com/group1.png, https://example.com/group2.png" />
|
<input type="text" class="form-control input-lg" id="profile:defaultCovers" data-field="profile:defaultCovers" data-field-type="tagsinput" value="{config.relative_path}/assets/images/cover-default.png" placeholder="https://example.com/group1.png, https://example.com/group2.png" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -315,6 +315,15 @@ describe('Groups', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail if system groups is being renamed', function (done) {
|
||||||
|
Groups.update('administrators', {
|
||||||
|
name: 'administrators_fail',
|
||||||
|
}, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:not-allowed-to-rename-system-group]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.destroy()', function () {
|
describe('.destroy()', function () {
|
||||||
|
|||||||
@@ -534,6 +534,50 @@ describe('Post\'s', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parse', function () {
|
||||||
|
it('should store post content in cache', function (done) {
|
||||||
|
var oldValue = global.env;
|
||||||
|
global.env = 'production';
|
||||||
|
var postData = {
|
||||||
|
pid: 9999,
|
||||||
|
content: 'some post content',
|
||||||
|
};
|
||||||
|
posts.parsePost(postData, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
posts.parsePost(postData, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
global.env = oldValue;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse signature and remove links and images', function (done) {
|
||||||
|
var meta = require('../src/meta');
|
||||||
|
meta.config['signatures:disableLinks'] = 1;
|
||||||
|
meta.config['signatures:disableImages'] = 1;
|
||||||
|
var userData = {
|
||||||
|
signature: '<img src="boop"/><a href="link">test</a> derp',
|
||||||
|
};
|
||||||
|
|
||||||
|
posts.parseSignature(userData, 1, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data.userData.signature, 'test derp');
|
||||||
|
meta.config['signatures:disableLinks'] = 0;
|
||||||
|
meta.config['signatures:disableImages'] = 0;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should turn relative links in post body to absolute urls', function (done) {
|
||||||
|
var nconf = require('nconf');
|
||||||
|
var content = '<a href="/users">test</a> <a href="youtube.com">youtube</a>';
|
||||||
|
var parsedContent = posts.relativeToAbsolute(content);
|
||||||
|
assert.equal(parsedContent, '<a href="' + nconf.get('url') + '/users">test</a> <a href="//youtube.com">youtube</a>');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('socket methods', function () {
|
describe('socket methods', function () {
|
||||||
var pid;
|
var pid;
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
@@ -600,7 +644,7 @@ describe('Post\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('shold error with invalid data', function (done) {
|
it('shold error with invalid data', function (done) {
|
||||||
socketPosts.loadMoreBookmarks({ uid: voterUid }, { uid: voterUid, after: null }, function (err, postData) {
|
socketPosts.loadMoreBookmarks({ uid: voterUid }, { uid: voterUid, after: null }, function (err) {
|
||||||
assert.equal(err.message, '[[error:invalid-data]]');
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1181,6 +1181,14 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not do anything if tids is empty array', function (done) {
|
||||||
|
socketTopics.markAsRead({ uid: adminUid }, [], function (err, markedRead) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(!markedRead);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('tags', function () {
|
describe('tags', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user