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

This commit is contained in:
Julian Lam
2017-03-15 10:01:00 -04:00
24 changed files with 279 additions and 214 deletions

View File

@@ -223,7 +223,7 @@ fs.open(path.join(__dirname, 'config.json'), 'r', function (err) {
stderr: process.stderr,
});
fs.writeFile(pidFilePath, process.pid);
fs.writeFileSync(pidFilePath, process.pid);
}
async.series([

View File

@@ -61,7 +61,7 @@
"nodebb-plugin-soundpack-default": "1.0.0",
"nodebb-plugin-spam-be-gone": "0.4.13",
"nodebb-rewards-essentials": "0.0.9",
"nodebb-theme-lavender": "3.0.15",
"nodebb-theme-lavender": "4.0.0",
"nodebb-theme-persona": "4.2.6",
"nodebb-theme-vanilla": "5.2.0",
"nodebb-widget-essentials": "2.0.13",

View File

@@ -8,27 +8,27 @@
"mongo.version": "Versione MongoDB",
"mongo.storage-engine": "Storage Engine",
"mongo.collections": "Collections",
"mongo.objects": "Objects",
"mongo.avg-object-size": "Avg. Object Size",
"mongo.data-size": "Data Size",
"mongo.storage-size": "Storage Size",
"mongo.index-size": "Index Size",
"mongo.file-size": "File Size",
"mongo.resident-memory": "Resident Memory",
"mongo.virtual-memory": "Virtual Memory",
"mongo.mapped-memory": "Mapped Memory",
"mongo.objects": "Oggetti",
"mongo.avg-object-size": "Dimensione Media dell'Oggetto",
"mongo.data-size": "Dimensione del Data",
"mongo.storage-size": "Dimensione dello Spazio di Archiviazione",
"mongo.index-size": "Dimensione dell'Indice",
"mongo.file-size": "Dimensione del file",
"mongo.resident-memory": "Memoria Allocata",
"mongo.virtual-memory": "Memoria Virtuale",
"mongo.mapped-memory": "Memoria Mappata",
"mongo.raw-info": "MongoDB Raw Info",
"redis": "Redis",
"redis.version": "Redis Version",
"redis.connected-clients": "Connected Clients",
"redis.version": "Versione Redis",
"redis.connected-clients": "Clients Connessi",
"redis.connected-slaves": "Connected Slaves",
"redis.blocked-clients": "Blocked Clients",
"redis.used-memory": "Used Memory",
"redis.memory-frag-ratio": "Memory Fragmentation Ratio",
"redis.total-connections-recieved": "Total Connections Received",
"redis.total-commands-processed": "Total Commands Processed",
"redis.iops": "Instantaneous Ops. Per Second",
"redis.blocked-clients": "Clients Bloccati",
"redis.used-memory": "Memoria Usata",
"redis.memory-frag-ratio": "Rateo della Frammentazione della Memoria",
"redis.total-connections-recieved": "Totale Connessioni Ricevute",
"redis.total-commands-processed": "Totale Comandi Processati",
"redis.iops": "Operazioni Instantanee al Secondo",
"redis.keyspace-hits": "Keyspace Hits",
"redis.keyspace-misses": "Keyspace Misses",
"redis.raw-info": "Redis Raw Info"

View File

@@ -1,14 +1,14 @@
{
"figure-x": "Figure %1",
"error-events-per-day": "<code>%1</code> events per day",
"error.404": "404 Not Found",
"error.503": "503 Service Unavailable",
"manage-error-log": "Manage Error Log",
"export-error-log": "Export Error Log (CSV)",
"clear-error-log": "Clear Error Log",
"route": "Route",
"count": "Count",
"no-routes-not-found": "Hooray! No 404 errors!",
"clear404-confirm": "Are you sure you wish to clear the 404 error logs?",
"clear404-success": "\"404 Not Found\" errors cleared"
"figure-x": "Figura %1",
"error-events-per-day": "<code>%1</code> eventi per giorno",
"error.404": "404 Non Trovato",
"error.503": "503 Servizio Non Disponibile",
"manage-error-log": "Gestisci il Registro degli Errori",
"export-error-log": "Esporta il Registro degli Errori (CSV)",
"clear-error-log": "Cancella il Registro degli Errori",
"route": "Strada",
"count": "Numero",
"no-routes-not-found": "Hooray! Nessun Errore 404!",
"clear404-confirm": "Sei sicuro di voler cancellare il Registro degli Errori 404?",
"clear404-success": "Error \"404 Non Trovato\" Cancellati"
}

View File

@@ -1,6 +1,6 @@
{
"events": "Events",
"no-events": "There are no events",
"control-panel": "Events Control Panel",
"delete-events": "Delete Events"
"events": "Eventi",
"no-events": "Non ci sono Eventi",
"control-panel": "Pannello di controllo degli Eventi",
"delete-events": "Cancella gli Eventi"
}

View File

@@ -1,7 +1,7 @@
{
"logs": "Logs",
"control-panel": "Logs Control Panel",
"reload": "Reload Logs",
"clear": "Clear Logs",
"clear-success": "Logs Cleared!"
"logs": "Registri",
"control-panel": "Pannello di Controllo dei Registri",
"reload": "Ricarica i Registri",
"clear": "Cancella i Registri",
"clear-success": "Registri Cancellati!"
}

View File

@@ -1,9 +1,9 @@
{
"custom-css": "Custom CSS",
"custom-css.description": "Enter your own CSS declarations here, which will be applied after all other styles.",
"custom-css.enable": "Enable Custom CSS",
"custom-css": "CSS Personalizzato",
"custom-css.description": "Inserisci le tue dichiarazioni CSS qui, verranno applicate dopo tutti gli altri stili.",
"custom-css.enable": "Abilita CSS Personalizzato",
"custom-header": "Custom Header",
"custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <code>&lt;head&gt;</code> section of your forum's markup.",
"custom-header.enable": "Enable Custom Header"
"custom-header": "Intestazione Personalizzata",
"custom-header.description": "Inserisci l' HTML personalizzato qui (es. JavaScript, Meta Tags, ecc.), verrà attaccato al codice <code>&lt;head&gt;</code> sezione del markup del tuo forum",
"custom-header.enable": "Abilita l'Intestazione Personalizzata"
}

View File

@@ -1,9 +1,9 @@
{
"loading": "Loading Skins...",
"homepage": "Homepage",
"select-skin": "Select Skin",
"current-skin": "Current Skin",
"skin-updated": "Skin Updated",
"applied-success": "%1 skin was succesfully applied",
"revert-success": "Skin reverted to base colours"
"loading": "Caricamento Skins",
"homepage": "Pagina Home",
"select-skin": "Seleziona la Skin",
"current-skin": "Skin Corrente",
"skin-updated": "Skin Aggiornata",
"applied-success": "%1 skin è stata applicata con successo",
"revert-success": "Skin riportata ai colori base"
}

View File

@@ -1,11 +1,11 @@
{
"checking-for-installed": "Checking for installed themes...",
"homepage": "Homepage",
"select-theme": "Select Theme",
"current-theme": "Current Theme",
"no-themes": "No installed themes found",
"revert-confirm": "Are you sure you wish to restore the default NodeBB theme?",
"theme-changed": "Theme Changed",
"revert-success": "You have successfully reverted your NodeBB back to it's default theme.",
"restart-to-activate": "Please restart your NodeBB to fully activate this theme"
"checking-for-installed": "Controllando se ci sono temi installati...",
"homepage": "Pagina Home",
"select-theme": "Seleziona il Tema",
"current-theme": "Tema Corrente",
"no-themes": "Nessun tema installato trovato",
"revert-confirm": "Sei sicuro di voler ripristinare al tema originale di NodeBB?",
"theme-changed": "Tema Cambiato",
"revert-success": "Hai correttamente ripristinato il tuo NodeBB al tema originale.",
"restart-to-activate": "Perfavore riavvia il tuo NodeBB per attivare correttamente questo tema"
}

View File

@@ -1,16 +1,16 @@
{
"you-are-on": "Info - You are on <strong>%1:%2</strong>",
"you-are-on": "Informazione - Tu sei su <strong>%1:%2</strong>",
"host": "host",
"pid": "pid",
"nodejs": "nodejs",
"online": "online",
"git": "git",
"load": "load",
"uptime": "uptime",
"load": "carica",
"uptime": "tempo di caricamento",
"registered": "Registered",
"registered": "Registrato",
"sockets": "Sockets",
"guests": "Guests",
"guests": "Ospiti",
"info": "Info"
"info": "Informazioni"
}

View File

@@ -1,6 +1,6 @@
{
"logger-settings": "Logger Settings",
"description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.",
"logger-settings": "Impostazioni del Registratore",
"description": "Abilitando le \"check boxes\", riceverai i registri sul tuo terminale. Se vuoi specificare un percorso, i registri verranno invece salvati in un file. Registrare l' HTTP è utile per collezionare statistiche su chi, quando, e a cosa le persone hanno accesso sul tuo forum. In più sul registrare le richieste HTTP, in combinazione con il monitoraggio redis-cli, può essere veramente utile per imparare l'interno di NodeBB",
"explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.",
"enable-http": "Enable HTTP logging",
"enable-socket": "Enable socket.io event logging",

View File

@@ -15,7 +15,7 @@
"invalid-username-or-password": "Молимо наведите и корисничко име и лозинку",
"invalid-search-term": "Неисправан упит за претрагу",
"csrf-invalid": "Нисмо успели да вас пријавимо, вероватно због истека сесије. Молимо покушајте поново",
"invalid-pagination-value": "Неважећа вредност при обележавању страна, мора бити најмање %1 а највише %2 ",
"invalid-pagination-value": "Неважећа вредност приликом нумерисања страница, мора бити најмање %1 а највише %2 ",
"username-taken": "Корисничко име је заузето",
"email-taken": "Адреса е-поште је заузета",
"email-not-confirmed": "Ваша адреса е-поште још увек није оверена, кликните овде да би сте то учинили.",

View File

@@ -21,7 +21,7 @@
"save_changes": "Сачувај измене",
"save": "Сачувај",
"close": "Затвори",
"pagination": "Обележавање страна",
"pagination": "Нумерисање страница",
"pagination.out_of": "%1 од %2",
"pagination.enter_index": "Унесите индекс",
"header.admin": "Админ",

View File

@@ -13,7 +13,7 @@
"notify_me": "Будите обавештени о новим порукама у овој теми",
"quote": "Цитирај",
"reply": "Одговори",
"replies_to_this_post": "%1 одговора",
"replies_to_this_post": "Одговора: %1",
"last_reply_time": "Последњи одговор",
"reply-as-topic": "Постави одговор као тему",
"guest-login-reply": "Пријавите се да бисте одговорили",

View File

@@ -90,7 +90,7 @@
"has_no_voted_posts": "Овај корисник нема објаве за које се гласало.",
"email_hidden": "Скривена е-пошта",
"hidden": "скривена",
"paginate_description": "Подели теме и поруке по страницама уместо бесконачног скроловања",
"paginate_description": "Нумериши теме и странице уместо бесконачног скроловања",
"topics_per_page": "Тема по страници",
"posts_per_page": "Порука по страници",
"notification_sounds": "Репродукуј звук приликом примања обавештења",

View File

@@ -1,9 +1,9 @@
{
"chat-settings": "Sohbet Ayarları",
"disable": "Sohbeti kapat",
"disable-editing": "Disable chat message editing/deletion",
"disable-editing": "Sohbet mesajlarını düzenlemeyi/silmeyi kapat",
"disable-editing-help": "Administrators and global moderators are exempt from this restriction",
"max-length": "Maximum length of chat messages",
"max-length": "Maksimum sohbet mesajı uzunluğu",
"max-room-size": "Maximum number of users in chat rooms",
"delay": "Time between chat messages in milliseconds"
}

View File

@@ -81,7 +81,10 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s
$('[data-toggle="tooltip"]').tooltip();
setupRealtimeButton();
setupGraphs();
setupGraphs(function () {
socket.emit('admin.rooms.getAll', Admin.updateRoomUsage);
initiateDashboard();
});
};
Admin.updateRoomUsage = function (err, data) {
@@ -159,7 +162,8 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s
}
/* eslint-enable */
function setupGraphs() {
function setupGraphs(callback) {
callback = callback || function () {};
var trafficCanvas = document.getElementById('analytics-traffic');
var registeredCanvas = document.getElementById('analytics-registered');
var presenceCanvas = document.getElementById('analytics-presence');
@@ -303,8 +307,12 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s
$(this).addClass('active');
});
<<<<<<< HEAD
socket.emit('admin.rooms.getAll', Admin.updateRoomUsage);
initiateDashboard();
=======
callback();
>>>>>>> origin/master
});
}

