Twitter or normal account username conflict - Meteor - meteor

The user can create an account with their e-mail or twitter. The problem with the code below is, it doesn't let the user with normal account create a post.
Posts.insert({
text: text,
createdAt: new Date(),
owner: Meteor.userId(),
username: Meteor.user().username,
twitname: Meteor.user().profile.name,
upvoters: [],
votes: 0,
commenters: [],
comments: 0,
});
If I erase
twitname: Meteor.user().profile.name,
both of the accounts can create posts again but it doesn't show the username for the posts created by a twitter account.

I have found a solution to this. Hope this helps someone.
What we do is telling "username" to get data from one or another like below.
Inside server, Meteor.methods:
username: function(){
return Meteor.user().username || Meteor.user().profile.name
}
And our insert method:
Posts.insert({
text: text,
createdAt: new Date(),
owner: this.userId,
username: Meteor.user().username,
});
Inside HTML:
{{username}}
PS: I have installed Sign in with Google, Twitter and FB. The solution above works with all of them.
EDIT:
I found out that its better to use onCreateUser
Example:
Accounts.onCreateUser(function(options,user){
if (typeof(user.services.facebook) != "undefined") {
username: user.services.facebook.name;
}
if (typeof(user.services.google) != "undefined") {
user.username = user.services.google.name;
}
//etc.
})
This way, you don't have to check which service and write anything else as its always "username" for all services. You just insert the correct data to the same place.

Related

How to add extra attributes to users collection?

I am using Accounts.createUser to add new users to the database, but the problem is that, not all the attributes are added.
Here is my code to add new user:
import {Accounts} from 'meteor/accounts-base';
Template.addingUser.events({
'submit #addUser': function (e, t) {
e.preventDefault();
Session.set('name', t.find('#name').value);
Session.set('email', t.find('#email').value);
Session.set('telephoneOffice', t.find('#telephoneOffice').value);
Session.set('telephoneHouse', t.find('#telephoneHouse').value);
Session.set('salary', t.find('#salary').value);
let userId = Accounts.createUser({
username: Session.get('name'),
password: "123456",
email: Session.get('email'),
telephoneOffice: Session.get('telephoneOffice'),
telephoneHouse: Session.get('telephoneHouse'),
employeeSalary: Session.get('salary'),
annualLeave: 14
}, function (err) {
if (err)
console.log(err);
else
console.log('It worked...');
});
Accounts.sendEnrollmentEmail(userId);
}
});
only the name, email, and password are added.
How do I include the other information as well such as telephoneOffice?
You need to pass the extra data inside the profile object.
Accounts.createUser({
username: Session.get('name'),
password: "123456",
email: Session.get('email'),
profile: {
telephoneOffice: Session.get('telephoneOffice'),
telephoneHouse: Session.get('telephoneHouse'),
employeeSalary: Session.get('salary'),
annualLeave: 14
}
...
Accounts.createUser does not accept custom arguments beyond username, email, password, and profile. The default functionality for passing custom user info is to pass those fields such as telephoneOffice as part of the profile object, which is copied to user.profile in the document inserted to the user collection.
For example:
let userId = Accounts.createUser({
username: Session.get('name'),
password: "123456",
email: Session.get('email'),
profile: {
telephoneOffice: Session.get('telephoneOffice'),
telephoneHouse: Session.get('telephoneHouse'),
employeeSalary: Session.get('salary'),
annualLeave: 14
}
});
Note that the user.profile fields are by default modifiable by users. So it's there by legacy, but Meteor actually recommends avoiding using it for storage.
If you want those fields to be on user instead of user.profile, What you can do is to pass your custom params on the profile object as above, and then override the default behavior using Accounts.onCreateUser. Something like this:
Accounts.onCreateUser(function(options, user) {
if (options.profile)
_.extend(user, options.profile);
return user;
});
See more info here: https://guide.meteor.com/accounts.html#custom-user-data

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 routing filter not working

i am trying to create an admin interface for my meteor project and for this i created a custom accounts register form which says
Accounts.createUser({
email: userEmail,
password: userPassword,
admin: true
})
and in my router.js code i have this
Router.route('/admin', {name: 'admin'})
var requireAdminLogin = function(){
if(!Meteor.user({admin: true})){
this.render('accessDenied')
}else{
this.next();
}
}
Router.onBeforeAction(requireAdminLogin, {only: 'admin'})
the problem is even when i change my register code to say that the new user signing up is not admin, i can still get to my admin page. Can anyone help? thank you
Meteor.user() doesn't take any arguments. You probably want:
if (Meteor.user() && Meteor.user().admin) {
// admin
} else {
// access denied
}
I also suspect that passing the admin option to Accounts.createUser doesn't do anything. On the server, you could do var userId = Accounts.createUser followed by Meteor.users.update(userId, {$set: {admin: true}});.
A package called houston:admin did exactly what I wanted to do.

How would I use Accounts.createUser in Meteor once, at first load, and then persist

I have thought of somehow using session, but that doesn't make much sense to me with a user that I always want to exist, and not be created over and over.
My issue is after a page refresh I get an error saying user already exists, I would like to know how to create a permanent user once.
This is in my server code.
Accounts.createUser({
username: "admin",
password: "password"
});
Try this.
Meteor.startup(function () {
if (Meteor.users.find().count() === 0) {
Accounts.createUser({
username: 'admin',
email: 'admin#admin.com',
password: 'password'
});
console.log('created user');
}
});

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