How can I add new field to Meteor.users()? - meteor

I have a button that inserts new user to Meteor.users().
In the server I have this method:
Meteor.methods({
'addUser': function(user) {
return Accounts.createUser(user)
}
})
And in the client (After button is clicked):
var newUser = {
email: t.find('#email').value,
password: t.find('#pwd').value,
profile: { name: t.find('#name').value, group: t.find('#userType').value },
roles: checkedRoles // I can successfully console.log(checkedRoles) which is an array of strings.
}
Meteor.call('addUser', newUser, function(error){
if(error){
sweetAlert(error)
} else {
sweetAlert('User Successfully Added')
}
})
Using the above code, the user is added but without the roles field.
My question is, how can I add the roles field to the newly added user?

Use alanning:roles package:
meteor add alanning:roles
then (in your server side method):
const userId = Accounts.createUser(user);
Roles.addUsersToRoles(userId, user.roles);

Related

Meteor SignUps Forbidden on Accounts.createUser

I'm getting the error "SignUps Forbidden" when i try to create a user account. Any ideas why?
My packages:
useraccounts:materialize
materialize:materialize
accounts-password
accounts-facebook
service-configuration
accounts-google
accounts-twitter
kadira:blaze-layout
msavin:mongol
kadira:flow-router
kevohagan:sweetalert
Client Code:
Template.register.events({
'click #register-button': function(e, t) {
e.preventDefault();
// Retrieve the input field values
var email = $('#email').val(),
firstName = $('#first-name').val(),
lastName = $('#last-name').val(),
password = $('#password').val(),
passwordAgain = $('#password-again').val();
// Trim Helper
var trimInput = function(val) {
return val.replace(/^\s*|\s*$/g, "");
}
var email = trimInput(email);
// If validation passes, supply the appropriate fields to the
// Meteor.loginWithPassword() function.
Accounts.createUser({
email: email,
firstName: firstName,
lastName: lastName,
password: password
}, function(error) {
if (error) {
return swal({
title: error.reason,
text: "Please try again",
showConfirmButton: true,
type: "error"
});
} else {
FlowRouter.go('/');
}
});
return false;
}
});
Server code
Accounts.onCreateUser(function(options, user) {
user.profile = options.profile || {};
user.profile.firstName = options.firstName;
user.profile.lastName = options.lastName;
user.profile.organization = ["Org"];
user.roles = ["User"];
return user;
});
UPDATE:
Here is a link to the repo
The problem seems to be on .....meteor\local\build\programs\server\packages. If i switch the value to false it's useless because it resets on every build.
// Client side account creation is disabled by default:
// the methos ATCreateUserServer is used instead!
// to actually disable client side account creation use:
//
// AccountsTemplates.config({
// forbidClientAccountCreation: true
// });
Accounts.config({
forbidClientAccountCreation: true
});
I had to remove the useraccounts:materialize in order to solve this problem
I don't think that the current accepted answer is the right one.
If you want to keep your packages yet override the setting you can change the value of Accounts._options.forbidClientAccountCreation in your code.
Set it to true if you want to prevent the account creation, or to false otherwise.

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?

How to get user ID during user creation in Meteor?

