3

I'm trying to make an admin section on my website. The admin can go to the admin page and see a table of the users in his group. I only want to publish a the users that are in that admin's group. E.g. he is in the group ['soccer'] but I wouldn't want him to see all users in the ['hockey'] group. A user can be in more than one group but I should be able to figure that out once I understand how to query it.

Anyways, here's what I have so far:

Meteor.publish('users', function() {

  groups = Roles.getGroupsForUser(this.userId)
  group = groups[0] //Just get the first group for now

  // If the user is an admin of any group (the first one in the array)
  if (Roles.userIsInRole(this.userId, ['admin'], group)) {
    return Meteor.users.find({roles: {$in: groups}}, 
                             {fields: {emails: 1,
                                       createdAt: 1,
                                       roles: 1
                                      }
    });
  } else {
    console.log('null')
    return null;
  }
});

My subscription in my router:

Meteor.subscribe("users")

Now when I replace:

{roles: {$in: groups}} 

with just:

{}

It works, but my table contains all users instead of just the users of a group. What query should I put to make this work? I'm using the roles package by alanning.

ekad
  • 14,436
  • 26
  • 44
  • 46
William
  • 161
  • 2
  • 9
  • This? http://alanning.github.io/meteor-roles/classes/Roles.html#method_getUsersInRole – fuzzybabybunny Jun 16 '15 at 20:15
  • Yes! so if I do return Roles.getUsersInRole(['admin'], 'group') it works however, how do I return only the fields specified above instead of the entire user document? – William Jun 16 '15 at 20:42
  • Well, someone correct me if I'm wrong, but the getUsersInRole returns a Mongo cursor, and I don't believe that you can attach a projection onto a cursor, a projection being the part that limits the fields returned. However, the source code for getUsersInRole is here so you can see if you can edit it's projection part. http://alanning.github.io/meteor-roles/files/roles_roles_common.js.html#l421 – fuzzybabybunny Jun 16 '15 at 20:57
  • You could try this and this: http://docs.mongodb.org/manual/reference/method/cursor.map/#cursor.map and http://stackoverflow.com/a/26081558 – fuzzybabybunny Jun 16 '15 at 21:04
  • Oh hey what do you know - http://docs.meteor.com/#/full/map – fuzzybabybunny Jun 16 '15 at 21:09

3 Answers3

3

you can do a simple query and add your fields, sorting, what you want ;-)

Meteor.users.find({'roles.__global_roles__':'admin'}).fetch()

__global_roles is the default group. replace it with the group you need.

http://docs.mongodb.org/manual/tutorial/query-documents/#match-an-array-element

trayan.dev
  • 87
  • 4
2

This code has about a 5% chance of working out of the box because I have no collection to test this on, I have no way of running this code, I don't have the roles package, I don't have your users database, and I've never done .map on a cursor before, haha.

/server/methods.js

Meteor.methods({

  returnAdminUsers: function(){
    var results = [];

    var results = Roles.getUsersInRole(['admin']).map(function(user, index, originalCursor){
      var result = {
        _id: user._id,
        emails: user.emails,
        createdAt: user.createdAt,
        roles: user.roles
      };
      console.log("result: ", result);
      return result;
    });

    console.log("all results - this needs to get returned: ", results);

    return results;
  }

})

/client/somethingsomething.js

Meteor.call("returnAdminUsers", function(error, result){
  if(error){
    console.log("error from returnAdminUsers: ", error);
  } else {
    Session.set("adminUsers", result);
  }
});

And then in your helpers:

Template.somethingsomething.helpers({
  adminUsers: function(){ return Session.get("adminUsers") }
});

/client/somethingsomething.html

