mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-03-13 16:10:43 +01:00
Merge remote-tracking branch 'origin/master' into 0.7.0
This commit is contained in:
@@ -364,7 +364,7 @@ app.uid = null;
|
||||
}
|
||||
};
|
||||
|
||||
function exposeConfigToTemplates() {
|
||||
app.exposeConfigToTemplates = function() {
|
||||
$(document).ready(function() {
|
||||
templates.setGlobal('loggedIn', config.loggedIn);
|
||||
templates.setGlobal('relative_path', RELATIVE_PATH);
|
||||
@@ -599,7 +599,7 @@ app.uid = null;
|
||||
|
||||
showWelcomeMessage = window.location.href.indexOf('loggedin') !== -1;
|
||||
|
||||
exposeConfigToTemplates();
|
||||
app.exposeConfigToTemplates();
|
||||
|
||||
socketIOConnect();
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) {
|
||||
config[key] = newSettings[key];
|
||||
}
|
||||
}
|
||||
app.exposeConfigToTemplates();
|
||||
|
||||
if (parseInt(app.uid, 10) === parseInt(ajaxify.variables.get('theirid'), 10)) {
|
||||
ajaxify.refresh();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
/* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH */
|
||||
/* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH, utils */
|
||||
|
||||
define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect) {
|
||||
var Details = {
|
||||
@@ -122,7 +122,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker',
|
||||
if (settings.name) {
|
||||
var pathname = window.location.pathname;
|
||||
pathname = pathname.substr(1, pathname.lastIndexOf('/'));
|
||||
ajaxify.go(pathname + encodeURIComponent(settings.name));
|
||||
ajaxify.go(pathname + utils.slugify(settings.name));
|
||||
} else {
|
||||
ajaxify.refresh();
|
||||
}
|
||||
@@ -134,7 +134,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker',
|
||||
};
|
||||
|
||||
Details.deleteGroup = function() {
|
||||
bootbox.confirm('Are you sure you want to delete the group: ' + ajaxify.variables.get('group_name'), function(confirm) {
|
||||
bootbox.confirm('Are you sure you want to delete the group: ' + utils.escapeHTML(ajaxify.variables.get('group_name')), function(confirm) {
|
||||
if (confirm) {
|
||||
bootbox.prompt('Please enter the name of this group in order to delete it:', function(response) {
|
||||
if (response === ajaxify.variables.get('group_name')) {
|
||||
@@ -142,7 +142,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker',
|
||||
groupName: ajaxify.variables.get('group_name')
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
app.alertSuccess('[[groups:event.deleted, ' + ajaxify.variables.get('group_name') + ']]');
|
||||
app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(ajaxify.variables.get('group_name')) + ']]');
|
||||
ajaxify.go('groups');
|
||||
} else {
|
||||
app.alertError(err.message);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
/* globals app, define, ajaxify, socket, bootbox */
|
||||
/* globals app, define, ajaxify, socket, bootbox, utils */
|
||||
|
||||
define('forum/groups/list', function() {
|
||||
var Groups = {};
|
||||
@@ -10,7 +10,7 @@ define('forum/groups/list', function() {
|
||||
groupsEl.on('click', '.list-cover', function() {
|
||||
var groupName = $(this).parents('[data-group]').attr('data-group');
|
||||
|
||||
ajaxify.go('groups/' + encodeURIComponent(groupName));
|
||||
ajaxify.go('groups/' + utils.slugify(groupName));
|
||||
});
|
||||
|
||||
// Group creation
|
||||
@@ -21,7 +21,7 @@ define('forum/groups/list', function() {
|
||||
name: name
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
ajaxify.go('groups/' + name);
|
||||
ajaxify.go('groups/' + utils.slugify(name));
|
||||
} else {
|
||||
app.alertError(err.message);
|
||||
}
|
||||
|
||||
@@ -39,15 +39,15 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com
|
||||
|
||||
function addVoteHandler() {
|
||||
$('#post-container').on('mouseenter', '.post-row .votes', function() {
|
||||
loadDataAndCreateTooltip($(this), 'posts.getUpvoters');
|
||||
loadDataAndCreateTooltip($(this));
|
||||
});
|
||||
}
|
||||
|
||||
function loadDataAndCreateTooltip(el, method) {
|
||||
function loadDataAndCreateTooltip(el) {
|
||||
var pid = el.parents('.post-row').attr('data-pid');
|
||||
socket.emit(method, pid, function(err, data) {
|
||||
if (!err) {
|
||||
createTooltip(el, data);
|
||||
socket.emit('posts.getUpvoters', [pid], function(err, data) {
|
||||
if (!err && data.length) {
|
||||
createTooltip(el, data[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,11 +25,14 @@ define('notifications', ['sounds'], function(sound) {
|
||||
image = '';
|
||||
}
|
||||
|
||||
return '<li class="' + (notification.readClass || '') + '"><a href="' + (notification.path || '#') + '">' + image + '<span class="pull-right relTime">' + utils.relativeTime(notification.datetime, true) + '</span><span class="text">' + notification.bodyShort + '</span></a></li>';
|
||||
return '<li class="' + (notification.readClass || '') + '"><a href="' + (notification.path || '#') + '">' + image + '<span class="pull-right relTime">' + $.timeago(new Date(parseInt(notification.datetime, 10))) + '</span><span class="text">' + notification.bodyShort + '</span></a></li>';
|
||||
}
|
||||
|
||||
var x, html = '';
|
||||
|
||||
// Switch to shorthand
|
||||
translator.toggleTimeagoShorthand();
|
||||
|
||||
if (!err && (data.read.length + data.unread.length) > 0) {
|
||||
var image = '';
|
||||
for (x = 0; x < data.unread.length; x++) {
|
||||
@@ -43,6 +46,9 @@ define('notifications', ['sounds'], function(sound) {
|
||||
html += '<li class="no-notifs"><a>[[notifications:no_notifs]]</a></li>';
|
||||
}
|
||||
|
||||
// Switch back to original timeago strings
|
||||
translator.toggleTimeagoShorthand();
|
||||
|
||||
html += '<li class="pagelink"><a href="' + config.relative_path + '/notifications">[[notifications:see_all]]</a></li>';
|
||||
|
||||
notifList.translateHtml(html);
|
||||
|
||||
@@ -85,6 +85,34 @@
|
||||
}
|
||||
};
|
||||
|
||||
translator.toggleTimeagoShorthand = function() {
|
||||
if (!translator.timeagoStrings) {
|
||||
translator.timeagoStrings = $.extend({}, jQuery.timeago.settings.strings);
|
||||
jQuery.timeago.settings.strings = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: "",
|
||||
suffixFromNow: "",
|
||||
seconds: "1m",
|
||||
minute: "1m",
|
||||
minutes: "%dm",
|
||||
hour: "1h",
|
||||
hours: "%dh",
|
||||
day: "1d",
|
||||
days: "%dd",
|
||||
month: "1mo",
|
||||
months: "%dmo",
|
||||
year: "1yr",
|
||||
years: "%dyr",
|
||||
wordSeparator: " ",
|
||||
numbers: []
|
||||
};
|
||||
} else {
|
||||
jQuery.timeago.settings.strings = $.extend({}, translator.timeagoStrings);
|
||||
delete translator.timeagoStrings;
|
||||
}
|
||||
};
|
||||
|
||||
translator.translate = function (text, language, callback) {
|
||||
if (typeof language === 'function') {
|
||||
callback = language;
|
||||
|
||||
@@ -63,44 +63,6 @@
|
||||
});
|
||||
},
|
||||
|
||||
relativeTime: function(timestamp, min) {
|
||||
var now = +new Date(),
|
||||
difference = now - Math.floor(parseFloat(timestamp));
|
||||
|
||||
if(difference < 0) {
|
||||
difference = 0;
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 1000);
|
||||
|
||||
if (difference < 60) {
|
||||
return difference + (min ? 's' : ' second') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 60) {
|
||||
return difference + (min ? 'm' : ' minute') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 24) {
|
||||
return difference + (min ? 'h' : ' hour') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 24);
|
||||
if (difference < 30) {
|
||||
return difference + (min ? 'd' : ' day') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 30);
|
||||
if (difference < 12) {
|
||||
return difference + (min ? 'mon' : ' month') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 12);
|
||||
return difference + (min ? 'y' : ' year') + (difference !== 1 && !min ? 's' : '');
|
||||
},
|
||||
|
||||
invalidUnicodeChars: XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'),
|
||||
invalidLatinChars: /[^\w\s\d\-_]/g,
|
||||
trimRegex: /^\s+|\s+$/g,
|
||||
@@ -119,7 +81,7 @@
|
||||
str = XRegExp.replace(str, utils.invalidUnicodeChars, '-');
|
||||
}
|
||||
str = str.toLocaleLowerCase();
|
||||
str = str.replace(utils.collapseWhitespace, '-')
|
||||
str = str.replace(utils.collapseWhitespace, '-');
|
||||
str = str.replace(utils.collapseDash, '-');
|
||||
str = str.replace(utils.trimTrailingDash, '');
|
||||
str = str.replace(utils.trimLeadingDash, '');
|
||||
@@ -193,7 +155,7 @@
|
||||
return function (path) {
|
||||
var extension = utils.fileExtension(path);
|
||||
return map[extension] || '*';
|
||||
}
|
||||
};
|
||||
})(),
|
||||
|
||||
isRelativeUrl: function(url) {
|
||||
@@ -248,6 +210,10 @@
|
||||
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
},
|
||||
|
||||
escapeHTML: function(raw) {
|
||||
return raw.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">");
|
||||
},
|
||||
|
||||
isAndroidBrowser: function() {
|
||||
// http://stackoverflow.com/questions/9286355/how-to-detect-only-the-native-android-browser
|
||||
var nua = navigator.userAgent;
|
||||
@@ -289,8 +255,9 @@
|
||||
key = decodeURI(val[0]),
|
||||
value = options.skipToType[key] ? decodeURI(val[1]) : utils.toType(decodeURI(val[1]));
|
||||
|
||||
if (key)
|
||||
if (key) {
|
||||
hash[key] = value;
|
||||
}
|
||||
});
|
||||
return hash;
|
||||
},
|
||||
@@ -331,12 +298,15 @@
|
||||
return str;
|
||||
} else {
|
||||
var nb = parseFloat(str);
|
||||
if (!isNaN(nb) && isFinite(str))
|
||||
if (!isNaN(nb) && isFinite(str)) {
|
||||
return nb;
|
||||
if (str === 'false')
|
||||
}
|
||||
if (str === 'false') {
|
||||
return false;
|
||||
if (str === 'true')
|
||||
}
|
||||
if (str === 'true') {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
str = JSON.parse(str);
|
||||
@@ -352,21 +322,25 @@
|
||||
// get example: utils.props(A, 'a.b.c.foo.bar') // returns undefined without throwing a TypeError
|
||||
// credits to github.com/gkindel
|
||||
props: function(obj, props, value) {
|
||||
if(obj === undefined)
|
||||
if(obj === undefined) {
|
||||
obj = window;
|
||||
if(props == null)
|
||||
}
|
||||
if(props == null) {
|
||||
return undefined;
|
||||
}
|
||||
var i = props.indexOf('.');
|
||||
if( i == -1 ) {
|
||||
if(value !== undefined)
|
||||
if(value !== undefined) {
|
||||
obj[props] = value;
|
||||
}
|
||||
return obj[props];
|
||||
}
|
||||
var prop = props.slice(0, i),
|
||||
newProps = props.slice(i + 1);
|
||||
|
||||
if(props !== undefined && !(obj[prop] instanceof Object) )
|
||||
if(props !== undefined && !(obj[prop] instanceof Object) ) {
|
||||
obj[prop] = {};
|
||||
}
|
||||
|
||||
return utils.props(obj[prop], newProps, value);
|
||||
}
|
||||
@@ -374,10 +348,12 @@
|
||||
|
||||
if (typeof String.prototype.startsWith != 'function') {
|
||||
String.prototype.startsWith = function (prefix){
|
||||
if (this.length < prefix.length)
|
||||
if (this.length < prefix.length) {
|
||||
return false;
|
||||
for (var i = prefix.length - 1; (i >= 0) && (this[i] === prefix[i]); --i)
|
||||
}
|
||||
for (var i = prefix.length - 1; (i >= 0) && (this[i] === prefix[i]); --i) {
|
||||
continue;
|
||||
}
|
||||
return i < 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ module.exports = function(Categories) {
|
||||
'cid:' + cid + ':tids',
|
||||
'cid:' + cid + ':tids:posts',
|
||||
'cid:' + cid + ':pids',
|
||||
'cid:' + cid + ':read_by_uid',
|
||||
'category:' + cid
|
||||
], next);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,18 @@ module.exports = function(Categories) {
|
||||
Categories.update = function(modified, callback) {
|
||||
|
||||
function updateCategory(cid, next) {
|
||||
var category = modified[cid];
|
||||
var fields = Object.keys(category);
|
||||
Categories.exists(cid, function(err, exists) {
|
||||
if (err || !exists) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
async.each(fields, function(key, next) {
|
||||
updateCategoryField(cid, key, category[key], next);
|
||||
}, next);
|
||||
var category = modified[cid];
|
||||
var fields = Object.keys(category);
|
||||
|
||||
async.each(fields, function(key, next) {
|
||||
updateCategoryField(cid, key, category[key], next);
|
||||
}, next);
|
||||
});
|
||||
}
|
||||
|
||||
var cids = Object.keys(modified);
|
||||
|
||||
@@ -26,7 +26,7 @@ groupsController.details = function(req, res, next) {
|
||||
|
||||
async.parallel({
|
||||
group: function(next) {
|
||||
groups.get(req.params.name, {
|
||||
groups.getByGroupslug(req.params.slug, {
|
||||
expand: true,
|
||||
uid: uid
|
||||
}, next);
|
||||
|
||||
@@ -174,6 +174,31 @@ module.exports = function(db, module) {
|
||||
});
|
||||
};
|
||||
|
||||
module.isObjectFields = function(key, fields, callback) {
|
||||
if (!key) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
var data = {};
|
||||
fields.forEach(function(field) {
|
||||
field = helpers.fieldToString(field);
|
||||
data[field] = '';
|
||||
});
|
||||
|
||||
db.collection('objects').findOne({_key: key}, {fields: data}, function(err, item) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var results = [];
|
||||
|
||||
fields.forEach(function(field, index) {
|
||||
results[index] = !!item && item[field] !== undefined && item[field] !== null;
|
||||
});
|
||||
|
||||
callback(null, results);
|
||||
});
|
||||
};
|
||||
|
||||
module.deleteObjectField = function(key, field, callback) {
|
||||
callback = callback || helpers.noop;
|
||||
if (!key || !field) {
|
||||
|
||||
@@ -88,7 +88,26 @@ module.exports = function(redisClient, module) {
|
||||
});
|
||||
};
|
||||
|
||||
module.isObjectFields = function(key, fields, callback) {
|
||||
var multi = redisClient.multi();
|
||||
for (var i=0; i<fields.length; ++i) {
|
||||
multi.hexists(key, fields[i]);
|
||||
}
|
||||
|
||||
multi.exec(function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
for (var i=0; i<results.length; ++i) {
|
||||
results[i] = results[i] === 1;
|
||||
}
|
||||
callback(null, results);
|
||||
});
|
||||
};
|
||||
|
||||
module.deleteObjectField = function(key, field, callback) {
|
||||
callback = callback || function() {};
|
||||
redisClient.hdel(key, field, function(err, res) {
|
||||
callback(err);
|
||||
});
|
||||
|
||||
@@ -147,7 +147,7 @@ var async = require('async'),
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (options.expand) {
|
||||
if (options.expand && uids.length) {
|
||||
async.map(uids, user.getUserData, next);
|
||||
} else {
|
||||
next(err, uids);
|
||||
@@ -193,8 +193,10 @@ var async = require('async'),
|
||||
});
|
||||
}
|
||||
}, function (err, results) {
|
||||
if (err || !results.base) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
} else if (!results.base) {
|
||||
return callback(new Error('[[error:no-group]]'));
|
||||
}
|
||||
|
||||
// Default image
|
||||
@@ -220,7 +222,21 @@ var async = require('async'),
|
||||
results.base.isPending = results.isPending;
|
||||
results.base.isOwner = results.isOwner;
|
||||
|
||||
callback(err, results.base);
|
||||
plugins.fireHook('filter:group.get', {group: results.base}, function(err, data) {
|
||||
callback(err, data ? data.group : null);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getByGroupslug = function(slug, options, callback) {
|
||||
db.getObjectField('groupslug:groupname', slug, function(err, groupName) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
} else if (!groupName) {
|
||||
return callback(new Error('[[error:no-group]]'));
|
||||
}
|
||||
|
||||
Groups.get.call(Groups, groupName, options, callback);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -389,9 +405,29 @@ var async = require('async'),
|
||||
|
||||
Groups.exists = function(name, callback) {
|
||||
if (Array.isArray(name)) {
|
||||
db.isSetMembers('groups', name, callback);
|
||||
var slugs = name.map(function(groupName) {
|
||||
return utils.slugify(groupName);
|
||||
});
|
||||
async.parallel([
|
||||
async.apply(db.isObjectFields, 'groupslug:groupname', slugs),
|
||||
async.apply(db.isSetMembers, 'groups', name)
|
||||
], function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, results.map(function(pair) {
|
||||
return pair[0] || pair[1];
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
db.isSetMember('groups', name, callback);
|
||||
var slug = utils.slugify(name);
|
||||
async.parallel([
|
||||
async.apply(db.isObjectField, 'groupslug:groupname', slug),
|
||||
async.apply(db.isSetMember, 'groups', name)
|
||||
], function(err, results) {
|
||||
callback(err, !err ? (results[0] || results[1]) : null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -413,8 +449,10 @@ var async = require('async'),
|
||||
return callback(new Error('[[error:group-already-exists]]'));
|
||||
}
|
||||
|
||||
var groupData = {
|
||||
var slug = utils.slugify(data.name),
|
||||
groupData = {
|
||||
name: data.name,
|
||||
slug: slug,
|
||||
userTitle: data.name,
|
||||
description: data.description || '',
|
||||
deleted: '0',
|
||||
@@ -432,11 +470,13 @@ var async = require('async'),
|
||||
tasks.push(async.apply(db.setAdd, 'group:' + data.name + ':members', data.ownerUid));
|
||||
}
|
||||
|
||||
if (!data.hidden) {
|
||||
tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', slug, data.name));
|
||||
}
|
||||
|
||||
async.parallel(tasks, function(err) {
|
||||
if (!err) {
|
||||
plugins.fireHook('action:group.create', {
|
||||
name: data.name
|
||||
});
|
||||
plugins.fireHook('action:group.create', groupData);
|
||||
}
|
||||
|
||||
callback(err);
|
||||
@@ -472,7 +512,7 @@ var async = require('async'),
|
||||
|
||||
plugins.fireHook('action:group.update', {
|
||||
name: groupName,
|
||||
values: payload
|
||||
values: values
|
||||
});
|
||||
renameGroup(groupName, values.name, callback);
|
||||
});
|
||||
@@ -502,6 +542,15 @@ var async = require('async'),
|
||||
function(next) {
|
||||
db.setObjectField('group:' + oldName, 'name', newName, next);
|
||||
},
|
||||
function(next) {
|
||||
db.setObjectField('group:' + oldName, 'slug', utils.slugify(newName), next);
|
||||
},
|
||||
function(next) {
|
||||
db.deleteObjectField('groupslug:groupname', group.slug, next);
|
||||
},
|
||||
function(next) {
|
||||
db.setObjectField('groupslug:groupname', utils.slugify(newName), newName, next);
|
||||
},
|
||||
function(next) {
|
||||
db.getSetMembers('groups', function(err, groups) {
|
||||
if (err) {
|
||||
@@ -569,6 +618,7 @@ var async = require('async'),
|
||||
async.apply(db.delete, 'group:' + groupName + ':members'),
|
||||
async.apply(db.delete, 'group:' + groupName + ':pending'),
|
||||
async.apply(db.delete, 'group:' + groupName + ':owners'),
|
||||
async.apply(db.deleteObjectField, 'groupslug:groupname', utils.slugify(groupName)),
|
||||
function(next) {
|
||||
db.getSetMembers('groups', function(err, groups) {
|
||||
if (err) {
|
||||
@@ -580,7 +630,7 @@ var async = require('async'),
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
Groups.join = function(groupName, uid, callback) {
|
||||
@@ -725,7 +775,7 @@ var async = require('async'),
|
||||
return 'group:' + groupName;
|
||||
});
|
||||
|
||||
db.getObjectsFields(groupKeys, ['name', 'hidden', 'userTitle', 'icon', 'labelColor'], function(err, groupData) {
|
||||
db.getObjectsFields(groupKeys, ['name', 'slug', 'hidden', 'userTitle', 'icon', 'labelColor'], function(err, groupData) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
@@ -268,11 +268,7 @@ function enableDefaultTheme(next) {
|
||||
function createAdministrator(next) {
|
||||
var Groups = require('./groups');
|
||||
Groups.get('administrators', {}, function (err, groupObj) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (groupObj && groupObj.memberCount > 0) {
|
||||
if (!err && groupObj && groupObj.memberCount > 0) {
|
||||
winston.info('Administrator found, skipping Admin setup');
|
||||
next();
|
||||
} else {
|
||||
|
||||
@@ -26,7 +26,6 @@ module.exports = function(Meta) {
|
||||
}
|
||||
|
||||
Meta.title.parseFragment(uri, language, locals, function(err, title) {
|
||||
|
||||
if (err) {
|
||||
title = fallbackTitle;
|
||||
} else {
|
||||
@@ -41,7 +40,6 @@ module.exports = function(Meta) {
|
||||
};
|
||||
|
||||
Meta.title.parseFragment = function (urlFragment, language, locals, callback) {
|
||||
urlFragment = validator.escape(urlFragment);
|
||||
var translated = ['', 'recent', 'unread', 'users', 'notifications'];
|
||||
if (translated.indexOf(urlFragment) !== -1) {
|
||||
if (!urlFragment.length) {
|
||||
@@ -75,7 +73,7 @@ module.exports = function(Meta) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (locals.notFound) {
|
||||
if (!username) {
|
||||
username = '[[error:no-user]]';
|
||||
}
|
||||
|
||||
|
||||
@@ -450,7 +450,12 @@ middleware.maintenanceMode = function(req, res, next) {
|
||||
'/login',
|
||||
'/stylesheet.css',
|
||||
'/nodebb.min.js',
|
||||
'/vendor/fontawesome/fonts/fontawesome-webfont.woff'
|
||||
'/vendor/fontawesome/fonts/fontawesome-webfont.woff',
|
||||
'/src/modules/[\\w]+\.js',
|
||||
'/api/get_templates_listing',
|
||||
'/api/login',
|
||||
'/api/?',
|
||||
'/language/.+'
|
||||
],
|
||||
render = function() {
|
||||
res.status(503);
|
||||
|
||||
@@ -93,7 +93,7 @@ function groupRoutes(app, middleware, controllers) {
|
||||
var middlewares = [middleware.checkGlobalPrivacySettings];
|
||||
|
||||
setupPageRoute(app, '/groups', middleware, middlewares, controllers.groups.list);
|
||||
setupPageRoute(app, '/groups/:name', middleware, middlewares, controllers.groups.details);
|
||||
setupPageRoute(app, '/groups/:slug', middleware, middlewares, controllers.groups.details);
|
||||
}
|
||||
|
||||
function setupPageRoute(router, name, middleware, middlewares, controller) {
|
||||
|
||||
@@ -124,11 +124,6 @@ function onMessage(socket, payload) {
|
||||
return winston.warn('[socket.io] Empty method name');
|
||||
}
|
||||
|
||||
if (ratelimit.isFlooding(socket)) {
|
||||
winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Message : ' + eventName);
|
||||
return socket.disconnect();
|
||||
}
|
||||
|
||||
var parts = eventName.toString().split('.'),
|
||||
namespace = parts[0],
|
||||
methodToCall = parts.reduce(function(prev, cur) {
|
||||
@@ -146,6 +141,17 @@ function onMessage(socket, payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.previousEvents = socket.previousEvents || [];
|
||||
socket.previousEvents.push(eventName);
|
||||
if (socket.previousEvents.length > 20) {
|
||||
socket.previousEvents.shift();
|
||||
}
|
||||
|
||||
if (ratelimit.isFlooding(socket)) {
|
||||
winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents);
|
||||
return socket.disconnect();
|
||||
}
|
||||
|
||||
if (Namespaces[namespace].before) {
|
||||
Namespaces[namespace].before(socket, eventName, function() {
|
||||
callMethod(methodToCall, socket, params, callback);
|
||||
|
||||
@@ -339,8 +339,11 @@ SocketPosts.getPrivileges = function(socket, pids, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
SocketPosts.getUpvoters = function(socket, pid, callback) {
|
||||
favourites.getUpvotedUidsByPids([pid], function(err, data) {
|
||||
SocketPosts.getUpvoters = function(socket, pids, callback) {
|
||||
if (!Array.isArray(pids)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
favourites.getUpvotedUidsByPids(pids, function(err, data) {
|
||||
if (err || !Array.isArray(data) || !data.length) {
|
||||
return callback(err, []);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ var db = require('./database'),
|
||||
schemaDate, thisSchemaDate,
|
||||
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
|
||||
latestSchema = Date.UTC(2015, 0, 15);
|
||||
latestSchema = Date.UTC(2015, 0, 19);
|
||||
|
||||
Upgrade.check = function(callback) {
|
||||
db.get('schemaDate', function(err, value) {
|
||||
@@ -72,6 +72,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 9, 31);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/10/31] Applying newbiePostDelay values');
|
||||
|
||||
async.series([
|
||||
@@ -93,6 +94,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 10, 6, 18, 30);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/11/6] Updating topic authorship sorted set');
|
||||
|
||||
async.waterfall([
|
||||
@@ -138,6 +140,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 10, 7);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/11/7] Renaming sorted set names');
|
||||
|
||||
async.waterfall([
|
||||
@@ -199,6 +202,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 10, 11);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/11/11] Upgrading permissions');
|
||||
|
||||
async.waterfall([
|
||||
@@ -272,6 +276,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 10, 17, 13);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/11/17] Updating user email digest settings');
|
||||
|
||||
async.waterfall([
|
||||
@@ -306,6 +311,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 10, 29, 22);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/11/29] Updating config.json to new format');
|
||||
var configPath = path.join(__dirname, '../config.json');
|
||||
|
||||
@@ -358,6 +364,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 11, 2);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/12/2] Removing register user fields');
|
||||
|
||||
db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) {
|
||||
@@ -393,6 +400,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 11, 12);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/12/12] Updating teasers');
|
||||
|
||||
db.getSortedSetRange('topics:tid', 0, -1, function(err, tids) {
|
||||
@@ -419,6 +427,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2014, 11, 20);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2014/12/20] Updating digest settings');
|
||||
|
||||
async.waterfall([
|
||||
@@ -455,6 +464,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2015, 0, 8);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2015/01/08] Updating category topics sorted sets');
|
||||
|
||||
db.getSortedSetRange('topics:tid', 0, -1, function(err, tids) {
|
||||
@@ -494,6 +504,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2015, 0, 9);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2015/01/09] Creating fullname:uid hash');
|
||||
|
||||
db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) {
|
||||
@@ -529,6 +540,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2015, 0, 13);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2015/01/13] Creating uid:followed_tids sorted set');
|
||||
|
||||
db.getSortedSetRange('topics:tid', 0, -1, function(err, tids) {
|
||||
@@ -570,6 +582,7 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2015, 0, 14);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2015/01/14] Upgrading follow sets to sorted sets');
|
||||
|
||||
db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) {
|
||||
@@ -634,11 +647,12 @@ Upgrade.upgrade = function(callback) {
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2015, 0, 15);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2015/01/15] Creating topiccount for users');
|
||||
|
||||
db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) {
|
||||
if (err) {
|
||||
winston.error('[2014/01/15] Error encountered while Creating topiccount for users');
|
||||
winston.error('[2015/01/15] Error encountered while Creating topiccount for users');
|
||||
return next(err);
|
||||
}
|
||||
|
||||
@@ -668,6 +682,34 @@ Upgrade.upgrade = function(callback) {
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2015, 0, 19);
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2015/01/19] Generating group slugs');
|
||||
|
||||
Groups.list({}, function(err, groups) {
|
||||
var tasks = [];
|
||||
groups.forEach(function(groupObj) {
|
||||
tasks.push(async.apply(db.setObjectField, 'group:' + groupObj.name, 'slug', Utils.slugify(groupObj.name)));
|
||||
tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', Utils.slugify(groupObj.name), groupObj.name));
|
||||
});
|
||||
|
||||
async.parallel(tasks, function(err) {
|
||||
if (err) {
|
||||
winston.error('[2015/01/19] Error encountered while Generating group slugs');
|
||||
return next(err);
|
||||
}
|
||||
|
||||
winston.info('[2015/01/19] Generating group slugs done');
|
||||
Upgrade.update(thisSchemaDate, next);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
winston.info('[2015/01/19] Generating group slugs skipped');
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
// Add new schema updates here
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 22!!!
|
||||
|
||||
@@ -79,11 +79,14 @@ module.exports = function(User) {
|
||||
'uid:' + uid + ':favourites', 'uid:' + uid + ':followed_tids', 'user:' + uid + ':settings',
|
||||
'uid:' + uid + ':topics', 'uid:' + uid + ':posts',
|
||||
'uid:' + uid + ':chats', 'uid:' + uid + ':chats:unread',
|
||||
'uid:' + uid + ':ip', 'uid:' + uid + ':upvote', 'uid:' + uid + ':downvote',
|
||||
'uid:' + uid + ':upvote', 'uid:' + uid + ':downvote',
|
||||
'uid:' + uid + ':ignored:cids'
|
||||
];
|
||||
db.deleteAll(keys, next);
|
||||
},
|
||||
function(next) {
|
||||
deleteUserIps(uids, next);
|
||||
},
|
||||
function(next) {
|
||||
deleteUserFromFollowers(uid, next);
|
||||
},
|
||||
@@ -110,6 +113,23 @@ module.exports = function(User) {
|
||||
});
|
||||
};
|
||||
|
||||
function deleteUserIps(uid, callback) {
|
||||
db.getSortedSetRange('uid:' + uid + ':ip', 0, -1, function(err, ips) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.each(ips, function(ip, next) {
|
||||
db.sortedSetRemove('ip:' + ip + ':uid', uid, next);
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
db.delete('uid:' + uid + ':ip', callback);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function deleteUserFromFollowers(uid, callback) {
|
||||
db.getSetMembers('followers:' + uid, function(err, uids) {
|
||||
if (err) {
|
||||
|
||||
@@ -57,6 +57,9 @@ module.exports = function(User) {
|
||||
}
|
||||
|
||||
function isUsernameAvailable(next) {
|
||||
if (!data.username) {
|
||||
return next();
|
||||
}
|
||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||
|
||||
var userslug = utils.slugify(data.username);
|
||||
@@ -96,7 +99,7 @@ module.exports = function(User) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
plugins.fireHook('action:user.updateProfile', {data: data, uid: uid});
|
||||
User.getUserFields(uid, ['email', 'userslug', 'picture', 'gravatarpicture'], callback);
|
||||
});
|
||||
});
|
||||
@@ -177,6 +180,9 @@ module.exports = function(User) {
|
||||
}
|
||||
|
||||
function updateUsername(uid, newUsername, callback) {
|
||||
if (!newUsername) {
|
||||
return callback();
|
||||
}
|
||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||
function update(field, object, value, callback) {
|
||||
async.parallel([
|
||||
|
||||
@@ -18,7 +18,7 @@ module.exports = function(User) {
|
||||
return callback(null, {timing: 0, users: [], matchCount: 0, pages: []});
|
||||
}
|
||||
|
||||
if (searchBy === 'ip') {
|
||||
if (searchBy.indexOf('ip') !== -1) {
|
||||
return searchByIP(query, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ describe('Hash methods', function() {
|
||||
});
|
||||
|
||||
it('should return false if field does not exist', function(done) {
|
||||
db.isObjectField('testObject1', 'field1', function(err, value) {
|
||||
db.isObjectField('hashTestObject', 'field1', function(err, value) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(arguments.length, 2);
|
||||
assert.equal(value, false);
|
||||
@@ -240,6 +240,27 @@ describe('Hash methods', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('isObjectFields()', function() {
|
||||
it('should return an array of false if object does not exist', function(done) {
|
||||
db.isObjectFields('doesnotexist', ['field1', 'field2'], function(err, values) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(arguments.length, 2);
|
||||
assert.deepEqual(values, [false, false]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if field does not exist', function(done) {
|
||||
db.isObjectFields('hashTestObject', ['name', 'age', 'field1'], function(err, values) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(arguments.length, 2);
|
||||
assert.deepEqual(values, [true, true, false]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteObjectField()', function() {
|
||||
before(function(done) {
|
||||
db.setObject('testObject10', {foo: 'bar', delete: 'this'}, done);
|
||||
|
||||
@@ -212,20 +212,36 @@ describe('Groups', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should rename a group if the name was updated', function(done) {
|
||||
Groups.update('foo', {
|
||||
name: 'foobar?'
|
||||
}, function(err) {
|
||||
if (err) return done(err);
|
||||
|
||||
Groups.get('foobar?', {}, function(err, groupObj) {
|
||||
if (err) return done(err);
|
||||
|
||||
assert.strictEqual('foobar?', groupObj.name);
|
||||
assert.strictEqual('foobar', groupObj.slug);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.destroy()', function() {
|
||||
before(function(done) {
|
||||
Groups.join('foo', 1, done);
|
||||
Groups.join('foobar?', 1, done);
|
||||
});
|
||||
|
||||
it('should destroy a group', function(done) {
|
||||
Groups.destroy('foo', function(err) {
|
||||
Groups.destroy('foobar?', function(err) {
|
||||
if (err) return done(err);
|
||||
|
||||
Groups.get('foo', {}, function(err, groupObj) {
|
||||
if (err) return done(err);
|
||||
assert.strictEqual(undefined, groupObj);
|
||||
Groups.get('foobar?', {}, function(err) {
|
||||
assert(err, 'Group still exists!');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user