How to group users roles in Meteor Js - meteor

Can someone help me to solve this problem? I want to assign multiple users to become admin for each category.
For example I have collections:
User:{emily, bob, roberts, alex}
Category:{Laptop, Computer, Tablet, Network}
How can I assigned roles for emily to become admin for Laptop and Computer? Besides, Bob and Alex are admin for Network?
Your kindness and help really appreciated. Thanks.. :)

I know this is a bit old. But if someone is searching similar.
Lets assume you have your users in users-collection like this.
var emily = Meteor.users.findOne({name: "Emily"});
var bob = Meteor.users.findOne({name: "Bob"});
var roberts = Meteor.users.findOne({name: "Roberts"});
var alex = Meteor.users.findOne({name: "Alex"});
Then you can put users to roles like this:
Roles.addUsersToRoles(emily._id, ['Laptop', 'Computer'], 'admin');
Roles.addUsersToRoles(bob._id, ['Network'], 'admin');
Roles.addUsersToRoles(alex._id, ['Network'], 'admin');
In if you look at users-collection's Emily-record you see Emily's roles:
"roles" : {
"admin" : [
"Laptop",
"Computer"
]
}
Then, if you want to check if user is admin for some group you can try it:
If
Roles.userIsInRole(emily._id, 'Laptop', 'admin') // == true
Roles.userIsInRole(emily._id, 'Network', 'admin') // == false
Roles.userIsInRole(bob._id, 'Laptop', 'admin') // == false
Roles.userIsInRole(bob._id, 'Network', 'admin') // == true
or from templates
{{#if isInRole 'Computers' 'admin'}}
{{/if}}
If you have categories in db
var network_category = Categories.findOne({..})
Roles.userIsInRole(bob._id, network_category.name, 'admin') // == true

Related

Discord.JS, How to use one discord button to allow the purchase of various server roles

Sorry for the poorly worded title, I'll try to explain as best as I can. I am creating a role shop command using the new discord-buttons module, and came across a problem, to my understanding I would have to create a button for each individual role, in order for someone to buy it. After searching through documentation, I'm still a bit stumped. Here's some example code I put together to show what I'm trying to do:
let embedRed = new Discord.MessageEmbed()
.setTitle('Red Role')
.setColor('#c46413')
.addField('**Price**', '10,000', true)
.addField('**Color Hex:**', '#ffffff',true)
let embedBlue = new Discord.MessageEmbed()
.setTitle('Blue')
.setColor('#c2a289')
.addField('**Price**', '10,000', true)
.addField('**Color Hex:**', '#ffffff',true)
///Buttons
let buttonBuyRed = new MessageButton()
.setStyle('green')
.setLabel('Buy Red Role')
.setID('role_buy1')
let buttonBuyBlue = new MessageButton()
.setStyle('green')
.setLabel('Buy Blue Role')
.setID('role_buy2')
//embeded messages being sent
message.channel.send({ buttons: [buttonBuyRed], embed: embedRed});
message.channel.send({ buttons: [buttonBuyRed], embed: embedBlue});
//What happens if buttons are pressed
client.on('clickButton', async (role_buy1) => {
if (button.id === 'roley_buy1') {
button.channel.send(`${button.clicker.user.tag} bought red role`);
db.push(message.author.id, `${message.guild.roles.cache.get('role id here')}`) //role being pushed to user's inventory
}
});
client.on('clickButton', async (role_buy2) => {
if (button.id === 'role_buy2') {
button.channel.send(`${button.clicker.user.tag} bought blue role`);
db.push(message.author.id, `${message.guild.roles.cache.get('role id here')}`) //role being pushed to user's inventory
}
});
Since I have about 25 different roles that I want users to be able to purchase, it's quite a hassle to create a button for each role, I am looking for a way to just use one single "buy_role" button that works for all available roles.
If I didn't explain something clearly, please let me know, any help is appreciated!
So i came to a conclusion, this code works, but if your guild has a lot of roles, it would throw an error "Invalid form body"
const rolesInGuild = message.guild.roles.cache.array(); //creating array from collection of roles in a guild
const buttons = []; // an empty array for our buttons
for (const role of rolesInGuild) { // creating a loop inorder to create a button for every roles in rolesInGuild Array
const button = new MessageButton()
.setStyle('red') // default: blurple
.setLabel(`${role.name}`) // default: NO_LABEL_PROVIDED
.setID(`${role.id}`);
buttons.push(button); // button id is the same as role id so its unique!
}
console.log(rolesInGuild);
console.log(buttons);
await message.channel.send('test', { buttons: buttons }); // sending our buttons
bot.on('clickButton', async(button) => {
for (const btn of buttons) {
if (btn.custom_id == button.id) {
const role = button.guild.roles.cache.get(btn.custom_id);
const member = message.guild.members.cache.get(button.clicker.user.id);
member.roles.add(role);
}
}
});
you could add specific roles to the array rolesInGuild in this format
[{ name: 'rolename', id: 'roleid' }] instead of every roles in the guild ( I wasn't sure what your goal was)
you have ${message.guild...}, that’s the wrong if you have an error, so try this:
client.on('clickButton', async (button) => {
if (button.id === 'roley_buy1') {
button.channel.send(`${button.clicker.user.tag} bought red role`);
db.push(message.author.id, `${button.guild.roles.cache.get('role id here')}`)
//role being pushed to user's inventory
button.clicker.roles.add('your role id');
// or you can find the role using
const role = button.guild.roles.cache.find(role => role.name == 'rolename');
button.clicker.roles.add(role);
}
});```

NoSQL Firebase denormalization

So I just recently learn about denormalization and I am just trying to figure out the best way to structure my firebase database. What I currently have is I am displaying a users class schedule with the classes course number and intructor name. Firebase looks like this for me:
-root
-users
-uid1
-uid2
-schedules
-uid1 : true (match with users uid)
-uid2 : true (match with users uid)
-class
-classKey1
-users
-uid1 : true
-uid2 : true
-courseKey1 : true
-intructorKey1 : true
-classKey1
-users
-uid1
-courseKey2 : true
-intructorKey2 : true
-courses
-courseKey1
-intructors
-intructorKey1 : true
-intructorKey2 : true
-courseKey2
-similar to above
-intructors
-intructorKey1
-courses
-courseKey1: true
-courseKey2: true
-intructorKey2
-similar to above
Now, that is the basic structure of what I am working with, excluding all the unneeded information. Lets say I want to display all of the the schedule of the currently logged in user I will need to do the following.
_af.auth.subscribe(authState => {
let userID = authState.uid;
_af.database.list('/schedule/' + userID ).subscribe(schedule =>{
this.schedule = schedule; //global variable
this.filterClasses(); call function
});
});
_af.database.list('/class').subscribe(classes => {
this.masterClasses = classes; //gloabal variable
this.filterClasses();
});
Now because its all done asynchronously the only way I could think to do this is call the filterClasses function inside of each subscription.
filterClasses(): void {
if (this.scheduleKeys != null && this.masterClasses.length > 0) {
this.outputClasses = this.masterClasses.filter(value => {
let bool = false;
for (let i = 0; i < this.scheduleKeys.length; i++) {
if (this.scheduleKeys[i].$key === value.$key) {
bool = true;
break;
}
}
return bool;
});
this.outputClasses.forEach((value, i) => {
_af.database.object('courses/' + value.courseKey).subscribe(v => {
this.outputClasses[i]['courseNum'] = v.course;
})
_af.database.object('intructors/' + value.intructorKey).subscribe(v => {
this.outputClasses[i]['intructorName'] = v.name;
})
})
}
}
As you can see when I am done filtering my master list of classes into just the ones in my schedule I also now need to go and grab the course number firebase and the intructors name. Both of which require me to call firebase again. So to try and reduce researching firebase which seems to be causing some problems due to being async should I instead be denormalizing my data? Should in the class root of firebase instead of storing just courseKey1 should I store all the data associated to couseKey1? However, this will cause rooting in firebase because if I structure this way now in my courses root when I say the intructors key instead of just saving the key I would save everything which is just another level deeper firebase goes.
This is kind of an indirect answer:
Denormalizing data is a good thing but in same cases it may not be necessary.
For example in this case a student wants to know his courses, so you know the direct path to the student; users/uid_0 and then maybe a query for the semester you are interested in.
Here's a structure that goes with your courses and instructors
users
uid_0
schedules
-Yiaiajsdaoiojasd
semester: 1
courses
course1: true
course3: true
-Jyuhus99jsijskkss
semester: 2
courses
course2: true
course4: true
uid_1
schedules
-Yjs909k0s0s0ks0s
semester: 1
courses
course1: true
course3: true
-Jyuhus99jsijskkss
semester: 2
courses
course2: true
course4: true
This really isn't very deep since once you read in the user node, you have specific paths to the other data you need: /courses/courseKey1/instructors for example, then followed by instructors/instructor1/name.
Where you get into trouble is if you DON'T know the direct path to the data and have to pile up queries on queries - that's where denormalizing is most effective. Also, queries have lot of overhead vs just observing a node at a path you know.

meteor-roles package, adding roles

Meteor.publish(null, function (){
var users = [ Meteor.users.find({})];
email="Osoba5#qwe.qwe";
if(users == email ){
users = Meteor.this.userId;
Roles.createRole('admin');
Roles.setUserRoles(users, 'admin');
}else{
users = Meteor.this.userId;
Roles.createRole(['']);
Roles.setUserRoles(users,['']);
}
return Meteor.users.find({});
});}
The goal is when creating user there are two user acc one should have admin and the other is normal user without role. But when I sign in with acc who should get admin role I can't do the things I specified for acc with admin role. I'm missing something and I can't figure what, Thank you in advance for any help you can give.
There are multiple mistakes in this code:
Firstly, you are setting users to be equal to an array and then checking if it is equal to a string. This will always return false.
Secondly, Meteor.this.userId, should be this.userId instead.
Thirdly, change this line:
var users = [ Meteor.users.find({})];
to either:
var users = Meteor.users.find({}); // users is a cursor
or:
var users = Meteor.users.find({}).fetch(); // users is an array

Publication of items where User is in group (Alanning Roles and Publications)

I am using Alanning Roles to maintain a set of groups/roles for the users of my application. When a user creates an "Application", I generate a new role for them as the app_name + UUID, then add that as a group with the roles of Admin to the user that created it. I can then use the combination of the generated group name plus either the Admin or Viewer roles to determine which Applications the user has rights to see and/or edit.
The issue that I am having is that I can't figure out a good way to get the publication to only publish the things the user should see. I know that, by default at least, publications are not "reactive" in the way the client is, and they they are only reactive for the cursors they return. But, in my code I create the group/role first, add it to the user, then save the "Application", which I thought would rerun my publication, but it did not:
Meteor.publish('myApplications', function(groups) {
if (this.userId) {
console.log('Running myApplications publication...');
console.log('Found roles for user ' + this.userId + ': ', Roles.getGroupsForUser(this.userId));
return Applications.find({group: {$in: Roles.getGroupsForUser(this.userId)}});
} else {
//console.log("Skipping null user");
return null;
}
});
But, contrary to what I thought would happen (the whole publication method would re-run), I am guessing what really happens is that only the Cursor is updates. So for my next attempt, I added the mrt:reactive-publications package and simply got a cursor to the Meteor.users collection for the user, thinking that would "trigger" the publication to re-run when the user gets updated with the new group/role, but that didn't work.
I have this finally working by simply passing in the groups for the user:
Meteor.publish('myApplications', function(groups) {
if (this.userId) {
if (!groups || groups.length === 0) {
groups = Roles.getGroupsForUser(this.userId);
}
console.log('Running myApplications publication...');
console.log('Found roles for user ' + this.userId + ': ', Roles.getGroupsForUser(this.userId));
return Applications.find({group: {$in: groups}});
} else {
//console.log("Skipping null user");
return null;
}
});
And then I just call the publication like Meteor.subscribe('myApplications', Roles.getGroupsForUser(Meteor.userId())) in my route's waitOn, but this would mean that any client could call the same publication and pass in any groups they like, and potentially see documents they were not intended to see. That seems like a pretty large security flaw.
Is there a better way to implement this such that the client would not be able to coax their way to seeing stuff not theirs? I think the only real way would be to gather the groups on the publication side, but then it breaks the reactivity.
After sifting through a bunch of docs and a few very helpful stack posts, this is the alternative I came up with. Works like a charm!
My objective was to publish 'guest' users' info to the group admins for approval/denial of enhanced permissions.
Meteor.publish('groupAdmin', function(groupId) {
// only publish guest users info to group admins
if(Roles.userIsInRole(this.userId, ['group-admin'], groupId)) {
// I can't explain it but it works!
var obj = {key: {$in: ['guest']}};
var query = {};
var key = ('roles.' + groupId);
query[key] = {$in: ['guest']};
return Meteor.users.find(query, {
fields: {
createdAt: 1,
profile: 1
}
});
} else {
this.stop();
return;
}
});
Reference: How to set mongo field from variable
& How do I use a variable as a field name in a Mongo query in Meteor?

Choosing a KEY for an object store

O., this may be a relatively simple question, but it's one that I couldn't figure out (both by testing code and googling).
I have Simple Login with Email/Password enabled, and so when they login, the email used will be converted into a md5_hash.
I've created a "Users" object store, which currently looks like this:
users
|____ Bob
|__ md5_hash: 8120jf283
|__ username: Bob
|____ Alice
|__ md5_hash: 2938njv29
|__ username: Alice
|____ Chris
|__ md5_hash: 230948mv2
|__ username: Chris
My question is: How can I change the "schema" so that it stores the users by their md5_hash, instead of by their username? You can see above that it's storing the users by Bob, Alice, and Chris. It would work better for me if I could store them by their md5_hash so that it would like something like this:
Users
|___ 02938492
|__ md5_hash: 02938492
|__ username: Bob
Here's the code that I'm using to create a user and $save it into the Firebase database:
var ref = new Firebase(FIREBASE_URL + 'users');
var users = $firebase(ref);
function(authUser, username) {
users[username] = {
md5_hash: authUser.md5_hash,
username: username,
$priority: authUser.uid
};
users.$save(username).then(function() {
*do something here in callback*
});
I've tried 2 different things that didn't work. I tried using
users[authUser.md5_hash] = {...};
and I also tried
users.$save(authUser.md5_hash)
Neither of these work. Nothing gets saved to the Firebase database. It's perplexing to me because I'm almost 100% sure that it's one of those 2 parts that determines how the key is saved to firebase, but it doesn't do anything.
ULTIMATELY, what I'm trying to do is be able to "grab" the username value after a person logs in. I will be displaying their username on a navbar in a kind of "welcome Bob" fashion. However, when a user logs in, there is not yet any correlation between their md5_hash and their username. I have to be able to grab the username from the "users" table, and because I don't know their username, and the "users" table is currently indexed or keyed by their username, I have no way to get it...
A quick design question as well (if you have time):
Also, this doesn't have to do with the above question, but it's somewhat related. Just a quick question about designing my firebase "schema". If you have time, it'd be great if you could take a quick read below:
As I mentioned before, I'm using Simple Login with email/password. When a user logs in, I have access to the md5_hash of their email. Thus, to associate other user information with that login, I've created the "User table". I'm trying to connect the Login/Authentication with the said table using the md5_hash (because this is unique to each user), and then store other useful user data in it such as username, age, etc.
Is this how one would design their firebase schema? I'm used to using some sort of GUID or index in SQL, and I figured that since an md5_hash is unique, I could similarly use it to "index" my firebase users table. I just don't know if doing it this way will cause some problems in the future.
Thank you very much for your time. Any help or advice on this would be much appreciated! :)
With Simple Login, the user is created with a unique id:
var fbRef = new Firebase(URL);
var auth = new FirebaseSimpleLogin(fbRef);
auth.createUser(email, password, function(error, user) {
if( error ) throw error;
console.log('unique id', user.uid);
});
You can utilize that for your unique identifier. If that is insufficient, you can obtain a unique ID from the Firebase push() command:
var fbRef = new Firebase(URL);
var uniqueId = fbRef.push().name();
fbRef.child('users/'+fbRef).set(userData);
// or even shorter
var uniqueId = fbRef.push(userData).name();
With AngularFire, you can simply call $add instead of $save to create unique ids:
var users = $firebase(fbRef);
users.$add( userData ).then(function(ref) {
console.log('unique id', ref.name());
});

Resources