{{#each adminUsers}}
  The ID is {{_id}}
{{/each}}
fuzzybabybunny
  • 5,146
  • 6
  • 32
  • 58
  • The only modification I had to make is adding 'groups[0]' to make it work. `var results = Roles.getUsersInRole(['admin'], groups[0])`. What's printed to the server console is the following: `result: { _id: '3pZpC2aLvfftFrEiv', createdAt: Tue Jun 16 2015 10:33:17 GMT-0400 (Eastern Daylight Time), roles: [ 'admin' ] }` So that part works. But now my subscription in my router seems to be hanging. I have the subscription in a waitOn: function. Do you think it has something to do with the result that's returned? – William Jun 17 '15 at 14:35
  • Cool, so it returns an array of multiple users with the _id, emails, createdAt, and roles fields? – fuzzybabybunny Jun 17 '15 at 14:37
  • In my case, I have 4 users so the output I see is: `result: { _id: 'znjvvNu7pFFqyfwPD', roles: [ 'admin' ] } result: { _id: '662Gtkbey2tAJ4Mrp', roles: [ 'admin' ] } result: { _id: 'G4XsexA3a4fy7QQhM', roles: [ 'admin' ] } result: { _id: '3pZpC2aLvfftFrEiv', roles: [ 'admin' ] }`(I removed the createdAt field to reduce my character count for my comment) – William Jun 17 '15 at 15:01
  • Also, when I check `Meteor.users.find().fetch()` in the browser console, I only see one user, the currently logged in user. – William Jun 17 '15 at 15:09
  • Correct. Meteor by default only publishes a small subset of the data for the currently logged in user to the client. To get the data for other users you need to call Meteor Methods on the server and bring that info back to the client. – fuzzybabybunny Jun 17 '15 at 15:29
  • Exactly, so isn't that what we're doing here? I'm returning the result of this subset in Meteor.publish(), and subscribing to this publication on the client side. That's why I thought I should be able to see the 4 users, like on the server's console.log output. So this leads me to believe there's something wrong with the subscription. Is th result variable that I'm returning in the right format? Does it return a cursor like Meteor.users.find() does? – William Jun 17 '15 at 15:50
  • Okay I think I'm getting closer to the answer. I was mistaking `return result` as the actual return statement for the publish function. So I added `return results` outside of the .map function. Now I get the following error: `Error: Publish function returned an array of non-Cursors`. I'll check the documentation and see if there's something I can use to make this work. – William Jun 17 '15 at 15:59
  • Answer edited. Sorry, still working on it. `Meteor.publish` can only return a cursor or an array of cursors, not an object. – fuzzybabybunny Jun 17 '15 at 18:21
  • `Meteor.publish('users', function() { groups = Roles.getGroupsForUser(this.userId) if (Roles.userIsInRole(this.userId, ['admin'], groups[0])) { var results = []; var results = Roles.getUsersInRole(['admin'], groups[0]).map(function(user,index, originalCursor){ return user }) var users = _.pluck(results, '_id') return Meteor.users.find({_id: {$in: users}}, {fields: {createdAt: 1, roles: 1, emails: 1}}) } else { console.log('null') return null; } });` this seems to work. I pluck the _id from the users and return the users subset with that. – William Jun 17 '15 at 18:31
  • I've edited my answer to just turn it all into a Meteor Method that returns the array of users. – fuzzybabybunny Jun 17 '15 at 18:35
  • sorry it's probably a bit hard to read in the comment formatting. Now this should probably be another question but there's another thing that is bothering me is that when you use the alanning:roles package with the groups functionality, the roles are defined in the document as follows: `{roles: {soccer: ['admin']}}`. but let's say I have a 'superadmin'. And their roles look like: `{roles: {soccer: ['admin', 'superadmin'], hockey: ['admin', 'superadmin']}}`, then an admin in the soccer group will be able to see the existence of the hockey group since that will be published, which I dont want. – William Jun 17 '15 at 18:39
  • That could work. I'll try it out and let you know how it works out. Btw thanks for all the help, I really appreciated it! – William Jun 17 '15 at 18:45
  • Yeah, might want to just make that into a separate question. – fuzzybabybunny Jun 17 '15 at 18:45
  • Would this be reactive still? – William Jun 17 '15 at 18:46
  • No. The Meteor.call will only run when you explicitly call it. Does it need to be reactive? – fuzzybabybunny Jun 17 '15 at 18:54
  • Not necessarily. It would be nicer for the admin but it's not like the admin will be on this user table page and need to see live updates of the user table. It won't be changing too often anyway. – William Jun 17 '15 at 19:03
  • Right. If you *really* need this to be reactive you're going to have to figure out that `Meteor.publish` thing to publish a cursor or an array of cursors for the roles since cursors are reactive data sources. You could make my code above reactive to changes in the `Users` collection but it would look really really goofy. – fuzzybabybunny Jun 17 '15 at 19:10
1

If you are using meteor-tabular/TabularTables this answer can help:

Display only users with a specific role in meteor-tabular table

If you want to check all the rules of such a group:

  Meteor.users.find({
    'roles.myrole': {$exists : true}
  });

Or with just a few:

  Meteor.users.find({
    'roles.myrole': {$in: ['viewer', 'editor']}
  });
Community
  • 1
  • 1
Liko
  • 2,130
  • 19
  • 20