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

v1.18.x
Julian Lam 8 years ago
commit 860999fa6c

@ -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,8 +366,13 @@ $(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);
e.preventDefault(); var href = this.href;
if (!safeUrls.some(function (url) { return href.indexOf(url) !== -1; })) {
ajaxify.go('outgoing?url=' + encodeURIComponent(href));
e.preventDefault();
}
} }
} }
} }

@ -75,15 +75,23 @@ define('forum/groups/details', [
break; break;
case 'kick': case 'kick':
socket.emit('groups.kick', { translator.translate('[[groups:details.kick_confirm]]', function (translated) {
uid: uid, bootbox.confirm(translated, function (confirm) {
groupName: groupName, if (!confirm) {
}, function (err) { return;
if (!err) { }
userRow.slideUp().remove();
} else { socket.emit('groups.kick', {
app.alertError(err.message); uid: uid,
} groupName: groupName,
}, function (err) {
if (!err) {
userRow.slideUp().remove();
} else {
app.alertError(err.message);
}
});
});
}); });
break; break;

@ -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);
} },
async.parallel([ function (groupData, next) {
async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName), async.parallel([
async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName), async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName),
async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName), async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName),
], callback); async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName),
}); ], next);
},
], 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;
async.waterfall([
function (next) {
db.getObject('group:' + oldName, next);
},
function (_group, next) {
group = _group;
if (!group) {
return callback();
}
db.getObject('group:' + oldName, function (err, group) { if (parseInt(group.system, 10) === 1) {
if (err || !group) { return callback(new Error('[[error:not-allowed-to-rename-system-group]]'));
return callback(err);
}
if (parseInt(group.system, 10) === 1) {
return callback();
}
Groups.exists(newName, function (err, exists) {
if (err || exists) {
return callback(err || new Error('[[error:group-already-exists]]'));
} }
Groups.exists(newName, next);
},
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,29 +232,33 @@ 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) { var score;
if (err || !isMember) { async.waterfall([
return callback(err); function (next) {
} db.isSortedSetMember(group, oldName, next);
var score; },
async.waterfall([ function (isMember, next) {
function (next) { if (!isMember) {
db.sortedSetScore(group, oldName, next); return callback();
}, }
function (_score, next) {
score = _score; db.sortedSetScore(group, oldName, next);
db.sortedSetRemove(group, oldName, next); },
}, function (_score, next) {
function (next) { score = _score;
db.sortedSetAdd(group, score, newName, next); db.sortedSetRemove(group, oldName, next);
}, },
], callback); function (next) {
}); db.sortedSetAdd(group, score, newName, next);
},
], 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) { data.postData.content = translator.escape(data.postData.content);
if (err) {
return callback(err);
}
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 {
@ -78,7 +73,7 @@ module.exports = function (Posts) {
}; };
function sanitizeSignature(signature) { function sanitizeSignature(signature) {
var string = S(signature); var string = S(signature);
var tagsToStrip = []; var tagsToStrip = [];
if (parseInt(meta.config['signatures:disableLinks'], 10) === 1) { if (parseInt(meta.config['signatures:disableLinks'], 10) === 1) {

@ -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 () {

Loading…
Cancel
Save