Updating Meteor.users from client - javascript

I have a form that tried to update meteor.users with extra information about users with the following helper
Template.Profile.events({
'submit form': function(e) {
e.preventDefault();
var post = {
firstName: $(e.target).find('[name=firstname]').val()
};
Meteor.users.update( { _id: Meteor.userId() }, { $set: { 'firstName': post.firstName }} );
}
});
however, i get update failed: Access denied
Another question is, I am wondering whether I should do extra update straight to Meteor.users collection or should I have a seperate collection to store these data.
thanks

Due to the fact that you are trying to set an attribute directly on the base user object, you are receiving the 'Access denied' error. According to the Meteor documentation for Meteor.users:
By default, the current user's username, emails, and profile are
published to the client.
This means that you can update any of those user attributes, but if you want to add additional ones, it is best to add them to one of these already existing fields. I would suggest adding something like `firstName' to the profile attribute. In this case, your code would look something like this:
Meteor.users.update({_id: Meteor.userId()}, {$set: {'profile.firstName': post.firstName}});

Related

Write an object containing an array of objects to a mongo database in Meteor

In my user collection, I have an object that contains an array of contacts.
The object definition is below.
How can this entire object, with the full array of contacts, be written to the user database in Meteor from the server, ideally in a single command?
I have spent considerable time reading the mongo docs and meteor docs, but can't get this to work.
I have also tried a large number of different commands and approaches using both the whole object and iterating through the component parts to try to achieve this, unsuccessfully. Here is an (unsuccessful) example that attempts to write the entire contacts object using $set:
Meteor.users.update({ _id: this.userId }, {$set: { 'Contacts': contacts}});
Thank you.
Object definition (this is a field within the user collection):
"Contacts" : {
"contactInfo" : [
{
"phoneMobile" : "1234567890",
"lastName" : "Johnny"
"firstName" : "Appleseed"
}
]
}
This update should absolutely work. What I suspect is happening is that you're not publishing the Contacts data back to the client because Meteor doesn't publish every key in the current user document automatically. So your update is working and saving data to mongo but you're not seeing it back on the client. You can check this by doing meteor mongo on the command line then inspecting the user document in question.
Try:
server:
Meteor.publish('me',function(){
if (this.userId) return Meteor.users.find(this.userId, { fields: { profile: 1, Contacts: 1 }});
this.ready();
});
client:
Meteor.subscribe('me');
The command above is correct. The issue is schema verification. Simple Schema was defeating the ability to write to the database while running 'in the background'. It doesn't produce an error, it just fails to produce the expected outcome.

Meteor Accounts add extra fields

How do I add extra fields to the Meteor Accounts?
Ex: Profile Picture, Header Picture field etc...
I know how to change the password Accounts.setUsername(userId, username);
But how would I add an extra field to the Accounts?
You need to update the users collection in mongo. The way to access it is by using
Meteor.users.update(Meteor.userId(), {
$set:{
profile: {
picture: 'url-to-picture'
}
}});
According to documentation, anything that need to be updated by user should be stored under the profile key
profile: an Object which the user can create and update with any data.
Do not store anything on profile that you wouldn't want the user to
edit unless you have a deny rule on the Meteor.users collection.

Attempting to update Meteor.users results in "Access Denied"

I am working on a Meteor project and I am trying to update a specific user's profile. However, I always receive
update failed: Access denied
I have the user id stored in the variable user_id, and my code looks like
Meteor.users.update({_id: user_id}, {$set: {'profile.participate_studies': updated_user_list}})
However, if I do
Meteor.users.update({_id: Meteor.userId()}, {$set: {'profile.participate_studies': updated_user_list}})
The update works. This makes me think there is some issue with the id I have stored. Is there a difference between what Meteor.userId() returns and a string containing an id? I know the id is correct because I can access it in shell with the id.
I have looked at Updating Meteor.users from client, but I am not trying to update the edit the base user object. I really appreciate all of your help!
You are allowed to update your own profile, so when you do
Meteor.users.update({_id: Meteor.userId()}, {
$set: {'profile.participate_studies': updated_user_list}
})
You update your own profile (Meteor.userId() returns an id of currently logged in user).
And when you update profile of some other user, you receive and Access denied error.
You need to set allow rules for Meteor.users:
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;
*/
}
});
Full documentation for allow rules: http://docs.meteor.com/#/full/allow
TIP: You can use shorthand for updating by id:
Meteor.users.update(Meteor.userId(), {...})

meteor.js - where to call a lookup to a collection

So I have two collections, 'Posts' and 'Locations' and a simple html form for users to add themselves into the Posts collection. Once added, I display a list of all users but also want to add some extra data to this list if they have attributes which appear in the Locations collection.
So, as an example, the user form has Name, Age, Location and the Locations collection has information on the location they live in, such as "London, Capital, UK". If a user from London enters their details I'd like to display the details they entered on the form along with "You live in a Capital city" if their location features in the Locations collection.
So, some code to insert the details from the form:
post_submit.js:
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
name: $(e.target).find('[id=usersName]').val().toUpperCase();,
age: $(e.target).find('[id=age]').val(),
location: $(e.target).find('[id=location]').val().toUpperCase(),
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return throwError(error.reason);
Router.go('postPage', {_id: result._id});
});
}
});
and in posts.js
Meteor.methods({
postInsert: function(postAttributes) {
var post = _.extend(postAttributes, {
submitted: new Date(),
});
var postId = Posts.insert(post);
return {
_id: postId
};
}
});
But how do I define the subscription to the Locations collection and where do I call the 'lookup' to that collection in order to lookup the location and then enable me to display that in some other portion of the page. I'm sure this is pretty simple, I'm struggling to connect the dots though.
You'll have to make a new publication that publishes the Posts and then publishes the Locations as a "dependency". There is a package that makes this very simple. copleykj:simple-publish will help you accomplish this. The samples in the readme should be enough to get you started.

meteor-useraccounts and alanning meteor-roles - Check role on sign-in

Is it possible to check users role on Sign in, and than if user is in role "admin" to display one page, and if is it in role "basic-user" to display another page ( go to another route).
Lets have a look at the Routes section of the documentation for the useraccounts:iron-routing package.
this should solve your problem
AccountsTemplates.configureRoute('signIn', {
redirect: function(){
var user = Meteor.user();
if (user && Roles.userIsInRole(user, ['admin'])) {
Router.go('admin');
}
else {
Router.go('home');
}
}
});
Be careful to check you can access the user roles field from the client side: lets check the allanning:roles official documentation
To define a default role for a user I use this:
// server
Accounts.onLogin(function(user) {
var user = user.user;
var defaultRole = ['student'];
if (!user.roles){
Roles.addUsersToRoles(user, defaultRole)
};
})
I'm using meteor-useraccounts and alanning meteor-roles packages and that work fine for me.
If I'm not outdated (and a look at http://docs.meteor.com/#/full/meteor_users suggests I'm not) there is no built in way for user roles. There should be some extensions for that task with which you could go and depending on what you choose you would have to check their documentation.
However it's not very hard to implement a own simple roles logic in Meteor:
First in your Accounts.onCreateUser function give your users object a new attribute role and assign them to a default role. If you don't have a Accounts.onCreateUser yet create one server side. It could look like something like this:
Accounts.onCreateUser(function(options, user) {
// Add an user roles array
user.roles = ["default-user"];
if (options.profile)
user.profile = options.profile;
return user;
}
Next you would need to implement some logic to add "admin" or whatever you like for trusted users to their roles array. That's up to you and for the beginning if you don't have dozens of admins you could also choose to do that manually in your MongoDB.
Now make sure you publish the new attribute of your user object to the currently logged in user. To do so make use of Meteor.publish with null as first parameter to address the current user like so:
Meteor.publish(null, function () {
return Meteor.users.find({_id: this.userId}, {fields: {
'roles': 1,
'profile': 1, // You probably want to publish the profile of a user to himself
// And here would be any other custom stuff you need
}});
});
And with that you are already in a state where you can do individual styling or routing client side. For example you could do something like this:
if (Meteor.user().roles.indexOf("admin") > -1) {
// Route for admins!
}
You could also parse through your array and add the user roles as class to your body element to for example only show certain elements to admins. This could be done this way:
Meteor.user().roles.forEach(function(role){
$('body').addClass(role);
});
Note that this will only be "cosmetic" but you can also implement real security with that as long as you do it server side. So if you want a Meteoer subscription or Meteor method only to be available for admins add something like this to it:
var requestingUser = Meteor.users.findOne({ '_id': this.userId});
if (!_.contains(requestingUser.roles, "admin")) {
// Terminate the pbulish function or Meteor method here when there is no "admin" role
return;
}
As said, this only works sever side and should be at the start of Meteor.publish functions or at the start of functions within Meteor.methods blocks.

Categories