Add extra user field - meteor

In my Meteor app I use the default accounts package, which gives me the default login and registration functionality. Now I want to add an extra field to user, say nickname, and for the logged in user the possibility to edit this information.
For editing the profile I suppose I should be doing something like this:
Template.profileEdit.events({
'submit form': function(e) {
e.preventDefault();
if(!Meteor.user())
throw new Meteor.Error(401, "You need to login first");
var currentUserId = this._id;
var user = {
"profile.nickname": $(e.target).find('[name=nickname]').val()
};
Meteor.users.update(currentUserId, {
$set: user
}, function(error){
if(error){
alert(error.reason);
} else {
Router.go('myProfile', {_id: currentUserId});
}
});
}
});
But I doesn't store the info if I look in Mongo. Also when showing the profile, {{profile.nickname}} returns empty. What is wrong here?
Edit: added collections\users.js to show permissions:
Meteor.users.allow({
update: function (userId, doc) {
if (userId && doc._id === userId) {
return true;
}
}
});
Meteor.users.deny({
update: function(userId, user, fieldNames) {
return (_.without(fieldNames, 'profile.nickname').length > 0);
}
});

Yeah, I believe that should do the job, although I haven't actually run the code. The idea is certainly right.
The main things to be aware of are:
The necessity to allow the user doc to be edited from the client with an appropriate Meteor.users.allow() block on the server, assuming you're going to remove the "insecure" package (which you need to before doing anything in production).
The fact that "by default the server publishes username, emails, and profile", so you'll need to write a Meteor.publish function on the server and subscribe to it if you want to expose any other fields within the user document to the client once you've removed the "autopublish" package (which again, you really should).

Related

Meteor - registerHelper, can't access user value

I am using meteor to build a website similar to reddit. I used the account-ui package for user accounts, but I had trouble getting the user value. I was able to create an account and login, but when I post a comment, it shows that I am an anonymous user. Here's the code -
Template.registerHelper('getUser', function(user_id) {
var user = Meteor.users.findOne({_id: user_id});
if (user) {
return user.username;
}
else {
return "anon";
}
});
post a comment -
Template.comment_form.events({
"submit .js-save-comment-form":function(event){
if (Meteor.user()) {
// here is an example of how to get the comment out of the form:
var comment = event.target.comment.value;
console.log("The comment is: "+comment);
Comments.insert({
website: Router.current().params._id,
comment: comment,
createdOn: new Date(),
user: Meteor.user()._id
});
event.target.comment.value = "";
}
else {
alert('You need to be logged in to submit comments!');
}
return false; // stop the form submit from reloading the page
}
});
I was logged in as user test, but when I posted a comment, it shows it's posted by anon, which means the server didn't return the user value
See if this makes the usernames show up. It's not the production solution, but it should give you some idea what's going on if it works.
Meteor.publish(null, function () {
if (!this.userId) return null;
return Meteor.users.find({},{fields: {'username': 1, '_id': 1}});
});

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 Collection and Security

I am curious if I'm setting up the allow statement on this collection correctly. I'm using aldeed:autoform and aldeed:collection2.
Below is the snapshot of a issue-collection.js from a toy project.
Is this the proper way to set up allow checks? Do these run on both client (for minimongo) and server? Specifically, on most update calls, is return !!userId && (doc.userId == userId); enough to ensure the user is logged in AND the logged in user is the owner of the document?
Clarification and actual question: Do the allow and deny methods run on BOTH server and client? Or do they run only on the client?
Issues = new Mongo.Collection("issues");
if (Meteor.isClient){
Meteor.subscribe("issues");
}
if(Meteor.isServer){
Meteor.publish('issues', function () {
return Issues.find({}, {limit: ServerSettings.maxSubscribe});
});
}
Issues.attachSchema(new SimpleSchema({
issue: {
type: String,
label: "Describe the issue you noticed",
max:256
}
}));
//SECURITY - Allow Callbacks for posting
Issues.allow({
insert: function(userId, doc) {
/* Throw in some defaults. */
doc.userId = userId;
doc.sumbitDate = new Date();
doc.date = new Date();
// only allow posting if you are logged in
return !! userId;
},
update: function(userId, doc) {
// only allow updating if you are logged in
return !!userId && (doc.userId == userId);
},
remove: function(userID, doc) {
//only allow deleting if you are owner
return doc.submittedById === Meteor.userId();
}
});
Remember, allow/deny comes from the client. And you can't trust anything that comes from the client (userId, date, etc).
What you want to do is call a Meteor.method from the client & _.extend the document with trustworthy data from the server.
As an example, rewrite that code in the browser console & change the value for the userId.
Check out Discover Meteor blog to learn more (it's probably the best source for learning basic patterns) https://www.discovermeteor.com/blog.

How to redirect New User to different page one time only?

ok so when my app starts after the first time you sign up I want to redirect the user to a different page.
In my server code I have this
Accounts.onCreateUser(function(options, user) {
Hooks.onCreateUser = function () {
Meteor.Router.to('/newUser');
}
});
but I want users to be redirected to another page if they have already been on more then once so I have this in my client code, it always defaults to the client, what am I doing wrong?
Hooks.onLoggedIn = function () {
Meteor.Router.to('/new');
}
If you want to redirect a signed user, simply set up a flag within user object denoting whether he was redirected:
Hooks.onLoggedIn = function (){
if(!Meteor.user()) return;
if(!Meteor.user().returning) {
Meteor.users.update(Meteor.userId(), {$set: {returning: true}});
Meteor.Router.to('/new');
}
}
Make sure to publish & subscribe to the returning field of user collection!
If you want similar functionality for all visitors, use cookies.
Hooks.onLoggedIn = function (){
if(!Cookie.get('returning')) {
Cookie.set('returning', true);
Meteor.Router.to('/new');
}
}
Here's the handy package for that: https://atmosphere.meteor.com/package/cookies
Create collection 'ExistingUsers' to keep track.
if (Meteor.isClient) {
Deps.autorun(function () {
if(Meteor.userId())
//will run when a user logs in - now check if userId is in 'ExistingUsers'
//If not display message and put userId in 'ExistingUsers'
});
Alternatively add field 'SeenMessage' to User collection

Meteor accounts framework signed/logged in event

I'm using the accounts-ui package and would like to process some javascript as soon as the user is logged in/and or registered.
Is there an event that gets called as soon as the user signs in?
The raw login API (eg loginWithFacebook, loginWithPassword, etc) has a callback that fires when login is complete, but this is not currently exposed through accounts-ui. This may change.
A potential workaround, inspired by Werner's suggestion, but also taking page load into account:
var oldUserId = undefined;
Meteor.autorun(function() {
var newUserId = Meteor.userId();
if (oldUserId === null && newUserId) {
console.log('The user logged in');
} else if (newUserId === null && oldUserId) {
console.log('The user logged out');
}
oldUserId = Meteor.userId();
});
You could check the result of the Meteor.userId() function inside Meteor.autorun():
Meteor.autorun(function() {
if (Meteor.userId()) {
console.log('The user logged in');
}
});
Just to give an alternative; I monkey patched the callback function. It looks a bit more complex because the credentialRequestCompleteHandler requires a function that returns a function but besides that its a plain monkey patch. Stick this in main.js or something that gets processed late and only once. I hope it helps for future reference.
var orgCallback = Accounts.oauth.credentialRequestCompleteHandler;
Accounts.oauth.credentialRequestCompleteHandler = function(callback){
return function (credentialTokenOrError) {
var tmpFunc = orgCallback(callback);
tmpFunc(credentialTokenOrError);
alert("do your own thing here");
}
}

Resources