From a6c75eea88973c4d1bef818e46e4cbcf9c6b2551 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:07:06 -0500 Subject: [PATCH 01/31] hash tests --- tests/database.js | 97 +----------------------------------------- tests/database/hash.js | 81 +++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 96 deletions(-) create mode 100644 tests/database/hash.js diff --git a/tests/database.js b/tests/database.js index 45379fc9b3..6238f9fa13 100644 --- a/tests/database.js +++ b/tests/database.js @@ -13,102 +13,7 @@ describe('Test database', function() { require('./database/keys'); require('./database/list'); require('./database/sets'); - - it('should not throw err', function(done) { - var objectKey = 'testObj'; - - function setObject(callback) { - db.setObject(objectKey, {name:'baris', 'lastname':'usakli', age:3}, function(err, result) { - callback(err, {'setObject':result}); - }); - } - - function getObject(callback) { - db.getObject(objectKey, function(err, data) { - callback(err, {'getObject':data}); - }); - } - - function getObjects(callback) { - db.getObjects(['testing1', objectKey, 'doesntexist', 'user:1'], function(err, data) { - callback(err, {'getObjects':data}); - }); - } - - function setObjectField(callback) { - db.setObjectField(objectKey, 'reputation', 5, function(err, result) { - callback(err, {'setObjectField': result}); - }); - } - - function getObjectField(callback) { - db.getObjectField(objectKey, 'age', function(err, age) { - callback(err, {'getObjectField' : age}); - }); - } - - function getObjectFields(callback) { - db.getObjectFields(objectKey, ['name', 'lastname'], function(err, data) { - callback(err, {'getObjectFields':data}); - }); - } - - function getObjectValues(callback) { - db.getObjectValues(objectKey, function(err, data) { - callback(err, {'getObjectValues':data}); - }); - } - - function isObjectField(callback) { - db.isObjectField(objectKey, 'age', function(err, data) { - callback(err, {'isObjectField':data}); - }); - } - - function deleteObjectField(callback) { - db.deleteObjectField(objectKey, 'reputation', function(err, data) { - callback(err, {'deleteObjectField':data}); - }); - } - - function incrObjectFieldBy(callback) { - db.incrObjectFieldBy(objectKey, 'age', 3, function(err, data) { - callback(err, {'incrObjectFieldBy':data}); - }); - } - - function getObjectKeys(callback) { - db.getObjectKeys(objectKey, function(err, data) { - callback(err, {'getObjectKeys':data}); - }); - } - - var objectTasks = [ - setObject, - getObject, - deleteObjectField, - getObject, - setObjectField, - getObject, - deleteObjectField, - getObject, - getObjectField, - getObjectFields, - getObjectValues, - isObjectField, - incrObjectFieldBy, - getObject, - getObjects, - getObjectKeys - ]; - - async.series(objectTasks, function(err, results) { - assert.equal(err, null, 'error in object methods'); - assert.ok(results); - - done(); - }); - }); + require('./database/hash'); it('should not throw err', function(done) { diff --git a/tests/database/hash.js b/tests/database/hash.js new file mode 100644 index 0000000000..114b982f38 --- /dev/null +++ b/tests/database/hash.js @@ -0,0 +1,81 @@ +'use strict'; + +var async = require('async'), + assert = require('assert'), + db = require('../mocks/databasemock'); + +describe('Hash methods', function() { + var testData = { + name: 'baris', + age: 99 + }; + + describe('setObject()', function() { + it('should create a object', function(done) { + db.setObject('testObject1', testData, function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('setObjectField()', function() { + it('should add a new field to an object', function(done) { + db.setObjectField('testObject1', 'lastname', 'usakli', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + + it('should create a new object with field', function(done) { + db.setObjectField('testObject2', 'name', 'ginger', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('getObject()', function() { + it('should return falsy if object does not exist', function(done) { + db.getObject('doesnotexist', function(err, data) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!data, false); + done(); + }); + }); + + it('should retrieve an object', function(done) { + db.getObject('testObject1', function(err, data) { + assert.equal(err, null); + assert.equal(data.name, testData.name); + assert.equal(data.age, testData.age); + assert.equal(data.lastname, 'usakli'); + done(); + }); + }); + }); + + describe('getObjects()', function() { + it('should return 3 objects with correct data', function(done) { + db.getObjects(['testObject1' 'testObject2', 'doesnotexist'], function(err, objects) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(objects) && objects.length === 3, true); + assert.equal(objects[0].name, 'baris'); + assert.equal(objects[1].name, 'ginger'); + assert.equal(!!objects[2], false); + done(); + }); + }); + }); + + + + after(function() { + db.flushdb(); + }); +}); From 81b32fc095e2bfc943261faf5a79f14dc351be9b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:08:16 -0500 Subject: [PATCH 02/31] missing comma --- tests/database/hash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database/hash.js b/tests/database/hash.js index 114b982f38..d5346bc61b 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -61,7 +61,7 @@ describe('Hash methods', function() { describe('getObjects()', function() { it('should return 3 objects with correct data', function(done) { - db.getObjects(['testObject1' 'testObject2', 'doesnotexist'], function(err, objects) { + db.getObjects(['testObject1', 'testObject2', 'doesnotexist'], function(err, objects) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(Array.isArray(objects) && objects.length === 3, true); From ebbb1bac26793d3878d45a0c70c43fb4492261f9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:25:32 -0500 Subject: [PATCH 03/31] more hash tests getObjectField getObjectFields getObjectsFields getObjectKeys --- tests/database/hash.js | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/tests/database/hash.js b/tests/database/hash.js index d5346bc61b..fe1a713cef 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -73,6 +73,99 @@ describe('Hash methods', function() { }); }); + describe('getObjectField()', function() { + it('should return falsy if object does not exist', function(done) { + db.getObjectField('doesnotexist', 'fieldName', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!value, false); + done(); + }); + }); + + it('should return falsy if field does not exist', function(done) { + db.getObjectField('testObject1', 'fieldName', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!value, false); + done(); + }); + }); + + it('should get an objects field', function(done) { + db.getObjectField('testObject1', 'lastname', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, 'usakli'); + done(); + }); + }); + }); + + describe('getObjectFields()', function() { + it('should return an object with falsy values', function(done) { + db.getObjectFields('doesnotexist', ['field1', 'field2'], function(err, object) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(typeof object, 'object'); + assert.equal(!!object.field1, false); + assert.equal(!!object.field2, false); + done(); + }); + }); + + it('should return an object with correct fields', function(done) { + db.getObjectFields('testObject1', ['lastname', 'age', 'field1'], function(err, object) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(typeof object, 'object'); + assert.equal(object.lastname, 'usakli'); + assert.equal(object.age, 99); + assert.equal(!!object.field1, false); + done(); + }); + }); + }); + + describe('getObjectsFields()', function() { + it('should return an array of objects with correct values', function(done) { + db.getObjectsFields(['testObject1', 'testObject2', 'doesnotexist'], ['name', 'age'], function(err, objects) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(objects), true); + assert.equal(objects.length, 3); + assert.equal(objects[0].name, 'baris'); + assert.equal(objects[0].age, 99); + assert.equal(objects[1].name, 'ginger'); + assert.equal(!!objects[2].name, false); + done(); + }); + }); + }); + + describe('getObjectKeys()', function() { + it('should return an empty array for a object that does not exist', function(done) { + db.getObjectKeys('doesnotexist', function(err, keys) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(keys) && keys.length === 0); + done(); + }); + }); + + it('should return an array of keys for the object\'s fields', function(done) { + db.getObjectKeys('testObject1', function(err, keys) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(keys) && keys.length === 3, true); + keys.forEach(function(key) { + assert.notEqual(['name', 'lastname', 'age'].indexOf(key), -1); + }); + + done(); + }); + }); + }); after(function() { From 13cd2e41bbcf4630d33563d97f82e0713552c802 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:29:11 -0500 Subject: [PATCH 04/31] fix assert --- tests/database/hash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database/hash.js b/tests/database/hash.js index fe1a713cef..18dbc1fbfb 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -148,7 +148,7 @@ describe('Hash methods', function() { db.getObjectKeys('doesnotexist', function(err, keys) { assert.equal(err, null); assert.equal(arguments.length, 2); - assert.equal(Array.isArray(keys) && keys.length === 0); + assert.equal(Array.isArray(keys) && keys.length === 0, true); done(); }); }); From 4d5ed784a100d65c7c7c7caef7d2e76a141d7cb9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:41:13 -0500 Subject: [PATCH 05/31] more hash tests getObjectValues isObjectField deleteObjectField incrObjectField --- src/database/mongo/hash.js | 4 +- src/database/redis/hash.js | 4 +- tests/database/hash.js | 87 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index c35df47952..6db3b5a4a9 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -182,7 +182,9 @@ module.exports = function(db, module) { var data = {}; field = helpers.fieldToString(field); data[field] = ''; - db.collection('objects').update({_key: key}, {$unset : data}, callback); + db.collection('objects').update({_key: key}, {$unset : data}, function(err, res) { + callback(err); + }); }; module.incrObjectField = function(key, field, callback) { diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js index 02df03c92e..ddc21b893c 100644 --- a/src/database/redis/hash.js +++ b/src/database/redis/hash.js @@ -89,7 +89,9 @@ module.exports = function(redisClient, module) { }; module.deleteObjectField = function(key, field, callback) { - redisClient.hdel(key, field, callback); + redisClient.hdel(key, field, function(err, res) { + callback(err); + }); }; module.incrObjectField = function(key, field, callback) { diff --git a/tests/database/hash.js b/tests/database/hash.js index 18dbc1fbfb..6c0d356725 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -167,6 +167,93 @@ describe('Hash methods', function() { }); }); + describe('getObjectValues()', function() { + it('should return an empty array for a object that does not exist', function(done) { + db.getObjectKeys('doesnotexist', function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(values) && values.length === 0, true); + done(); + }); + }); + + it('should return an array of values for the object\'s fields', function(done) { + db.getObjectKeys('testObject1', function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(values) && values.length === 3, true); + values.forEach(function(value) { + assert.notEqual(['baris', 'usakli', 99].indexOf(value), -1); + }); + + done(); + }); + }); + }); + + describe('isObjectField()', function() { + it('should return false if object does not exist', function(done) { + db.isObjectField('doesnotexist', 'field1', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, false); + done(); + }); + }); + + it('should return false if field does not exist', function(done) { + db.getObjectKeys('testObject1', 'field1', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, false); + done(); + }); + }); + + it('should return true if field exists', function(done) { + db.getObjectKeys('testObject1', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, true); + done(); + }); + }); + }); + + describe('deleteObjectField()', function() { + it('should delete an objects field', function(done) { + db.deleteObjectField('testObject1', 'lastname', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.isObjectField('testObject1', 'lastname', function(err, isField) { + assert.equal(err, null); + assert.equal(isField, false); + done(); + }); + }); + }); + }); + + describe('incrObjectField()', function() { + it('should set an objects field to 1 if object does not exist', function(done) { + db.incrObjectField('testObject3', 'field1', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 1); + done(); + }); + }); + + it('should increment an object fields by 1 and return it', function(done) { + db.incrObjectField('testObject1', 'age', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 100); + done(); + }); + }); + }); + after(function() { db.flushdb(); From 9af3007f3d3b7a86284e2a0cd2e8ff4e3611c06f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:44:44 -0500 Subject: [PATCH 06/31] fix copy paste fails --- tests/database/hash.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/database/hash.js b/tests/database/hash.js index 6c0d356725..e72082b49d 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -169,7 +169,7 @@ describe('Hash methods', function() { describe('getObjectValues()', function() { it('should return an empty array for a object that does not exist', function(done) { - db.getObjectKeys('doesnotexist', function(err, values) { + db.getObjectValues('doesnotexist', function(err, values) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(Array.isArray(values) && values.length === 0, true); @@ -178,7 +178,7 @@ describe('Hash methods', function() { }); it('should return an array of values for the object\'s fields', function(done) { - db.getObjectKeys('testObject1', function(err, values) { + db.getObjectValues('testObject1', function(err, values) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(Array.isArray(values) && values.length === 3, true); @@ -202,7 +202,7 @@ describe('Hash methods', function() { }); it('should return false if field does not exist', function(done) { - db.getObjectKeys('testObject1', 'field1', function(err, value) { + db.isObjectField('testObject1', 'field1', function(err, value) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(value, false); @@ -211,7 +211,7 @@ describe('Hash methods', function() { }); it('should return true if field exists', function(done) { - db.getObjectKeys('testObject1', function(err, value) { + db.isObjectField('testObject1', function(err, value) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(value, true); From e6244c547a14bd02837f617ec9bb0cd831cb5e61 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:46:46 -0500 Subject: [PATCH 07/31] fix isObjectField test --- tests/database/hash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database/hash.js b/tests/database/hash.js index e72082b49d..48012396d9 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -211,7 +211,7 @@ describe('Hash methods', function() { }); it('should return true if field exists', function(done) { - db.isObjectField('testObject1', function(err, value) { + db.isObjectField('testObject1', 'lastname', function(err, value) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(value, true); From 74a3977d4295601abe84798c4d447eb68cfa02b0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:50:10 -0500 Subject: [PATCH 08/31] decrObjectField, incrObjectFieldBy tests --- tests/database/hash.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/database/hash.js b/tests/database/hash.js index 48012396d9..34f6179428 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -254,6 +254,48 @@ describe('Hash methods', function() { }); }); + describe('decrObjectField()', function() { + it('should set an objects field to -1 if object does not exist', function(done) { + db.decrObjectField('testObject4', 'field1', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, -1); + done(); + }); + }); + + it('should decrement an object fields by 1 and return it', function(done) { + db.decrObjectField('testObject1', 'age', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 99); + done(); + }); + }); + }); + + describe('incrObjectFieldBy()', function() { + it('should set an objects field to 5 if object does not exist', function(done) { + db.incrObjectFieldBy('testObject5', 'field1', 5, function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 5); + done(); + }); + }); + + it('should increment an object fields by passed in value and return it', function(done) { + db.decrObjectField('testObject1', 'age', 11, function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 110); + done(); + }); + }); + }); + + + after(function() { db.flushdb(); From ee820341d551db06a1a4b3f280fb9e3dab82b282 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 18:52:23 -0500 Subject: [PATCH 09/31] fix incrObjectFieldBy test --- tests/database/hash.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/database/hash.js b/tests/database/hash.js index 34f6179428..2086dc1af7 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -285,7 +285,7 @@ describe('Hash methods', function() { }); it('should increment an object fields by passed in value and return it', function(done) { - db.decrObjectField('testObject1', 'age', 11, function(err, newValue) { + db.incrObjectFieldBy('testObject1', 'age', 11, function(err, newValue) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(newValue, 110); @@ -296,7 +296,6 @@ describe('Hash methods', function() { - after(function() { db.flushdb(); }); From 38e7ecbb412e909d281cd375cc7ad77e6bef12a0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 30 Dec 2014 22:06:48 -0500 Subject: [PATCH 10/31] setRemove doesnt return result --- app.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app.js b/app.js index fd30123972..fb757a497d 100644 --- a/app.js +++ b/app.js @@ -328,14 +328,9 @@ function resetThemes(callback) { function resetPlugin(pluginId) { var db = require('./src/database'); - db.setRemove('plugins:active', pluginId, function(err, result) { - if (err || result !== 1) { - winston.error('[reset] Could not disable plugin: %s', pluginId); - if (err) { - winston.error('[reset] Encountered error: %s', err.message); - } else { - winston.info('[reset] Perhaps it has already been disabled?'); - } + db.setRemove('plugins:active', pluginId, function(err) { + if (err) { + winston.error('[reset] Could not disable plugin: %s encountered error %s', pluginId, err.message); } else { winston.info('[reset] Plugin `%s` disabled', pluginId); } From cb4fb62d4a476c941c96f54638cbef7972cd03b3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 29 Dec 2014 15:52:32 -0500 Subject: [PATCH 11/31] moved ACP menu into a partial --- src/views/admin/header.tpl | 90 +------------------------------ src/views/admin/partials/menu.tpl | 89 ++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 89 deletions(-) create mode 100644 src/views/admin/partials/menu.tpl diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index 2b14ff8e1f..dfa2dc9c89 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -101,95 +101,7 @@
\ No newline at end of file diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl new file mode 100644 index 0000000000..af9acc8e12 --- /dev/null +++ b/src/views/admin/partials/menu.tpl @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + \ No newline at end of file From fcbb48bb773ca50f3fdeb69126b6e42d71a28b67 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 31 Dec 2014 12:16:20 -0500 Subject: [PATCH 12/31] acp setting for allowGuestHandles #2569 --- src/views/admin/partials/menu.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index af9acc8e12..7059ad6ba0 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -23,6 +23,7 @@
  • Reputation
  • Email
  • User
  • +
  • Guest
  • Post
  • Pagination
  • Tags
  • From 7dc309fc16088fa0462f83c4fe394a9fa1c96480 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 31 Dec 2014 12:36:25 -0500 Subject: [PATCH 13/31] template in composer #2569 --- public/language/en_GB/topic.json | 1 + public/src/modules/composer.js | 3 ++- src/controllers/api.js | 1 + src/views/admin/settings/guest.tpl | 25 +++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/views/admin/settings/guest.tpl diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index 88b1662e3b..5488475e10 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -90,6 +90,7 @@ "fork_success": "Successfully forked topic! Click here to go to the forked topic.", "composer.title_placeholder": "Enter your topic title here...", + "composer.handle_placeholder": "Enter your guest handle", "composer.discard": "Discard", "composer.submit": "Submit", "composer.replying_to": "Replying to %1", diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index e9ae8e8b5d..1a62a5e6e6 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -224,7 +224,8 @@ define('composer', [ var data = { allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain, - isTopic: isTopic + isTopic: isTopic, + allowGuestHandles: config.allowGuestHandles }; parseAndTranslate(template, data, function(composerTemplate) { diff --git a/src/controllers/api.js b/src/controllers/api.js index 2648f944b4..52bfd6233f 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -30,6 +30,7 @@ apiController.getConfig = function(req, res, next) { config.maximumSignatureLength = meta.config.maximumSignatureLength; config.useOutgoingLinksPage = parseInt(meta.config.useOutgoingLinksPage, 10) === 1; config.allowGuestSearching = parseInt(meta.config.allowGuestSearching, 10) === 1; + config.allowGuestHandles = parseInt(meta.config.allowGuestHandles, 10) === 1; config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1; config.allowTopicsThumbnail = parseInt(meta.config.allowTopicsThumbnail, 10) === 1; config.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; diff --git a/src/views/admin/settings/guest.tpl b/src/views/admin/settings/guest.tpl new file mode 100644 index 0000000000..a212547164 --- /dev/null +++ b/src/views/admin/settings/guest.tpl @@ -0,0 +1,25 @@ + + +
    +
    Guests
    +
    +

    + These options affect guest users as a whole. Control over which categories a guest can see or post to is handled in + the categories themselves +

    + +
    +
    + +
    +
    +
    +
    + + \ No newline at end of file From b97c9e4467522e3e6e718b992b2631a5a4cbca39 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 31 Dec 2014 12:44:26 -0500 Subject: [PATCH 14/31] shorter language string for composer guest handle placeholder, #2569 --- public/language/en_GB/topic.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index 5488475e10..1320e21923 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -90,7 +90,7 @@ "fork_success": "Successfully forked topic! Click here to go to the forked topic.", "composer.title_placeholder": "Enter your topic title here...", - "composer.handle_placeholder": "Enter your guest handle", + "composer.handle_placeholder": "Name", "composer.discard": "Discard", "composer.submit": "Submit", "composer.replying_to": "Replying to %1", From 1511a0c3feafcb134cdb9b254e3e809e419edb3e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 13:51:47 -0500 Subject: [PATCH 15/31] closes #2568 --- src/routes/api.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/routes/api.js b/src/routes/api.js index c3d011e1e2..a0a710307b 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -169,12 +169,11 @@ function getTemplatesListing(req, res, next) { return next(err); } - var data = []; - data = results.views.filter(function(value, index, self) { - return self.indexOf(value) === index; - }).map(function(el) { - return el.replace(nconf.get('views_dir') + '/', ''); - }); + var data = results.views.filter(function(value, index, self) { + return value && self.indexOf(value) === index; + }).map(function(el) { + return el && el.replace(nconf.get('views_dir') + '/', ''); + }); data = data.concat(results.extended); From bfaf50908dc6ae058a7374b4535ed9bab7cd2034 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 14:16:27 -0500 Subject: [PATCH 16/31] closes #2565 --- src/messaging.js | 5 +++-- src/views/emails/notif_chat.tpl | 2 +- src/views/emails/notif_chat_plaintext.tpl | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index 980dc13e86..339ec7798c 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -78,7 +78,7 @@ var db = require('./database'), if (!messages || !messages[0]) { return next(null, null); } - + messages[0].newSet = isNewSet; messages[0].mid = mid; next(null, messages[0]); @@ -341,7 +341,8 @@ var db = require('./database'), summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', message: messageObj, site_title: meta.config.title || 'NodeBB', - url: nconf.get('url') + '/chats/' + utils.slugify(messageObj.fromUser.username) + url: nconf.get('url'), + fromUserslug: utils.slugify(messageObj.fromUser.username) }); } }); diff --git a/src/views/emails/notif_chat.tpl b/src/views/emails/notif_chat.tpl index 39bbe1e6cd..a7e6be420d 100644 --- a/src/views/emails/notif_chat.tpl +++ b/src/views/emails/notif_chat.tpl @@ -3,7 +3,7 @@

    {summary}:

    {message.content}
    -[[email:notif.chat.cta]] +[[email:notif.chat.cta]]

    [[email:closing]]
    diff --git a/src/views/emails/notif_chat_plaintext.tpl b/src/views/emails/notif_chat_plaintext.tpl index a7b78ee481..3ecc1f82fc 100644 --- a/src/views/emails/notif_chat_plaintext.tpl +++ b/src/views/emails/notif_chat_plaintext.tpl @@ -4,7 +4,7 @@ {message.content} -[[email:notif.chat.cta]]: {url} +[[email:notif.chat.cta]]: {url}/chats/{fromUserslug} [[email:closing]] From 0ff37f9e0e366491e9ad45c021a624cd0e2a34d3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 14:27:16 -0500 Subject: [PATCH 17/31] sorted set tests --- tests/database.js | 99 +--------------------------------------- tests/database/sorted.js | 43 +++++++++++++++++ 2 files changed, 44 insertions(+), 98 deletions(-) create mode 100644 tests/database/sorted.js diff --git a/tests/database.js b/tests/database.js index 6238f9fa13..9118823d00 100644 --- a/tests/database.js +++ b/tests/database.js @@ -14,103 +14,6 @@ describe('Test database', function() { require('./database/list'); require('./database/sets'); require('./database/hash'); + require('./database/sorted'); - - it('should not throw err', function(done) { - function sortedSetAdd(callback) { - db.sortedSetAdd('sortedSet3', 12, 5, function(err) { - callback(err); - }); - } - - function sortedSetRemove(callback) { - db.sortedSetRemove('sortedSet3', 12, function(err, data) { - callback(err); - }); - } - - function getSortedSetRange(callback) { - db.getSortedSetRange('sortedSet3', 0, -1, function(err, data) { - callback(err, {'getSortedSetRange': data}); - }); - } - - function getSortedSetRevRange(callback) { - db.getSortedSetRevRange('sortedSet3', 0, -1, function(err, data) { - callback(err, {'getSortedSetRevRange': data}); - }); - } - - function getSortedSetRevRangeByScore(callback) { - db.getSortedSetRevRangeByScore('sortedSet3', 0, 10, Infinity, 100, function(err, data) { - callback(err, {'getSortedSetRevRangeByScore': data}); - }); - } - - function sortedSetCount(callback) { - db.sortedSetCount('sortedSet3', -Infinity, Infinity, function(err, data) { - callback(err, {'sortedSetCount': data}); - }); - } - - function sortedSetScore(callback) { - db.sortedSetScore('users:joindate', 1, function(err, data) { - callback(err, {'sortedSetScore': data}); - }); - } - - function sortedSetsScore(callback) { - db.sortedSetsScore(['users:joindate', 'users:derp', 'users:postcount'], 1, function(err, data) { - callback(err, {'sortedSetsScore': data}); - }); - } - - function isSortedSetMember(callback) { - db.isSortedSetMember('sortedSet3', 5, function(err, data) { - callback(err, {'sortedSetMember': data}); - }); - } - - function getSortedSetUnion(callback) { - db.getSortedSetUnion(['users:joindate', 'users:derp', 'users:postcount'], 0, -1, function(err, data) { - callback(err, {'sortedSetUnion': data}); - }); - } - - function getSortedSetRevUnion(callback) { - db.getSortedSetRevUnion(['users:joindate', 'users:derp', 'users:postcount'], 0, -1, function(err, data) { - callback(err, {'sortedSetUnion': data}); - }); - } - - var sortedSetTasks = [ - sortedSetAdd, - sortedSetAdd, - isSortedSetMember, - getSortedSetRange, - sortedSetAdd, - getSortedSetRange, - getSortedSetRevRange, - sortedSetRemove, - getSortedSetRange, - sortedSetCount, - sortedSetScore, - sortedSetsScore, - getSortedSetRevRangeByScore, - getSortedSetUnion, - getSortedSetRevUnion - ]; - - async.series(sortedSetTasks, function(err, results) { - assert.equal(err, null, 'error in sorted set methods'); - assert.ok(results); - - done(); - }); - - }); - - after(function() { - db.flushdb(); - }); }); diff --git a/tests/database/sorted.js b/tests/database/sorted.js new file mode 100644 index 0000000000..d5a532ccb2 --- /dev/null +++ b/tests/database/sorted.js @@ -0,0 +1,43 @@ +'use strict'; + +var async = require('async'), + assert = require('assert'), + db = require('../mocks/databasemock'); + +describe('Sorted Set methods', function() { + + describe('sortedSetAdd()', function() { + + it('should add an element to a sorted set', function(done) { + db.sortedSetAdd('sorted1', 1, 'value1', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + + it('should add two elements to a sorted set', function(done) { + db.sortedSetAdd('sorted2', [1, 2], ['value1', 'value2'], function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('sortedSetsAdd()', function() { + it('should add an element to two sorted sets', function(done) { + db.sortedSetsAdd(['sorted1, sorted2'], 3, 'value3', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + + + after(function() { + db.flushdb(); + }); +}); From 00541b2bc8507c58688da9d4c9b12015798fd5d3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 14:41:58 -0500 Subject: [PATCH 18/31] sortedSetRange tests --- src/database/mongo/sorted.js | 32 +++++++++---------- tests/database/sorted.js | 62 +++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 2031155b78..c61e748a14 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -100,6 +100,22 @@ module.exports = function(db, module) { }); }; + module.getSortedSetRange = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, 1, false, callback); + }; + + module.getSortedSetRevRange = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, -1, false, callback); + }; + + module.getSortedSetRangeWithScores = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, 1, true, callback); + }; + + module.getSortedSetRevRangeWithScores = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, -1, true, callback); + }; + function getSortedSetRange(key, start, stop, sort, withScores, callback) { if (!key) { return callback(); @@ -128,22 +144,6 @@ module.exports = function(db, module) { }); } - module.getSortedSetRange = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, 1, false, callback); - }; - - module.getSortedSetRevRange = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, -1, false, callback); - }; - - module.getSortedSetRangeWithScores = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, 1, true, callback); - }; - - module.getSortedSetRevRangeWithScores = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, -1, true, callback); - }; - module.getSortedSetRangeByScore = function(key, start, count, min, max, callback) { getSortedSetRangeByScore(key, start, count, min, max, 1, false, callback); }; diff --git a/tests/database/sorted.js b/tests/database/sorted.js index d5a532ccb2..0a7f11db5c 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -7,7 +7,6 @@ var async = require('async'), describe('Sorted Set methods', function() { describe('sortedSetAdd()', function() { - it('should add an element to a sorted set', function(done) { db.sortedSetAdd('sorted1', 1, 'value1', function(err) { assert.equal(err, null); @@ -35,6 +34,67 @@ describe('Sorted Set methods', function() { }); }); + describe('getSortedSetRange()', function() { + it('should return the lowest scored element', function(done) { + db.getSortedSetRange('sorted2', 0, 0, function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, 'value1'); + done(); + }); + }); + + it('should return elements sorted by score lowest to highest', function(done) { + db.getSortedSetRange('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value1', 'value2', 'value3']); + done(); + }); + }); + }); + + describe('getSortedSetRevRange()', function() { + it('should return the highest scored element', function(done) { + db.getSortedSetRevRange('sorted2', 0, 0, function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, 'value3'); + done(); + }); + }); + + it('should return elements sorted by score highest to lowest', function(done) { + db.getSortedSetRevRange('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value3', 'value2', 'value1']); + done(); + }); + }); + }); + + describe('getSortedSetRangeWithScores()', function() { + it('should return array of elements sorted by score lowest to highest with scores', function(done) { + db.getSortedSetRangeWithScores('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value1', score: 1}, {value: 'value2', score: 2}, {value: 'value3', score: 3}]); + done(); + }); + }); + }); + + describe('getSortedSetRevRangeWithScores()', function() { + it('should return array of elements sorted by score highest to lowest with scores', function(done) { + db.getSortedSetRevRangeWithScores('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value3', score: 3}, {value: 'value2', score: 2}, {value: 'value1', score: 1}]); + done(); + }); + }); + }); after(function() { From 1f0902bbe8cdad9ad9c1f4be1dc99619ef0ba1a3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 14:44:52 -0500 Subject: [PATCH 19/31] fix range tests --- tests/database/sorted.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database/sorted.js b/tests/database/sorted.js index 0a7f11db5c..e38f8b8a8b 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -26,7 +26,7 @@ describe('Sorted Set methods', function() { describe('sortedSetsAdd()', function() { it('should add an element to two sorted sets', function(done) { - db.sortedSetsAdd(['sorted1, sorted2'], 3, 'value3', function(err) { + db.sortedSetsAdd(['sorted1', 'sorted2'], 3, 'value3', function(err) { assert.equal(err, null); assert.equal(arguments.length, 1); done(); From b443aec450b4084af659dba99edbe4b221de68f9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 15:52:52 -0500 Subject: [PATCH 20/31] rest of the sorted set tests --- tests/database/sorted.js | 369 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) diff --git a/tests/database/sorted.js b/tests/database/sorted.js index e38f8b8a8b..eceec0bc38 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -22,6 +22,14 @@ describe('Sorted Set methods', function() { done(); }); }); + + it('should add four elements to a sorted set', function(done) { + db.sortedSetAdd('sorted3', [2, 3, 4, 5], ['value2', 'value3', 'value4', 'value5'], function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); }); describe('sortedSetsAdd()', function() { @@ -97,6 +105,367 @@ describe('Sorted Set methods', function() { }); + describe('getSortedSetRangeByScore()', function() { + it('should get count elements with score between min max sorted by score lowest to highest', function(done) { + db.getSortedSetRangeByScore('sorted2', 0, -1, '-inf', 2, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value1', 'value2']); + done(); + }); + }); + }); + + describe('getSortedSetRevRangeByScore()', function() { + it('should get count elements with score between max min sorted by score highest to lowest', function(done) { + db.getSortedSetRevRangeByScore('sorted2', 0, -1, '+inf', 1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value3', 'value2']); + done(); + }); + }); + }); + + + describe('getSortedSetRangeByScoreWithScores()', function() { + it('should get count elements with score between min max sorted by score lowest to highest with scores', function(done) { + db.getSortedSetRangeByScoreWithScores('sorted2', 0, -1, '-inf', 2, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value1', score: 1}, {value: 'value2', score: 2}]); + done(); + }); + }); + }); + + describe('getSortedSetRevRangeByScoreWithScores()', function() { + it('should get count elements with score between max min sorted by score highest to lowest', function(done) { + db.getSortedSetRevRangeByScoreWithScores('sorted2', 0, -1, '+inf', 1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value3', score: 3}, {value: 'value2', score: 2}]); + done(); + }); + }); + }); + + describe('sortedSetCount()', function() { + it('should return 0 for a sorted set that does not exist', function(done) { + db.sortedSetCount('doesnotexist', 0, 10, function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 0); + done(); + }); + }); + + it('should return number of elements between scores min max inclusive', function(done) { + db.sortedSetCount('sorted2', '-inf', 2, function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 2); + done(); + }); + }); + }); + + describe('sortedSetCard()', function() { + it('should return 0 for a sorted set that does not exist', function(done) { + db.sortedSetCard('doesnotexist', function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 0); + done(); + }); + }); + + it('should return number of elements in a sorted set', function(done) { + db.sortedSetCard('sorted2', function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 3); + done(); + }); + }); + }); + + describe('sortedSetsCard()', function() { + it('should return the number of elements in sorted sets', function(done) { + db.sortedSetsCard(['sorted1', 'sorted2', 'doesnotexist'], function(err, counts) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(counts, [2, 3, 0]); + done(); + }); + }); + }); + + describe('sortedSetRank()', function() { + it('should return falsy if sorted set doesnot exist', function(done) { + db.sortedSetRank('doesnotexist', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return falsy if element isnt in sorted set', function(done) { + db.sortedSetRank('sorted2', 'value5', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return the rank of the element in the sorted set sorted by lowest to highest score', function(done) { + db.sortedSetRank('sorted2', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(rank, 0); + done(); + }); + }); + }); + + describe('sortedSetRevRank()', function() { + it('should return falsy if sorted set doesnot exist', function(done) { + db.sortedSetRevRank('doesnotexist', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return falsy if element isnt in sorted set', function(done) { + db.sortedSetRevRank('sorted2', 'value5', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return the rank of the element in the sorted set sorted by highest to lowest score', function(done) { + db.sortedSetRevRank('sorted2', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(rank, 2); + done(); + }); + }); + }); + + describe('sortedSetsRanks()', function() { + it('should return the ranks of values in sorted sets', function(done) { + db.sortedSetsRanks(['sorted1', 'sorted2'], ['value1', 'value2'], function(err, ranks) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(ranks, [0, 1]); + done(); + }); + }); + }); + + describe('sortedSetRanks()', function() { + it('should return the ranks of values in a sorted set', function(done) { + db.sortedSetsRanks('sorted2', ['value2', 'value1', 'value3', 'value4'], function(err, ranks) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(ranks, [1, 0, 2, null]); + done(); + }); + }); + }); + + describe('sortedSetScore()', function() { + it('should return falsy if sorted set does not exist', function(done) { + db.sortedSetScore('doesnotexist', 'value1', function(err, score) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!score, false); + done(); + }); + }); + + it('should return falsy if element is not in sorted set', function(done) { + db.sortedSetScore('sorted2', 'value5', function(err, score) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!score, false); + done(); + }); + }); + + it('should return the score of an element', function(done) { + db.sortedSetScore('sorted2', 'value2', function(err, score) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(score, 2); + done(); + }); + }); + }); + + describe('sortedSetsScore()', function() { + it('should return the scores of value in sorted sets', function(done) { + db.sortedSetsScore(['sorted1', 'sorted2', 'doesnotexist'], 'value2', function(err, scores) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(scores, [2, 2, null]); + done(); + }); + }); + }); + + describe('sortedSetScores()', function() { + it('should return the scores of value in sorted sets', function(done) { + db.sortedSetScores('sorted2', ['value2', 'value1', 'doesnotexist'], function(err, scores) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(scores, [2, 1, null]); + done(); + }); + }); + }); + + describe('isSortedSetMember()', function() { + it('should return false if sorted set does not exist', function(done) { + db.isSortedSetMember('doesnotexist', 'value1', function(err, isMember) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(isMember, false); + done(); + }); + }); + + it('should return false if element is not in sorted set', function(done) { + db.isSortedSetMember('sorted2', 'value5', function(err, isMember) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(isMember, false); + done(); + }); + }); + + it('should return true if element is in sorted set', function(done) { + db.isSortedSetMember('sorted2', 'value2', function(err, isMember) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(isMember, true); + done(); + }); + }); + }); + + describe('isSortedSetMembers()', function() { + it('should return an array of booleans indicating membership', function(done) { + db.isSortedSetMembers('sorted2', ['value1', 'value2', 'value5'], function(err, isMembers) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(isMembers, [true, true, false]); + done(); + }); + }); + }); + + describe('getSortedSetUnion()', function() { + it('should return an array of values from both sorted sets sorted by scores lowest to highest', function(done) { + db.getSortedSetUnion(['sorted2', 'sorted3'], 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value1', 'value2', 'value3', 'value4', 'value5']); + done(); + }); + }); + }); + + describe('getSortedSetRevUnion()', function() { + it('should return an array of values from both sorted sets sorted by scores highest to lowest', function(done) { + db.getSortedSetRevUnion(['sorted2', 'sorted3'], 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value5', 'value4', 'value3', 'value2', 'value1']); + done(); + }); + }); + }); + + describe('sortedSetIncrBy()', function() { + it('should create a sorted set with a field set to 1', function(done) { + db.sortedSetIncrBy('sortedIncr', 'field1', 1, function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 1); + db.sortedSetScore('sortedIncr', 'field1', function(err, score) { + assert.equal(err, null); + assert.equal(score, 1); + done(); + }); + }); + }); + + it('should increment a field of a sorted set by 5', function(done) { + db.sortedSetIncrBy('sortedIncr', 'field1', 5, function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 6); + db.sortedSetScore('sortedIncr', 'field1', function(err, score) { + assert.equal(err, null); + assert.equal(score, 6); + done(); + }); + }); + }); + }); + + + describe('sortedSetRemove()', function() { + it('should remove an element from a sorted set', function(done) { + db.sortedSetRemove('sorted2', 'value2', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.isSortedSetMember('sorted2', 'value2', function(err, isMember) { + assert.equal(err, null); + assert.equal(isMember, false); + done(); + }); + }); + }); + }); + + describe('sortedSetsRemove()', function() { + it('should remove element from multiple sorted sets', function(done) { + db.sortedSetsRemove(['sorted1', 'sorted2'], 'value1', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.sortedSetsScore(['sorted1', 'sorted2'], 'value1', function(err, scores) { + assert.equal(err, null); + assert.deepEqual(scores, [null, null]); + done(); + }); + }); + }); + }); + + describe('sortedSetsRemoveRangeByScore()', function() { + it('should remove elements with scores between min max inclusive', function(done) { + db.sortedSetsRemoveRangeByScore(['sorted3'], 4, 5, function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.getSortedSetRange('sorted3', 0, -1, function(err, values) { + assert.equal(err, null); + assert.deepEqual(values, ['value2', 'value3']); + done(); + }); + }); + }); + }); + + after(function() { db.flushdb(); }); From 03ee524121956b84936d59b1a0f02060094344d4 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 15:59:57 -0500 Subject: [PATCH 21/31] fix tests and mongo method --- src/database/mongo/sorted.js | 2 +- tests/database/sorted.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index c61e748a14..603b1ab770 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -153,7 +153,7 @@ module.exports = function(db, module) { }; module.getSortedSetRangeByScoreWithScores = function(key, start, count, min, max, callback) { - getSortedSetRangeByScore(key, start, count, min, max, -1, true, callback); + getSortedSetRangeByScore(key, start, count, min, max, 1, true, callback); }; module.getSortedSetRevRangeByScoreWithScores = function(key, start, count, max, min, callback) { diff --git a/tests/database/sorted.js b/tests/database/sorted.js index eceec0bc38..c00bd2a392 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -118,7 +118,7 @@ describe('Sorted Set methods', function() { describe('getSortedSetRevRangeByScore()', function() { it('should get count elements with score between max min sorted by score highest to lowest', function(done) { - db.getSortedSetRevRangeByScore('sorted2', 0, -1, '+inf', 1, function(err, values) { + db.getSortedSetRevRangeByScore('sorted2', 0, -1, '+inf', 2, function(err, values) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.deepEqual(values, ['value3', 'value2']); From 0a534b20e32b9672460fd03fdbba4f26883e6af7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 16:09:33 -0500 Subject: [PATCH 22/31] sortedSetCount fix for +inf/-inf --- src/database/mongo/sorted.js | 9 ++++++++- tests/database/sorted.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 603b1ab770..d21d7a616b 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -204,7 +204,14 @@ module.exports = function(db, module) { if (!key) { return callback(); } - db.collection('objects').count({_key: key, score: {$gte: min, $lte: max}}, function(err, count) { + var scoreQuery = {}; + if (min !== '-inf') { + scoreQuery.$gte = min; + } + if (max !== '+inf') { + scoreQuery.$lte = max; + } + db.collection('objects').count({_key: key, score: scoreQuery}, function(err, count) { callback(err, count ? count : 0); }); }; diff --git a/tests/database/sorted.js b/tests/database/sorted.js index c00bd2a392..e202b5738f 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -141,7 +141,7 @@ describe('Sorted Set methods', function() { describe('getSortedSetRevRangeByScoreWithScores()', function() { it('should get count elements with score between max min sorted by score highest to lowest', function(done) { - db.getSortedSetRevRangeByScoreWithScores('sorted2', 0, -1, '+inf', 1, function(err, values) { + db.getSortedSetRevRangeByScoreWithScores('sorted2', 0, -1, '+inf', 2, function(err, values) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.deepEqual(values, [{value: 'value3', score: 3}, {value: 'value2', score: 2}]); From 861a3ca8a87a8965cdba6b3718db943443190695 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 16:14:34 -0500 Subject: [PATCH 23/31] more test fixes --- tests/database/sorted.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/database/sorted.js b/tests/database/sorted.js index e202b5738f..74c2e5fb5d 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -272,7 +272,7 @@ describe('Sorted Set methods', function() { describe('sortedSetRanks()', function() { it('should return the ranks of values in a sorted set', function(done) { - db.sortedSetsRanks('sorted2', ['value2', 'value1', 'value3', 'value4'], function(err, ranks) { + db.sortedSetRanks('sorted2', ['value2', 'value1', 'value3', 'value4'], function(err, ranks) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.deepEqual(ranks, [1, 0, 2, null]); @@ -315,7 +315,7 @@ describe('Sorted Set methods', function() { db.sortedSetsScore(['sorted1', 'sorted2', 'doesnotexist'], 'value2', function(err, scores) { assert.equal(err, null); assert.equal(arguments.length, 2); - assert.deepEqual(scores, [2, 2, null]); + assert.deepEqual(scores, [null, 2, null]); done(); }); }); From 325815a78d25f21b1be3c2d5872b219f3d9f6aff Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 31 Dec 2014 16:27:35 -0500 Subject: [PATCH 24/31] showing guest handles in frontend UI #2569 --- public/src/modules/composer.js | 4 ++++ src/postTools.js | 23 +++++++++---------- src/posts/create.js | 6 ++++- src/socket.io/posts.js | 12 +++++++++- src/socket.io/topics.js | 1 + src/topics/create.js | 6 +++-- src/topics/posts.js | 42 ++++++++++++++++++++-------------- 7 files changed, 61 insertions(+), 33 deletions(-) diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index 1a62a5e6e6..6615c51df5 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -378,6 +378,7 @@ define('composer', [ function post(post_uuid) { var postData = composer.posts[post_uuid], postContainer = $('#cmp-uuid-' + post_uuid), + handleEl = postContainer.find('.handle'), titleEl = postContainer.find('.title'), bodyEl = postContainer.find('textarea'), thumbEl = postContainer.find('input#topic-thumb-url'); @@ -406,6 +407,7 @@ define('composer', [ if (parseInt(postData.cid, 10) > 0) { composerData = { + handle: handleEl ? handleEl.val() : undefined, title: titleEl.val(), content: bodyEl.val(), topic_thumb: thumbEl.val() || '', @@ -424,6 +426,7 @@ define('composer', [ } else if (parseInt(postData.tid, 10) > 0) { composerData = { tid: postData.tid, + handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), toPid: postData.toPid }; @@ -433,6 +436,7 @@ define('composer', [ } else if (parseInt(postData.pid, 10) > 0) { composerData = { pid: postData.pid, + handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), title: titleEl.val(), topic_thumb: thumbEl.val() || '', diff --git a/src/postTools.js b/src/postTools.js index 0e8d3934ce..c9e7648ea9 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -18,21 +18,22 @@ var winston = require('winston'), (function(PostTools) { - PostTools.edit = function(uid, pid, title, content, options, callback) { - options = options || {}; + PostTools.edit = function(data, callback) { + var options = data.options || {}, + title = data.title.trim(); async.waterfall([ function (next) { - privileges.posts.canEdit(pid, uid, next); + privileges.posts.canEdit(data.pid, data.uid, next); }, function(canEdit, next) { if (!canEdit) { return next(new Error('[[error:no-privileges]]')); } - posts.getPostData(pid, next); + posts.getPostData(data.pid, next); }, function(postData, next) { - postData.content = content; + postData.content = data.content; plugins.fireHook('filter:post.save', postData, next); } ], function(err, postData) { @@ -42,15 +43,15 @@ var winston = require('winston'), async.parallel({ post: function(next) { - posts.setPostFields(pid, { + posts.setPostFields(data.pid, { edited: Date.now(), - editor: uid, + editor: data.uid, content: postData.content }, next); }, topic: function(next) { var tid = postData.tid; - posts.isMain(pid, function(err, isMainPost) { + posts.isMain(data.pid, function(err, isMainPost) { if (err) { return next(err); } @@ -64,11 +65,9 @@ var winston = require('winston'), }); } - title = title.trim(); - var topicData = { tid: tid, - mainPid: pid, + mainPid: data.pid, title: title, slug: tid + '/' + utils.slugify(title) }; @@ -96,7 +95,7 @@ var winston = require('winston'), }); }, postData: function(next) { - PostTools.parsePost(postData, uid, next); + PostTools.parsePost(postData, data.uid, next); } }, function(err, results) { if (err) { diff --git a/src/posts/create.js b/src/posts/create.js index d9040e443e..198b9baae4 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -14,10 +14,10 @@ module.exports = function(Posts) { Posts.create = function(data, callback) { var uid = data.uid, tid = data.tid, + handle = data.uid ? null : data.handle, // Only guests have handles! content = data.content, timestamp = data.timestamp || Date.now(); - if (!uid && parseInt(uid, 10) !== 0) { return callback(new Error('[[error:invalid-uid]]')); } @@ -51,6 +51,10 @@ module.exports = function(Posts) { postData.ip = data.ip; } + if (handle) { + postData.handle = handle; + } + plugins.fireHook('filter:post.save', postData, next); }, function(postData, next) { diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 1b7193ccf0..de66e6a5e3 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -257,7 +257,17 @@ SocketPosts.edit = function(socket, data, callback) { return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); } - postTools.edit(socket.uid, data.pid, data.title, data.content, {topic_thumb: data.topic_thumb, tags: data.tags}, function(err, results) { + // uid, pid, title, content, options + postTools.edit({ + uid: socket.uid, + pid: data.pid, + title: data.title, + content: data.content, + options: { + topic_thumb: data.topic_thumb, + tags: data.tags + } + }, function(err, results) { if (err) { return callback(err); } diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index e5222e04a5..fe2c313452 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -27,6 +27,7 @@ SocketTopics.post = function(socket, data, callback) { topics.post({ uid: socket.uid, + handle: data.handle, title: data.title, content: data.content, cid: data.category_id, diff --git a/src/topics/create.js b/src/topics/create.js index 71a1dddec4..25053ef819 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -93,6 +93,7 @@ module.exports = function(Topics) { Topics.post = function(data, callback) { var uid = data.uid, + handle = data.handle, title = data.title, content = data.content, cid = data.cid; @@ -134,7 +135,7 @@ module.exports = function(Topics) { Topics.create({uid: uid, title: title, cid: cid, thumb: data.thumb, tags: data.tags}, next); }, function(tid, next) { - Topics.reply({uid:uid, tid:tid, content:content, req: data.req}, next); + Topics.reply({uid:uid, tid:tid, handle: handle, content:content, req: data.req}, next); }, function(postData, next) { async.parallel({ @@ -184,6 +185,7 @@ module.exports = function(Topics) { var tid = data.tid, uid = data.uid, toPid = data.toPid, + handle = data.handle, content = data.content, postData; @@ -226,7 +228,7 @@ module.exports = function(Topics) { checkContentLength(content, next); }, function(next) { - posts.create({uid: uid, tid: tid, content: content, toPid: toPid, ip: data.req ? data.req.ip : null}, next); + posts.create({uid: uid, tid: tid, handle: handle, content: content, toPid: toPid, ip: data.req ? data.req.ip : null}, next); }, function(data, next) { postData = data; diff --git a/src/topics/posts.js b/src/topics/posts.js index 0666303630..6653c15934 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -4,6 +4,7 @@ var async = require('async'), winston = require('winston'), + _ = require('underscore'), db = require('../database'), user = require('../user'), @@ -110,25 +111,32 @@ module.exports = function(Topics) { return callback(err); } - for (var i = 0; i < postData.length; ++i) { - if (postData[i]) { - postData[i].index = results.indices[i]; - postData[i].deleted = parseInt(postData[i].deleted, 10) === 1; - postData[i].user = results.userData[postData[i].uid]; - postData[i].editor = postData[i].editor ? results.editors[postData[i].editor] : null; - postData[i].favourited = results.favourites[i]; - postData[i].upvoted = results.voteData.upvotes[i]; - postData[i].downvoted = results.voteData.downvotes[i]; - postData[i].votes = postData[i].votes || 0; - postData[i].display_moderator_tools = results.privileges[i].editable; - postData[i].display_move_tools = results.privileges[i].move && postData[i].index !== 0; - postData[i].selfPost = parseInt(uid, 10) === parseInt(postData[i].uid, 10); - - if(postData[i].deleted && !results.privileges[i].view_deleted) { - postData[i].content = '[[topic:post_is_deleted]]'; + postData = postData.map(function(postObj, i) { + if (postObj) { + postObj.index = results.indices[i]; + postObj.deleted = parseInt(postObj.deleted, 10) === 1; + postObj.user = _.clone(results.userData[postObj.uid]); + postObj.editor = postObj.editor ? results.editors[postObj.editor] : null; + postObj.favourited = results.favourites[i]; + postObj.upvoted = results.voteData.upvotes[i]; + postObj.downvoted = results.voteData.downvotes[i]; + postObj.votes = postObj.votes || 0; + postObj.display_moderator_tools = results.privileges[i].editable; + postObj.display_move_tools = results.privileges[i].move && postObj.index !== 0; + postObj.selfPost = parseInt(uid, 10) === parseInt(postObj.uid, 10); + + if(postObj.deleted && !results.privileges[i].view_deleted) { + postObj.content = '[[topic:post_is_deleted]]'; + } + + // Username override for guests, if enabled + if (parseInt(postObj.uid, 10) === 0 && postObj.handle) { + postObj.user.username = postObj.handle; } } - } + + return postObj; + }).filter(Boolean); callback(null, postData); }); From faf4163a6e9ca343356d1823fd88a8a43e7f018a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 16:28:24 -0500 Subject: [PATCH 25/31] more test fixes --- tests/database/sorted.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/database/sorted.js b/tests/database/sorted.js index 74c2e5fb5d..a675c709ec 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -374,10 +374,10 @@ describe('Sorted Set methods', function() { describe('getSortedSetUnion()', function() { it('should return an array of values from both sorted sets sorted by scores lowest to highest', function(done) { - db.getSortedSetUnion(['sorted2', 'sorted3'], 0, -1, function(err, values) { + db.getSortedSetUnion(['sorted1', 'sorted3'], 0, -1, function(err, values) { assert.equal(err, null); assert.equal(arguments.length, 2); - assert.deepEqual(values, ['value1', 'value2', 'value3', 'value4', 'value5']); + assert.deepEqual(values, ['value1', 'value2', 'value4', 'value5', 'value3']); done(); }); }); @@ -385,10 +385,10 @@ describe('Sorted Set methods', function() { describe('getSortedSetRevUnion()', function() { it('should return an array of values from both sorted sets sorted by scores highest to lowest', function(done) { - db.getSortedSetRevUnion(['sorted2', 'sorted3'], 0, -1, function(err, values) { + db.getSortedSetRevUnion(['sorted1', 'sorted3'], 0, -1, function(err, values) { assert.equal(err, null); assert.equal(arguments.length, 2); - assert.deepEqual(values, ['value5', 'value4', 'value3', 'value2', 'value1']); + assert.deepEqual(values, ['value3', 'value5', 'value4', 'value2', 'value1']); done(); }); }); @@ -396,7 +396,7 @@ describe('Sorted Set methods', function() { describe('sortedSetIncrBy()', function() { it('should create a sorted set with a field set to 1', function(done) { - db.sortedSetIncrBy('sortedIncr', 'field1', 1, function(err, newValue) { + db.sortedSetIncrBy('sortedIncr', 1, 'field1', function(err, newValue) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(newValue, 1); @@ -409,7 +409,7 @@ describe('Sorted Set methods', function() { }); it('should increment a field of a sorted set by 5', function(done) { - db.sortedSetIncrBy('sortedIncr', 'field1', 5, function(err, newValue) { + db.sortedSetIncrBy('sortedIncr', 5, 'field1', function(err, newValue) { assert.equal(err, null); assert.equal(arguments.length, 2); assert.equal(newValue, 6); From fbd875b397ac241a284586fa6cc7834d164c0384 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 16:38:57 -0500 Subject: [PATCH 26/31] fix sortedSetIncrBy return --- src/database/mongo/sorted.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index d21d7a616b..74da932a18 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -454,8 +454,8 @@ module.exports = function(db, module) { value = helpers.fieldToString(value); data.score = parseInt(increment, 10); - db.collection('objects').findAndModify({_key: key, value: value}, {}, {$inc: data}, {new:true, upsert:true}, function(err, result) { - callback(err, result ? result[value] : null); + db.collection('objects').findAndModify({_key: key, value: value}, {}, {$inc: data}, {new: true, upsert: true}, function(err, result) { + callback(err, result ? result.score : null); }); }; }; \ No newline at end of file From 6d31fee3f5d0714171148d52e7ebaf3964b91822 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 31 Dec 2014 17:13:19 -0500 Subject: [PATCH 27/31] closes #2564 change user.search to accept params can search substr with startsWith:false no infinite scroll on admin user search page --- public/src/admin/manage/users.js | 35 +++++++++++++++++-------------- src/controllers/admin/users.js | 5 ++++- src/socket.io/admin/categories.js | 2 +- src/socket.io/admin/user.js | 2 +- src/socket.io/user.js | 2 +- src/user/search.js | 16 ++++++++++---- 6 files changed, 38 insertions(+), 24 deletions(-) diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index c762bc68f4..75ab7e224c 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -267,14 +267,21 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) handleUserCreate(); - function onUsersLoaded(users) { - templates.parse('admin/manage/users', 'users', {users: users, requireEmailConfirmation: config.requireEmailConfirmation}, function(html) { - $('#users-container').append($(html)); - selectable.enable('#users-container', '.user-selectable'); - }); - } + $('#load-more-users-btn').on('click', loadMoreUsers); + + $(window).off('scroll').on('scroll', function() { + var bottom = ($(document).height() - $(window).height()) * 0.9; + + if ($(window).scrollTop() > bottom && !loadingMoreUsers) { + loadMoreUsers(); + } + }); + function loadMoreUsers() { + if (active === 'search') { + return; + } var set = 'users:joindate'; if (active === 'sort-posts') { set = 'users:postcount'; @@ -284,7 +291,6 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) set = 'users:banned'; } - loadingMoreUsers = true; socket.emit('user.loadMore', { set: set, @@ -297,15 +303,12 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) }); } - $('#load-more-users-btn').on('click', loadMoreUsers); - - $(window).off('scroll').on('scroll', function() { - var bottom = ($(document).height() - $(window).height()) * 0.9; - - if ($(window).scrollTop() > bottom && !loadingMoreUsers) { - loadMoreUsers(); - } - }); + function onUsersLoaded(users) { + templates.parse('admin/manage/users', 'users', {users: users, requireEmailConfirmation: config.requireEmailConfirmation}, function(html) { + $('#users-container').append($(html)); + selectable.enable('#users-container', '.user-selectable'); + }); + } }; diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index f750c6648b..17944b8370 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -9,7 +9,7 @@ var usersController = {}; usersController.search = function(req, res, next) { res.render('admin/manage/users', { search_display: '', - loadmore_display: 'none', + loadmore_display: 'hide', users: [] }); }; @@ -52,6 +52,9 @@ function getUsers(set, req, res, next) { usersController.getCSV = function(req, res, next) { user.getUsersCSV(function(err, data) { + if (err) { + return next(err); + } res.attachment('users.csv'); res.setHeader('Content-Type', 'text/csv'); res.end(data); diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js index 00992fc4b4..2ba036d7e0 100644 --- a/src/socket.io/admin/categories.js +++ b/src/socket.io/admin/categories.js @@ -36,7 +36,7 @@ Categories.search = function(socket, data, callback) { var username = data.username, cid = data.cid; - user.search(username, 'username', function(err, data) { + user.search({query: username}, function(err, data) { if (err) { return callback(err); } diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index 2184ee83c2..bcb78b223f 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -175,7 +175,7 @@ User.deleteUsers = function(socket, uids, callback) { }; User.search = function(socket, data, callback) { - user.search(data.query, data.type, function(err, searchData) { + user.search({query: data.query, by: data.type, startsWith: false}, function(err, searchData) { if (err) { return callback(err); } diff --git a/src/socket.io/user.js b/src/socket.io/user.js index a7ca2d6605..e20ff65bbb 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -66,7 +66,7 @@ SocketUser.search = function(socket, username, callback) { if (!socket.uid) { return callback(new Error('[[error:not-logged-in]]')); } - user.search(username, 'username', callback); + user.search({query: username}, callback); }; // Password Reset diff --git a/src/user/search.js b/src/user/search.js index 1400c63a67..9bc162d097 100644 --- a/src/user/search.js +++ b/src/user/search.js @@ -6,18 +6,22 @@ var async = require('async'), module.exports = function(User) { - User.search = function(query, type, callback) { + User.search = function(data, callback) { + var query = data.query; + var by = data.by || 'username'; + var startsWith = data.hasOwnProperty('startsWith') ? data.startsWith : true; + if (!query || query.length === 0) { return callback(null, {timing:0, users:[]}); } - if (type === 'ip') { + if (by === 'ip') { return searchByIP(query, callback); } var start = process.hrtime(); var key = 'username:uid'; - if (type === 'email') { + if (by === 'email') { key = 'email:uid'; } @@ -32,7 +36,11 @@ module.exports = function(User) { var uids = []; for(var i=0; i Date: Wed, 31 Dec 2014 17:20:28 -0500 Subject: [PATCH 28/31] search test --- tests/user.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/user.js b/tests/user.js index 2f992215f7..9e53b4c4c5 100644 --- a/tests/user.js +++ b/tests/user.js @@ -157,6 +157,17 @@ describe('User', function() { }); }); + describe('.search()', function() { + it('should return an object containing an array of matching users', function(done) { + User.search({query: 'john'}, function(err, searchData) { + assert.ifError(err); + assert.equal(Array.isArray(searchData.users) && searchData.users.length > 0, true); + assert.equal(searchData.users[0].username, 'John Smith'); + done(); + }); + }); + }); + after(function() { db.flushdb(); }); From 9befa6aca74e0ba53f2859d28ec30f82ab8b247f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 31 Dec 2014 20:48:32 -0500 Subject: [PATCH 29/31] proper handling of post editing, integration with ACP toggle, #2569 --- public/src/modules/composer.js | 11 +++++++---- src/socket.io/modules.js | 3 ++- src/topics/posts.js | 5 +++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index 6615c51df5..a07af172f1 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -154,6 +154,7 @@ define('composer', [ push({ pid: pid, + uid: threadData.uid, title: $('

    ').html(threadData.title).text(), body: threadData.body, modified: false, @@ -213,9 +214,11 @@ define('composer', [ } function createNewComposer(post_uuid) { - var allowTopicsThumbnail = config.allowTopicsThumbnail && composer.posts[post_uuid].isMain && (config.hasImageUploadPlugin || config.allowFileUploads); - var isTopic = composer.posts[post_uuid] ? !!composer.posts[post_uuid].cid : false; - var isMain = composer.posts[post_uuid] ? !!composer.posts[post_uuid].isMain : false; + var allowTopicsThumbnail = config.allowTopicsThumbnail && composer.posts[post_uuid].isMain && (config.hasImageUploadPlugin || config.allowFileUploads), + isTopic = composer.posts[post_uuid] ? !!composer.posts[post_uuid].cid : false, + isMain = composer.posts[post_uuid] ? !!composer.posts[post_uuid].isMain : false, + isEditing = composer.posts[post_uuid] ? !!composer.posts[post_uuid].pid : false, + isGuestPost = composer.posts[post_uuid] ? composer.posts[post_uuid].uid === '0' : null; composer.bsEnvironment = utils.findBootstrapEnvironment(); @@ -225,7 +228,7 @@ define('composer', [ allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain, isTopic: isTopic, - allowGuestHandles: config.allowGuestHandles + showHandleInput: (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)) && config.allowGuestHandles }; parseAndTranslate(template, data, function(composerTemplate) { diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index a922613ac3..7c74530e3e 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -35,7 +35,7 @@ SocketModules.composer.push = function(socket, pid, callback) { if (err || !canRead) { return callback(err || new Error('[[error:no-privileges]]')); } - posts.getPostFields(pid, ['content', 'tid'], function(err, postData) { + posts.getPostFields(pid, ['content', 'tid', 'uid'], function(err, postData) { if(err || (!postData && !postData.content)) { return callback(err || new Error('[[error:invalid-pid]]')); } @@ -61,6 +61,7 @@ SocketModules.composer.push = function(socket, pid, callback) { callback(null, { pid: pid, + uid: postData.uid, body: postData.content, title: results.topic.title, topic_thumb: results.topic.thumb, diff --git a/src/topics/posts.js b/src/topics/posts.js index 6653c15934..c6e7829e5d 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -10,7 +10,8 @@ var async = require('async'), user = require('../user'), favourites = require('../favourites'), posts = require('../posts'), - privileges = require('../privileges'); + privileges = require('../privileges'), + meta = require('../meta'); module.exports = function(Topics) { @@ -130,7 +131,7 @@ module.exports = function(Topics) { } // Username override for guests, if enabled - if (parseInt(postObj.uid, 10) === 0 && postObj.handle) { + if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postObj.uid, 10) === 0 && postObj.handle) { postObj.user.username = postObj.handle; } } From 23b9b21cddcf8e1a03473662b0bf70335f9154e0 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 31 Dec 2014 21:27:41 -0500 Subject: [PATCH 30/31] better handling of guest handles in frontend, #2569 --- public/src/modules/composer.js | 4 +++- src/postTools.js | 1 + src/socket.io/modules.js | 3 ++- src/socket.io/posts.js | 2 ++ src/topics/create.js | 5 +++++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index a07af172f1..5a80616517 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -155,6 +155,7 @@ define('composer', [ push({ pid: pid, uid: threadData.uid, + handle: threadData.handle, title: $('
    ').html(threadData.title).text(), body: threadData.body, modified: false, @@ -228,7 +229,8 @@ define('composer', [ allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain, isTopic: isTopic, - showHandleInput: (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)) && config.allowGuestHandles + showHandleInput: (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)) && config.allowGuestHandles, + handle: composer.posts[post_uuid] ? composer.posts[post_uuid].handle || '' : undefined }; parseAndTranslate(template, data, function(composerTemplate) { diff --git a/src/postTools.js b/src/postTools.js index c9e7648ea9..b157ead650 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -46,6 +46,7 @@ var winston = require('winston'), posts.setPostFields(data.pid, { edited: Date.now(), editor: data.uid, + handle: data.handle, content: postData.content }, next); }, diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 7c74530e3e..71a44f36bd 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -35,7 +35,7 @@ SocketModules.composer.push = function(socket, pid, callback) { if (err || !canRead) { return callback(err || new Error('[[error:no-privileges]]')); } - posts.getPostFields(pid, ['content', 'tid', 'uid'], function(err, postData) { + posts.getPostFields(pid, ['content', 'tid', 'uid', 'handle'], function(err, postData) { if(err || (!postData && !postData.content)) { return callback(err || new Error('[[error:invalid-pid]]')); } @@ -62,6 +62,7 @@ SocketModules.composer.push = function(socket, pid, callback) { callback(null, { pid: pid, uid: postData.uid, + handle: parseInt(meta.config.allowGuestHandles, 10) ? postData.handle : undefined, body: postData.content, title: results.topic.title, topic_thumb: results.topic.thumb, diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index de66e6a5e3..97b8e7ea74 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -260,6 +260,7 @@ SocketPosts.edit = function(socket, data, callback) { // uid, pid, title, content, options postTools.edit({ uid: socket.uid, + handle: data.handle, pid: data.pid, title: data.title, content: data.content, @@ -274,6 +275,7 @@ SocketPosts.edit = function(socket, data, callback) { websockets.in('topic_' + results.topic.tid).emit('event:post_edited', { pid: data.pid, + handle: data.handle, title: results.topic.title, isMainPost: results.topic.isMainPost, tags: results.topic.tags, diff --git a/src/topics/create.js b/src/topics/create.js index 25053ef819..9fd7354b17 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -260,6 +260,11 @@ module.exports = function(Topics) { postData.user = results.userInfo[0]; postData.topic = results.topicInfo; + // Username override for guests, if enabled + if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postData.uid, 10) === 0 && data.handle) { + postData.user.username = data.handle; + } + if (results.settings.followTopicsOnReply) { threadTools.follow(postData.tid, uid); } From 7b49effc14b1ce803dbeedade86a9778d4051ff1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 1 Jan 2015 13:05:58 -0500 Subject: [PATCH 31/31] minor tweaks --- src/postTools.js | 9 ++++++--- src/posts/create.js | 5 ++--- src/topics/create.js | 2 +- src/topics/posts.js | 10 ++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/postTools.js b/src/postTools.js index b157ead650..692878734a 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -43,12 +43,15 @@ var winston = require('winston'), async.parallel({ post: function(next) { - posts.setPostFields(data.pid, { + var d = { edited: Date.now(), editor: data.uid, - handle: data.handle, content: postData.content - }, next); + }; + if (data.handle) { + d.handle = data.handle; + } + posts.setPostFields(data.pid, d, next); }, topic: function(next) { var tid = postData.tid; diff --git a/src/posts/create.js b/src/posts/create.js index 198b9baae4..a235b2f3f0 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -14,7 +14,6 @@ module.exports = function(Posts) { Posts.create = function(data, callback) { var uid = data.uid, tid = data.tid, - handle = data.uid ? null : data.handle, // Only guests have handles! content = data.content, timestamp = data.timestamp || Date.now(); @@ -51,8 +50,8 @@ module.exports = function(Posts) { postData.ip = data.ip; } - if (handle) { - postData.handle = handle; + if (parseInt(uid, 10) === 0 && data.handle) { + postData.handle = data.handle; } plugins.fireHook('filter:post.save', postData, next); diff --git a/src/topics/create.js b/src/topics/create.js index 9fd7354b17..415c3b955a 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -155,7 +155,7 @@ module.exports = function(Topics) { }); }, topicData: function(next) { - Topics.getTopicsByTids([postData.tid], 0, next); + Topics.getTopicsByTids([postData.tid], uid, next); } }, next); }, diff --git a/src/topics/posts.js b/src/topics/posts.js index c6e7829e5d..66fbb6bc4a 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -108,15 +108,15 @@ module.exports = function(Topics) { posts.getPostIndices(postData, uid, next); } }, function(err, results) { - if(err) { + if (err) { return callback(err); } - postData = postData.map(function(postObj, i) { + postData.forEach(function(postObj, i) { if (postObj) { postObj.index = results.indices[i]; postObj.deleted = parseInt(postObj.deleted, 10) === 1; - postObj.user = _.clone(results.userData[postObj.uid]); + postObj.user = parseInt(postObj.uid, 10) ? results.userData[postObj.uid] : _.clone(results.userData[postObj.uid]); postObj.editor = postObj.editor ? results.editors[postObj.editor] : null; postObj.favourited = results.favourites[i]; postObj.upvoted = results.voteData.upvotes[i]; @@ -135,9 +135,7 @@ module.exports = function(Topics) { postObj.user.username = postObj.handle; } } - - return postObj; - }).filter(Boolean); + }); callback(null, postData); });