View File

@@ -73,7 +73,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
if (!selected.length) {
selected = menu.find('li.result > a').first().attr('href');
}
var href = selected || config.relative_path + '/search/' + input.val();
var href = selected || config.relative_path + '/search?in=titlesposts&term=' + input.val();
ajaxify.go(href.replace(/^\//, ''));
@@ -140,7 +140,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
menu.find('.search-forum')
.not('.divider')
.find('a')
.attr('href', config.relative_path + '/search/' + value)
.attr('href', config.relative_path + '/search?in=titlesposts&term=' + value)
.find('strong')
.html(value);
} else {

View File

@@ -79,8 +79,8 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu
onPasswordConfirmChanged();
return app.alertError(err.message);
}
ajaxify.go('user/' + ajaxify.data.userslug);
app.alertSuccess('[[user:change_password_success]]');
window.location.href = config.relative_path + '/login';
});
} else {
if (!passwordsmatch) {

View File

@@ -82,7 +82,7 @@ module.exports = function (middleware) {
picture: meta.config.defaultAvatar,
status: 'offline',
reputation: 0,
'email:confirmed': false,
'email:confirmed': 0,
};
if (req.uid) {
user.getUserFields(req.uid, Object.keys(userData), next);

View File

@@ -11,85 +11,90 @@ var batch = require('../batch');
module.exports = function (Topics) {
Topics.delete = function (tid, uid, callback) {
Topics.getTopicFields(tid, ['cid'], function (err, topicData) {
if (err) {
return callback(err);
}
async.parallel([
function (next) {
Topics.setTopicFields(tid, {
deleted: 1,
deleterUid: uid,
deletedTimestamp: Date.now(),
}, next);
},
function (next) {
db.sortedSetsRemove(['topics:recent', 'topics:posts', 'topics:views'], tid, next);
},
function (next) {
Topics.getPids(tid, function (err, pids) {
if (err) {
return next(err);
}
db.sortedSetRemove('cid:' + topicData.cid + ':pids', pids, next);
});
},
], function (err) {
callback(err);
});
async.parallel([
function (next) {
Topics.setTopicFields(tid, {
deleted: 1,
deleterUid: uid,
deletedTimestamp: Date.now(),
}, next);
},
function (next) {
db.sortedSetsRemove(['topics:recent', 'topics:posts', 'topics:views'], tid, next);
},
function (next) {
async.waterfall([
function (next) {
async.parallel({
cid: function (next) {
Topics.getTopicField(tid, 'cid', next);
},
pids: function (next) {
Topics.getPids(tid, next);
},
}, next);
},
function (results, next) {
db.sortedSetRemove('cid:' + results.cid + ':pids', results.pids, next);
},
], next);
},
], function (err) {
callback(err);
});
};
Topics.restore = function (tid, uid, callback) {
Topics.getTopicFields(tid, ['cid', 'lastposttime', 'postcount', 'viewcount'], function (err, topicData) {
if (err) {
return callback(err);
}
async.parallel([
function (next) {
Topics.setTopicField(tid, 'deleted', 0, next);
},
function (next) {
Topics.deleteTopicFields(tid, ['deleterUid', 'deletedTimestamp'], next);
},
function (next) {
Topics.updateRecent(tid, topicData.lastposttime, next);
},
function (next) {
db.sortedSetAdd('topics:posts', topicData.postcount, tid, next);
},
function (next) {
db.sortedSetAdd('topics:views', topicData.viewcount, tid, next);
},
function (next) {
Topics.getPids(tid, function (err, pids) {
if (err) {
return callback(err);
}
posts.getPostsFields(pids, ['pid', 'timestamp', 'deleted'], function (err, postData) {
if (err) {
return next(err);
}
postData = postData.filter(function (post) {
return post && parseInt(post.deleted, 10) !== 1;
});
var pidsToAdd = [];
var scores = [];
postData.forEach(function (post) {
pidsToAdd.push(post.pid);
scores.push(post.timestamp);
});
db.sortedSetAdd('cid:' + topicData.cid + ':pids', scores, pidsToAdd, next);
});
});
},
], function (err) {
callback(err);
});
});
var topicData;
async.waterfall([
function (next) {
Topics.getTopicFields(tid, ['cid', 'lastposttime', 'postcount', 'viewcount'], next);
},
function (_topicData, next) {
topicData = _topicData;
async.parallel([
function (next) {
Topics.setTopicField(tid, 'deleted', 0, next);
},
function (next) {
Topics.deleteTopicFields(tid, ['deleterUid', 'deletedTimestamp'], next);
},
function (next) {
Topics.updateRecent(tid, topicData.lastposttime, next);
},
function (next) {
db.sortedSetAdd('topics:posts', topicData.postcount, tid, next);
},
function (next) {
db.sortedSetAdd('topics:views', topicData.viewcount, tid, next);
},
function (next) {
async.waterfall([
function (next) {
Topics.getPids(tid, next);
},
function (pids, next) {
posts.getPostsFields(pids, ['pid', 'timestamp', 'deleted'], next);
},
function (postData, next) {
postData = postData.filter(function (post) {
return post && parseInt(post.deleted, 10) !== 1;
});
var pidsToAdd = [];
var scores = [];
postData.forEach(function (post) {
pidsToAdd.push(post.pid);
scores.push(post.timestamp);
});
db.sortedSetAdd('cid:' + topicData.cid + ':pids', scores, pidsToAdd, next);
},
], next);
},
], function (err) {
next(err);
});
},
], callback);
};
Topics.purgePostsAndTopic = function (tid, uid, callback) {
@@ -179,24 +184,28 @@ module.exports = function (Topics) {
}
function deleteTopicFromCategoryAndUser(tid, callback) {
Topics.getTopicFields(tid, ['cid', 'uid'], function (err, topicData) {
if (err) {
return callback(err);
}
async.parallel([
function (next) {
db.sortedSetsRemove([
'cid:' + topicData.cid + ':tids',
'cid:' + topicData.cid + ':tids:pinned',
'cid:' + topicData.cid + ':tids:posts',
'cid:' + topicData.cid + ':uid:' + topicData.uid + ':tids',
'uid:' + topicData.uid + ':topics',
], tid, next);
},
function (next) {
user.decrementUserFieldBy(topicData.uid, 'topiccount', 1, next);
},
], callback);
async.waterfall([
function (next) {
Topics.getTopicFields(tid, ['cid', 'uid'], next);
},
function (topicData, next) {
async.parallel([
function (next) {
db.sortedSetsRemove([
'cid:' + topicData.cid + ':tids',
'cid:' + topicData.cid + ':tids:pinned',
'cid:' + topicData.cid + ':tids:posts',
'cid:' + topicData.cid + ':uid:' + topicData.uid + ':tids',
'uid:' + topicData.uid + ':topics',
], tid, next);
},
function (next) {
user.decrementUserFieldBy(topicData.uid, 'topiccount', 1, next);
},
], next);
},
], function (err) {
callback(err);
});
}
@@ -207,26 +216,28 @@ module.exports = function (Topics) {
db.incrObjectFieldBy('global', 'topicCount', incr, next);
},
function (next) {
Topics.getTopicFields(tid, ['cid', 'postcount'], function (err, topicData) {
if (err) {
return next(err);
}
topicData.postcount = parseInt(topicData.postcount, 10);
topicData.postcount = topicData.postcount || 0;
var postCountChange = incr * topicData.postcount;
async.waterfall([
function (next) {
Topics.getTopicFields(tid, ['cid', 'postcount'], next);
},
function (topicData, next) {
topicData.postcount = parseInt(topicData.postcount, 10);
topicData.postcount = topicData.postcount || 0;
var postCountChange = incr * topicData.postcount;
async.parallel([
function (next) {
db.incrObjectFieldBy('global', 'postCount', postCountChange, next);
},
function (next) {
db.incrObjectFieldBy('category:' + topicData.cid, 'post_count', postCountChange, next);
},
function (next) {
db.incrObjectFieldBy('category:' + topicData.cid, 'topic_count', incr, next);
},
], next);
});
async.parallel([
function (next) {
db.incrObjectFieldBy('global', 'postCount', postCountChange, next);
},
function (next) {
db.incrObjectFieldBy('category:' + topicData.cid, 'post_count', postCountChange, next);
},
function (next) {
db.incrObjectFieldBy('category:' + topicData.cid, 'topic_count', incr, next);
},
], next);
},
], next);
},
], callback);
}

View File

@@ -83,16 +83,21 @@ var emailer = require('../emailer');
}
});
},
function (next) {
next(null, confirm_code);
},
], callback);
};
UserEmail.confirm = function (code, callback) {
db.getObject('confirm:' + code, function (err, confirmObj) {
if (err) {
return callback(new Error('[[error:parse-error]]'));
}
if (confirmObj && confirmObj.uid && confirmObj.email) {
async.waterfall([
function (next) {
db.getObject('confirm:' + code, next);
},
function (confirmObj, next) {
if (!confirmObj || !confirmObj.uid || !confirmObj.email) {
return next(new Error('[[error:invalid-data]]'));
}
async.series([
async.apply(user.setUserField, confirmObj.uid, 'email:confirmed', 1),
async.apply(db.delete, 'confirm:' + code),
@@ -103,12 +108,10 @@ var emailer = require('../emailer');
function (next) {
plugins.fireHook('action:user.email.confirmed', { uid: confirmObj.uid, email: confirmObj.email }, next);
},
], function (err) {
callback(err ? new Error('[[error:email-confirm-failed]]') : null);
});
} else {
callback(new Error('[[error:invalid-data]]'));
}
], next);
},
], function (err) {
callback(err);
});
};
}(exports));

