From 45383a9630afd02ab95c902f84e04f0d08d832f2 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 31 Aug 2013 17:47:03 -0400 Subject: [PATCH] rewrite & api: use mongoose populate to replace group.members array with popualte member objects. this puts a load on the front-end, we'll need to optimize! also added some duplicates sanitization. --- assets/js/controllers/groupsCtrl.js | 4 +- src/controllers/groups.js | 46 ++++++++++------------ src/models/group.js | 61 ++++++++++++++++++++--------- src/models/user.js | 39 ++++++------------ views/options/groups/group.jade | 28 ++++++------- views/options/groups/index.jade | 6 +-- 6 files changed, 96 insertions(+), 88 deletions(-) diff --git a/assets/js/controllers/groupsCtrl.js b/assets/js/controllers/groupsCtrl.js index 32db899921..d75388476a 100644 --- a/assets/js/controllers/groupsCtrl.js +++ b/assets/js/controllers/groupsCtrl.js @@ -4,7 +4,9 @@ habitrpg .controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', '$location', function($scope, $rootScope, Groups) { - $scope.groups = Groups.query(); + $scope.groups = Groups.query(function(groups){ + $scope.members = groups.members; + }); $scope.party = true; } ]) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 8c4c75cb06..0e10d90c38 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -18,34 +18,23 @@ var api = module.exports; api.getGroups = function(req, res, next) { var user = res.locals.user; - /*TODO should we support non-authenticated users? just for viewing public groups?*/ + var usernameFields = 'auth.local.username auth.facebook.first_name auth.facebook.last_name auth.facebook.name auth.facebook.username'; - return async.parallel({ + // First get all groups + async.parallel({ party: function(cb) { - async.waterfall([ - function(cb2) { - Group.findOne({type: 'party', members: {'$in': [user._id]}}, cb2); - }, function(party, cb2) { - var fields, query; - party = party.toJSON(); - query = {_id: { - '$in': party.members, - '$nin': [user._id] - } - }; - fields = 'profile preferences items stats achievements party backer auth.local.username auth.facebook.first_name auth.facebook.last_name auth.facebook.name auth.facebook.username'.split(' '); - fields = _.reduce(fields, (function(m, k, v) {m[k] = 1;return m;}), {}); - User.find(query, fields, function(err, members) { - party.members = members; - cb2(err, party); - }); - } - ], function(err, members) { - cb(err, members); - }); + Group + .findOne({type: 'party', members: {'$in': [user._id]}}) + .populate({ + path: 'members', + //match: {_id: {$ne: user._id}}, //fixme this causes it to hang?? + select: 'profile preferences items stats achievements party backer ' + usernameFields + }) + .exec(cb); }, guilds: function(cb) { - Group.find({type: 'guild', members: {'$in': [user._id]}}, cb); + Group.find({type: 'guild', members: {'$in': [user._id]}}).populate('members', usernameFields).exec(cb); +// Group.find({type: 'guild', members: {'$in': [user._id]}}, cb); }, tavern: function(cb) { Group.findOne({_id: 'habitrpg'}, cb); @@ -59,8 +48,13 @@ api.getGroups = function(req, res, next) { members: 1 }, cb); } - }, function(err, results) { + }, function(err, results){ if (err) return res.json(500, {err: err}); + + // Remove self from party (see above failing `match` directive in `populate` + var i = _.findIndex(results.party.members, {_id:user._id}); + if (~i) results.party.members.splice(i,1); + res.json(results); - }); + }) }; \ No newline at end of file diff --git a/src/models/group.js b/src/models/group.js index 046c267ffe..aac86729d5 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -1,18 +1,10 @@ -var GroupSchema, Schema, helpers, mongoose, _; +var mongoose = require("mongoose"); +var Schema = mongoose.Schema; +var helpers = require('habitrpg-shared/script/helpers'); +var _ = require('lodash'); -mongoose = require("mongoose"); - -Schema = mongoose.Schema; - -helpers = require('habitrpg-shared/script/helpers'); - -_ = require('lodash'); - -GroupSchema = new Schema({ - _id: { - type: String, - 'default': helpers.uuid - }, +var GroupSchema = new Schema({ + _id: {type: String, 'default': helpers.uuid}, name: String, description: String, leader: { @@ -43,7 +35,9 @@ GroupSchema = new Schema({ Number: Number, 'default': 0 }, + websites: Array, chat: Array, + /* # [{ # timestamp: Date @@ -58,10 +52,41 @@ GroupSchema = new Schema({ balance: Number, logo: String, leaderMessage: String -}, { - strict: 'throw' -}); +}, {strict: 'throw'}); + + +/** + * Derby duplicated stuff. This is a temporary solution, once we're completely off derby we'll run an mongo migration + * to remove duplicates, then take these fucntions out + */ +function removeDuplicates(doc){ + // Remove duplicate members + if (doc.members) { + var uniqMembers = _.uniq(doc.members); + if (uniqMembers.length != doc.members.length) { + doc.members = uniqMembers; + } + } + + if (doc.websites) { + var uniqWebsites = _.uniq(doc.websites); + if (uniqWebsites.length != doc.websites.length) { + doc.websites = uniqWebsites; + } + console.log(doc.websites); + } +} + +GroupSchema.pre('save', function(next){ + removeDuplicates(this); + next(); +}) + +GroupSchema.methods.toJSON = function(){ + var doc = this.toObject(); + removeDuplicates(doc); + return doc; +} module.exports.schema = GroupSchema; - module.exports.model = mongoose.model("Group", GroupSchema); \ No newline at end of file diff --git a/src/models/user.js b/src/models/user.js index 328591f15b..bb1f57894e 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -1,14 +1,9 @@ -var Schema, UserSchema, helpers, mongoose, _; +var mongoose = require("mongoose"); +var Schema = mongoose.Schema; +var helpers = require('habitrpg-shared/script/helpers'); +var _ = require('lodash'); -mongoose = require("mongoose"); - -Schema = mongoose.Schema; - -helpers = require('habitrpg-shared/script/helpers'); - -_ = require('lodash'); - -UserSchema = new Schema({ +var UserSchema = new Schema({ _id: { type: String, 'default': helpers.uuid @@ -274,20 +269,16 @@ UserSchema.post('init', function(doc) { UserSchema.methods.toJSON = function() { - var doc; - doc = this.toObject(); + var doc = this.toObject(); doc.id = doc._id; _.each(['habit', 'daily', 'todo', 'reward'], function(type) { - /* we use _.transform instead of a simple _.where in order to maintain sort-order*/ - - return doc["" + type + "s"] = _.transform(doc["" + type + "Ids"], function(result, tid) { - return result.push(doc.tasks[tid]); + // we use _.transform instead of a simple _.where in order to maintain sort-order + doc["" + type + "s"] = _.transform(doc["" + type + "Ids"], function(result, tid) { + result.push(doc.tasks[tid]); }); - /*delete doc["#{type}Ids"]*/ - + //delete doc["#{type}Ids"] }); - /*delete doc.tasks*/ - + //delete doc.tasks doc.filters = {}; return doc; }; @@ -296,16 +287,12 @@ UserSchema.methods.toJSON = function() { # FIXME - since we're using special @post('init') above, we need to flag when the original path was modified. # Custom setter/getter virtuals? */ - - UserSchema.pre('save', function(next) { this.markModified('tasks'); - /*our own version incrementer*/ - + //our own version incrementer this._v++; - return next(); + next(); }); module.exports.schema = UserSchema; - module.exports.model = mongoose.model("User", UserSchema); \ No newline at end of file diff --git a/views/options/groups/group.jade b/views/options/groups/group.jade index 3cbc96164d..db8885e25b 100644 --- a/views/options/groups/group.jade +++ b/views/options/groups/group.jade @@ -1,8 +1,8 @@ -a.pull-right.gem-wallet(ng-show='group.type=="guild" && group.id!="habitrpg"', rel='popover', data-trigger='hover', data-title='Guild Bank', data-content='Gems which your Guild leader can use for prizes in the upcoming Challenges feature.', data-placement='bottom', data-html='true') +a.pull-right.gem-wallet(rel='popover', data-trigger='hover', data-title='Guild Bank', data-content='Gems which your Guild leader can use for prizes in the upcoming Challenges feature.', data-placement='bottom', data-html='true') // span.task-action-btn.tile.flush.neutral .Gems - | {{gems(group.balance)}} Guild Gems + | {{group.balance / 4 | number:0 }} Guild Gems .row-fluid .span4 h3 {{group.name}} @@ -43,24 +43,24 @@ a.pull-right.gem-wallet(ng-show='group.type=="guild" && group.id!="habitrpg"', r accordion-group(heading='Members') table.table.table-striped - tr(ng-repeat='memberId in group.members') + tr(ng-repeat='member in group.members') td // allow leaders to ban members - div(ng-show='and(equal(group.leader,_user.id),not(equal(_user.id,memberId)))') + div(ng-show='group.leader == user.id && user.id!=member._id') // {{#with group.members[$index]}} a(x-bind='click:removeAt', data-refresh='true', data-confirm='Boot this member?') - i.icon-ban-circle(rel='tooltip', title='Boot Member') + i.icon-ban-circle(tooltip='Boot Member') // {{/}} - a(data-toggle='modal', data-target='#avatar-modal-{{memberId}}') - span(ng-class='{"badge badge-info": group.leader==memberId}') - | {{username(_members[memberId].auth, _members[memberId].profile.name)}} + a(data-toggle='modal', data-target='#avatar-modal-{{member._id}}') + span(ng-class='{"badge badge-info": group.leader==member._id}') + | {{username(member.auth, member.profile.name)}} td - | ({{memberId}}) + | ({{member._id}}) // {#with group as :group} - form.form-inline(x-bind='submit:groupInvite', data-type='{group.type}') - .alert.alert-danger(ng-show='_groupError') {_groupError} + form.form-inline(x-bind='submit:groupInvite', data-type='{{group.type}}') + .alert.alert-danger(ng-show='_groupError') {{_groupError}} .control-group - input.input-medium(type='text', placeholder='User Id', value='{_groupInvitee}') + input.input-medium(type='text', placeholder='User Id', value='{{_groupInvitee}}') input.btn(type='submit', value='Invite') // {/} @@ -89,7 +89,7 @@ a.pull-right.gem-wallet(ng-show='group.type=="guild" && group.id!="habitrpg"', r div(ng-show='_editing.leaderMessage[group.id]') a.pull-right(x-bind='click:toggleLeaderMessageEdit', data-gid='{{group.id}}') i.icon-ok - textarea(cols='3', placeholder='Message from group leader') {group.leaderMessage} + textarea(cols='3', placeholder='Message from group leader') {{group.leaderMessage}} div(ng-hide='_editing.leaderMessage[group.id]') a.btn.pull-right(x-bind='click:toggleLeaderMessageEdit', data-gid='{{group.id}}') Edit leader message table(ng-show='group.leaderMessage') @@ -100,7 +100,7 @@ a.pull-right.gem-wallet(ng-show='group.type=="guild" && group.id!="habitrpg"', r .popover.static-popover.fade.right.in .arrow h3.popover-title {{username(_members[group.leader].auth,_members[group.leader].profile.name)}} - .popover-content {group.leaderMessage} + .popover-content {{group.leaderMessage}} h3 Chat include ./chat-box diff --git a/views/options/groups/index.jade b/views/options/groups/index.jade index 129c878c72..318d8ff895 100644 --- a/views/options/groups/index.jade +++ b/views/options/groups/index.jade @@ -12,9 +12,9 @@ div(ng-controller='GroupsCtrl') .tab-content #groups-party.tab-pane.active Party - div(ng-show='groups.party.id', ng-controller='PartyCtrl') - app:groups:group(group='{groups[_party.id]}') - div(ng-hide='groups.party.id') + div(ng-show='group._id', ng-controller='PartyCtrl') + include ./group + div(ng-hide='group._id') div(ng-show='user.invitations.party') // #with required for the accept/reject buttons // {#with _user.invitations.party as :party}