I am creating default users on the server with a Meteor startup function. I want to create a user and also verify his/her email on startup (I'm assuming you can only do this after creating the account).
Here's what I have:
Meteor.startup(function() {
// Creates default accounts if there no user accounts
if(!Meteor.users.find().count()) {
// Set default account details here
var barry = {
username: 'barrydoyle18',
password: '123456',
email: 'myemail#gmail.com',
profile: {
firstName: 'Barry',
lastName: 'Doyle'
},
roles: ['webmaster', 'admin']
};
// Create default account details here
Accounts.createUser(barry);
Meteor.users.update(<user Id goes here>, {$set: {"emails.0.verified": true}});
}
});
As I said, I assume the user has to be created first before setting the the verified flag as true (if this statement is false please show a solution to making the flag true in the creation of the user).
In order to set the email verified flag to be true I know I can update the user after creation using Meteor.users.update(userId, {$set: {"emails.0.verified": true}});.
My problem is, I don't know how to get the userID of my newly created user, how do I do that?
You should be able to access the user id that is returned from the Accounts.createUser() function:
var userId = Accounts.createUser(barry);
Meteor.users.update(userId, {
$set: { "emails.0.verified": true}
});
Alternatively you can access newly created users via the Accounts.onCreateUser() function:
var barry = {
username: 'barrydoyle18',
password: '123456',
email: 'myemail#gmail.com',
profile: {
firstName: 'Barry',
lastName: 'Doyle'
},
isDefault: true, //Add this field to notify the onCreateUser callback that this is default
roles: ['webmaster', 'admin']
};
Accounts.onCreateUser(function(options, user) {
if (user.isDefault) {
Meteor.users.update(user._id, {
$set: { "emails.0.verified": true}
});
}
});

Meteor: inserting a session variable value into a field upon user creation

This is branching from my previous question sent earlier Retrieve value from html 'value' attribute. I am now trying to insert a session variable value into field named 'userType' upon user creation. I have kept the insecure package so I can immediately perform Meteor.users.find().count(); in the console. So far, the users are not being created.
Am I inserting the session variable value the correct way, should this session value be inserted server side with Accounts.onCreateUser?
The client js
Template.joinForm.events({
'submit .form-join': function(e, t) {
e.preventDefault();
var firstName = t.find('#firstName').value,
lastName = t.find('#email').value,
email = t.find('#email').value,
password = t.find('#password').value,
username = firstName + '.' + lastName,
profile = {
name: firstName + ' ' + lastName,
userType: selectedUserType
};
Accounts.createUser({
email: email,
username: username,
password: password,
profile: profile
}, function(error) {
if (error) {
alert(error);
} else {
Router.go('/');
}
});
}
});
I have made the 'userType' session variable global, please see as follows...
Template.authJoinType.events({
'click div.join-type-inner': function(e, tmpl) {
userType = $(e.target).attr("value");
Session.set('userType', userType);
selectedUserType = Session.get('userType');
console.log(selectedUserType);
}
});
createUser takes an options object with at most four fields: username, email, password, and profile. You are passing a fifth field which is being ignored. In order to transmit the userType data to the server you will need to add it to the profile object in the call to createUser.
If it's important that userType exist on the root of the user document (instead of in the profile), you can modify it in the onCreateUser callback like this:
client
profile = {
name: firstName + ' ' + lastName,
userType: userType
};
server
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
if (options.profile.userType) {
user.userType = options.profile.userType;
delete options.profile.userType;
}
user.profile = options.profile;
}
return user;
});
Meteor.publish(null, function() {
// automatically publish the userType for the connected user
// no subscription is necessary
return Meteor.users.find(this.userId, {fields: {userType: 1}});
});

How to make sign-up invitation only?