View File

@@ -279,6 +279,7 @@ module.exports = function (User) {
async.parallel([
async.apply(User.setUserField, data.uid, 'password', hashedPassword),
async.apply(User.reset.updateExpiry, data.uid),
async.apply(User.auth.revokeAllSessions, data.uid),
], function (err) {
next(err);
});

View File

@@ -399,19 +399,22 @@ describe('User', function () {
});
it('should change a user\'s password', function (done) {
this.timeout(20000);
io.emit('user.changePassword', { uid: uid, newPassword: '654321', currentPassword: '123456' }, function (err) {
var socketUser = require('../src/socket.io/user');
User.create({ username: 'changepassword', password: '123456' }, function (err, uid) {
assert.ifError(err);
User.isPasswordCorrect(uid, '654321', function (err, correct) {
socketUser.changePassword({ uid: uid }, { uid: uid, newPassword: '654321', currentPassword: '123456' }, function (err) {
assert.ifError(err);
assert(correct);
done();
User.isPasswordCorrect(uid, '654321', function (err, correct) {
assert.ifError(err);
assert(correct);
done();
});
});
});
});
it('should change username', function (done) {
io.emit('user.changeUsernameEmail', { uid: uid, username: 'updatedAgain', password: '654321' }, function (err) {
io.emit('user.changeUsernameEmail', { uid: uid, username: 'updatedAgain', password: '123456' }, function (err) {
assert.ifError(err);
db.getObjectField('user:' + uid, 'username', function (err, username) {
assert.ifError(err);
@@ -422,7 +425,7 @@ describe('User', function () {
});
it('should change email', function (done) {
io.emit('user.changeUsernameEmail', { uid: uid, email: 'updatedAgain@me.com', password: '654321' }, function (err) {
io.emit('user.changeUsernameEmail', { uid: uid, email: 'updatedAgain@me.com', password: '123456' }, function (err) {
assert.ifError(err);
db.getObjectField('user:' + uid, 'email', function (err, email) {
assert.ifError(err);
@@ -1152,6 +1155,45 @@ describe('User', function () {
});
});
describe('email confirm', function () {
it('should error with invalid code', function (done) {
User.email.confirm('asdasda', function (err) {
assert.equal(err.message, '[[error:invalid-data]]');
done();
});
});
it('should confirm email of user', function (done) {
var email = 'confirm@me.com';
User.create({
username: 'confirme',
email: email,
}, function (err, uid) {
assert.ifError(err);
User.email.sendValidationEmail(uid, email, function (err, code) {
assert.ifError(err);
User.email.confirm(code, function (err) {
assert.ifError(err);
async.parallel({
confirmed: function (next) {
db.getObjectField('user:' + uid, 'email:confirmed', next);
},
isMember: function (next) {
db.isSortedSetMember('users:notvalidated', uid, next);
},
}, function (err, results) {
assert.ifError(err);
assert.equal(results.confirmed, 1);
assert.equal(results.isMember, false);
done();
});
});
});
});
});
});
after(function (done) {
db.emptydb(done);