How do you add extra data in currentUser object? - meteor

I am trying to add an extra information that is not in profile for the currentUser object in Meteor. I am thinking it is possible and the technique should be somewhere in meteor/alanning:roles.
e.g. if I have a organizations object in users e.g.
{
_id: ...
profile: { ... }
roles: [ ... ]
organizations: [ ... ]
}
I would like to see it when I do
{{ currentUser }}

To push an orgId onto the organizations array in the user object for example:
Meteor.users.update(_id,{$push: {organizations: orgId}});
As #Blaze Sahlzen says, you'll need to publish this field:
Meteor.publish('orgUsers',function(orgId){
return Meteor.users.find({organization: orgId},{fields: {organization: 1, profile: 1}});
});

Basically currentUser is a global helper in meteor which calls returns Meteor.user()
From meteor repo
Package.blaze.Blaze.Template.registerHelper('currentUser', function () {
return Meteor.user();
});
https://github.com/meteor/meteor/blob/2ccb7467c9bb5889a3c36739d2d8b59a0656961c/packages/accounts-base/accounts_client.js#L421
Meteor.user() function returns following data
user() {
var userId = this.userId();
return userId ? this.users.findOne(userId) : null;
}
https://github.com/meteor/meteor/blob/dc3cd6eb92f2bdd1bb44000cdd6abd1e5d0285b1/packages/accounts-base/accounts_common.js#L52
Coming to your question, if you want to add extra fields to currentUser just publish the data to client.
If you want to publish that field to users by default
Meteor.publish(null, function(argument){
if(this.userId){
return Members.findOne({userIds: this.userId}, { fields: { organizations: 1,... } });
}
this.ready()
});

Related

Meteor "update failed: Access denied" when user attempts to update their own profile?

Im adding a field to a user's account on creation. This is working fine:
Accounts.onCreateUser((options, user) => {
user.groups = [2];
return user;
});
I need to make a function that allows the user to change this. When I run this from the front-end I get an error "update failed: Access denied"
Meteor.users.update(
{ _id: Meteor.userId() },
{
$set: { groups: [4, 5] },
},
);
In server/main.js I have:
Meteor.publish('currentUser', function() {
return Meteor.users.find({ _id: this.userId }, { fields: { groups: 1 } });
});
Do not make db updates from the client directly. No client can ever be trusted.
Having said that, there are two ways to deal with this:
ONE
As per the documentation :
By default, the current user’s username, emails and profile are
published to the client. You can publish additional fields for the
current user with:
// Server
Meteor.publish('userData', function () {
if (this.userId) {
return Meteor.users.find({ _id: this.userId }, {
fields: { groups: 1 }
});
} else {
this.ready();
}
});
Meteor.users.allow({
update: function(userId, user) {
return true;
/**
* Don't use `return true` in production!
* You probably need something like this:
* return Meteor.users.findOne(userId).profile.isAdmin;
*/
}
});
// Client
Meteor.subscribe('userData');
Meteor allow rules
TWO
Define a meteor method in the server and have the server update the relevant data for you AFTER it does some validations. Server code is always trusted.
server.js
Meteor.method({
updateGroups: function(data){
// make changes to the user record
});

Add accountStatus to new users on app startup

I'm trying to add accountStatus to the users I create when I first run the application however it keeps crashing. accountStatus is not part of user.profile.
Can someone please look at my code and tell me what I'm doing wrong.
Thanks for any help.
Path: server.js
// run at Meteor app startup
Meteor.startup(function(options, user) {
// if users database is empty, seed these values
if(Meteor.users.find().count() < 1) {
// users array
var users = [
{firstName: 'Sam', lastName: 'Smith', email: 'sam#gmail.com', roles: ['is_student']},
];
// user creation
_.each(users, function(userData) {
// return id for use in roles assignment below
var userId = Accounts.createUser({
email: userData.email,
password: 'password',
profile: {
firstName: userData.firstName,
lastName: userData.lastName,
}
});
// verify user email
Meteor.users.update({ _id: userId }, { $set: { 'emails.0.verified': true } });
// add roles to user
Roles.addUsersToRoles(userId, userData.roles);
// add accountStatus and set to true
_.extend(userId, { accountStatus: true });
});
console.log('New users created!');
}
});
Look at this line:
_.extend(userId, { accountStatus: true });
And look at _.extend definition:
Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.
What this line is supposed to do?

Meteor reactive transform

I have a mobile app in development and I'm transforming one of the collections to get the user last seen time, avatar etc.
PlayerRecord.prototype = {
constructor : PlayerRecord,
getAssociatedUser: function () {
return Meteor.users.findOne( this.user_id );
},
lastSeenFormatted: function () {
var user = this.getAssociatedUser();
return (user && user.last_seen) ? user.last_seen : 'Never';
}
}
My problem is that, if the user last seen returns Never initially but then the user is seen, my string return over there is not updated...obviously.
How would you advise me to handle this situation?
Did you check whether any user had a value for last_seen? This field has to be explicitly published.
According to the Meteor docs (http://docs.meteor.com/#/full/meteor_user):
By default, the current user's username, emails and profile are
published to the client. You can publish additional fields for the
current user with:
// server
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'last_seen': 1}});
} else {
this.ready();
}
});
// client
Meteor.subscribe("userData");

Meteor.users.find() - how to filter by group in alanning:roles package

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.
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
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}}
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']}
});

What is wrong with my subscription method on my route?

Can someone see why the route is not subscribing to the publication. Profiles = new Meteor.Collection('profiles');
The mongo database does have documents in this collection, but the browser console still has a count of 0 in Profiles collection.
I am trying to tell the router, "subscribe to user-profile publication, when you are ready, render the 'profile' template. I also named the route 'profile.'
Now I have noticed that after typingsub = Meteor.subscribe('user-profile'); and then sub.ready(); I get the count of the collection. Otherwise the path is not subscribed. This behaviour has not occurred before.
lib/router.js
Router.plugin('loading', {loadingTemplate: 'Loading'});
Router.route('user/profile', {
name: 'profile',
waitOn: function () {
// return one handle, a function, or an array
return Meteor.subscribe('user-profile');
},
action: function () {
// this.ready() is true if all items returned from waitOn are ready
if (this.ready())
this.render('profile');
else
this.render('Loading');
}
});
server.js:
Meteor.publish('user-profile', function () {
return Profiles.find({userId: this.userId});
});
userId is a field in the Profiles collection. This profiles doc id is stored within the user.profile.experiences array for reference.
Meteor.userId is a function which returns the _id, not the _id itself, and you can't pass a function over DDP anyway. It should be:
waitOn: function () {
return Meteor.subscribe('user-profile', Meteor.userId());
}

Resources