Using Meteor accounts (and accounts-ui) is there an easy way to make new user sign-ups invitation only? For example by providing an invitation link or an invitation code.
The only thing related I could find in the Meteor documentation is Meteor.sendEnrollmentEmail but it doesn't solve my problem.
You can do this with the built in package, but I found it alot easier and powerful to roll a simple implementation.
You'll need to:
Create a collection, eg UserInvitations to contain the invites to become a user.
Create UI for making UserInvitations / insert some using meteor mongo
Using iron-router or similar create a route, eg:
Router.map ->
#route 'register',
path: '/register/:invitationId'
template: 'userRegistration'
data: ->
return {
invitationId: #params.invitationId
}
onBeforeAction: ->
if Meteor.userId()?
Router.go('home')
return
When the form in userRegistration is submitted - call
Accounts.createUser({invitationId: Template.instance().data.invitationId /*,.. other fields */})
On the server, make an Accounts.onCreateUser hook to pass through the invitationId from options to the user
Accounts.onCreateUser(function(options, user){
user.invitationId = options.invitationId
return user;
});
Also, on the server make an Accounts.validateNewUser hook to check the invitationId and mark the invitation as used
Accounts.validateNewUser(function(user){
check(user.invitationId, String);
// validate invitation
invitation = UserInvitations.findOne({_id: user.invitationId, used: false});
if (!invitation){
throw new Meteor.Error(403, "Please provide a valid invitation");
}
// prevent the token being re-used.
UserInvitations.update({_id: user.invitationId, used: false}, {$set: {used: true}});
return true
});
Now, only users that have a valid unused invitationId can register.
EDIT: Oct 2014 - Updated to use meteor 0.9.x API's
To do it with the built in stuff, you can plumb together the existing Accounts.sendEnrollmentEmail - however it's a little more complicated than the other solution given.
Using the example code below, call the enroll method as such:
Meteor.call('enroll', 'john.smith', 'js#harvard.edu', {name: 'John Smith'});
Meteor will then email the user a link (You can configure the template with Accounts.emailTemplates)
When they click the link, meteor calls the function passed to Accounts.onEnrollmentLink - in this case you can take them to a password setup page; but you have to mess around with their done callback.
Modify the following code, where it says INSERT XXX HERE ; then in your code call SomeGlobalEnrollmentObjectThing.cancel() if the user cancels, or SomeGlobalEnrollmentObjectThing.complete(theUsersNewPassword) if they submit the new password.
if (Meteor.isServer){
Meteor.methods({
"enroll": function(username, email, profile){
var userId;
check(username, String);
check(email, String); // Or email validator
check(profile, {
name: String
}); // your own schema
// check that the current user is privileged (using roles package)
if (!Roles.isInRole(this.userId, 'admin')){
throw new Meteor.Error(403);
}
userId = Accounts.createUser({
username: username,
email: email,
profile: profile
});
Accounts.sendEnrollmentEmail(userId);
}
});
} else {
// uses `underscore`, `reactive-var` and `tracker` packages
function Enrollment(){
this.computation = null;
this.token = new ReactiveVar(null);
this.password = new ReactiveVar(null);
this.cancelled = new ReactiveVar(false);
this.done = null;
this._bind();
}
_.extend(Enrollment.prototype, {
_bind: function(){
Accounts.onEnrollmentLink(_.bind(this.action, this));
},
reset: function(){
this.token.set(null);
this.password.set(null);
this.cancelled.set(false);
this.done = null;
if (this.computation !== null){
this.computation.stop();
this.computation = null;
}
},
cancel: function(){
this.cancelled.set(true);
},
complete: function(password){
this.password.set(password);
},
action: function(token, done){
this.reset();
this.token.set(token);
this.done = done;
this.computation = Tracker.autorun(_.bind(this._computation, this));
// --- INSERT REDIRECT LOGIC HERE [TAKE TO PASSWORD SETUP PAGE]--- //
},
_computation: function(){
var password;
if (this.cancelled.get()){
this.reset();
this.done();
// --- INSERT REDIRECT LOGIC HERE [USER CANCELLED]--- //
} else {
password = this.password.get();
if (password !== null){
Accounts.resetPassword(this.token.get(), password, _.bind(this._complete, this));
}
}
},
_complete: function(err){
// TODO - check if we were reset before callback completed
this.reset();
this.done();
if (err){
// --- INSERT REDIRECT LOGIC HERE [RESET FAILED] --- //
} else {
// --- INSERT REDIRECT LOGIC HERE [SUCCESS] --- //
}
}
});
SomeGlobalEnrollmentObjectThing = new Enrollment();
}
I have created a specific solution to this, since all the other solutions only allow you to explicitly create password-based accounts. The t3db0t:accounts-invite package allows account creation with any service only when you allow them, such as with an 'accept invitation' route. Live demo here.

Resources