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

v1.18.x
barisusakli 10 years ago
commit ea3d7d610a

@ -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,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;");
},
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);
async.each(fields, function(key, next) {
updateCategoryField(cid, key, category[key], next);
}, next);
Categories.exists(cid, function(err, exists) {
if (err || !exists) {
return next(err);
}
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();
});

Loading…
Cancel
Save