I want to access users emails in from the client,
here is what I have done, in the server side:
Meteor.publish('userList', function (){
return Meteor.users.find({}, {fields:{emails: 1, profile: 1}});
});
In the client side:
Template.usersManagement.onCreated(function () {
var self = this;
self.autorun(function() {
self.subscribe('userList');
});
});
And the template helper to retrieve the users:
Template.usersManagement.helpers({
allUsers: function() {
console.log(Meteor.users.find({}).fetch())
return Meteor.users.find({}).fetch()
}
})
In the usersManagement template:
{{#each allUsers}}
<h1>{{this.emails.address}}</h1>
<h1>{{this.profile.name}}</h1>
{{/each}}
The users names are displayed but the email isn't and no errors showing in the console.
Here is the look on how the users stored in the database:
{
"_id" : "m7admvZc32Jr3SeiE",
"createdAt" : ISODate("2017-12-27T21:24:48.927Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$wv6KsRp6s91A.0mHH89Q0eT3jrZmJjKJhw8SIH9c8c8OpwMrXyGMC"
}
},
"emails" : [
{
"address" : "222#222.com",
"verified" : false
}
],
"profile" : {
"name" : "222",
"createdAt" : ISODate("2017-12-27T21:24:48.707Z"),
"updatedAt" : ISODate("2017-12-27T21:24:48.707Z"),
"group" : "admin"
},
"status" : {
"online" : false
}
}
My question is, how can I retrieve the emails of the users?
EDIT
Here is how the emails are retrieved (from the console):
There is another field (0) below the emails. I tried this.emails.0.address it didn't work (desperate attempt)
Your desperate attempt almost nailed it, try this:
this.emails.[0].address
An item in the Meteor.users collection store emails as an array, for you to be able to store more than one email-address per user. So since it is so, the right way would be:
<h1>{{this.emails[0].address}}</h1>
Related
i am using firebase realtime DB with my ionic app and using angularfire2. I have data stored like:
"users" : {
"230A3lKQqWh0TczOGP8sbtMVpuF2" : {
"email" : "xxx#gmail.com",
"isAdmin" : false,
"name" : "Santosh Yadav",
"pic" : "https://graph.facebook.com/xxx/picture"
},
"oSEYj0zrkhPCk9r7uwyOOkHcqe53" : {
"email" : "yyy#gmail.com",
"isAdmin" : true,
"name" : "Vik Kumar",
"pic" : "https://graph.facebook.com/xxx/picture"
},
"tl3uvseaBeWVYFCTwSngUqcSokX2" : {
"email" : "zzz#gmail.com",
"isAdmin" : false,
"name" : "Neeti Singhal",
"pic" : "https://graph.facebook.com/xxx/picture"
}
}
We ended up to store more user data like date of birth and date of aniversary and when we try to update it as below:
createUser(user:User){
console.log('start of create user')
var payload = {
"name": user.name,
"email": user.email,
"pic" : user.pic,
"dob" : user.dob,
"anivDate" : user.anivDate,
}
return this.db.object('/users/' + user.uid).set(payload).then(
(resp) => console.log("user created")
).catch(
(err) => console.log("issues creating user:" + err)
)
}
It fails with error:
ERROR Error: Reference.set failed: First argument contains undefined in property 'users.oSEYj0zrkhPCk9r7uwyOOkHcqe53.anivDate'
at validateFirebaseData (validation.js:113)
at validation.js:140
at forEach (obj.js:46)
I understand the error that it is unable to set a property anivDate that does not exist in my firebase realtime DB. This will fix if i manually add the property into the db manually. But that is not a practical fix. So, what is the solution here?
To define the fix: i am expecting to add a new property if that does not exist else just update it.
For a quick fix, use the || operator to set a default value on undefined elements. Usage:
var payload = {
"anivDate": user.anivDate || "1970-01-01",
...
};
This sets anivDate to user.anivDate if it exists, 1970-01-01 if it does not. (This works, but in my opinion it is better practice to not have a anivDate property on a user if no such value exists, and check for that value's existence when accessing the data.)
For a more scalable/reliable fix, use Firebase's realtime database triggers to add async validating or default values to your new columns.
This is my first post. I will try and be complete. I am creating a app in Meteor which uses google oAuth. I am using the following packages:
accounts-ui
accounts-google
service-configuration
google-config-ui
In my login.js I have:
Template.login.events({
'click #googleLoginButton': function() {
Meteor.loginWithGoogle(
{ requestPermissions: ['email', 'profile'],
requestOfflineToken: 'true'
}
);
}
});
I get the following in mongo:
db.users.find({}).pretty();
{
"_id" : "9TjGEjEj4ocFhwHtS",
"createdAt" : ISODate("2017-10-11T17:38:07.400Z"),
"services" : {
"google" : {
"accessToken" : "ya29.-REDACTED-T9z",
"idToken" : "eyJhbGcj-REDACTED-LTg",
"scope" : [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
],
"id" : "107113228066746203535",
"email" : "XXXXXXy#XXXX.com",
"verified_email" : true,
"name" : "XXX XXX",
"given_name" : "XXX",
"family_name" : "XXX",
"picture" : "https://lh4.googleusercontent.com/.../photo.jpg",
"locale" : "en",
"gender" : "male",
"refreshToken" : "1/HG-REDACTED-oLq0USutc"
},
"resume" : {
"loginTokens" : [
{
"when" : ISODate("2017-10-11T21:47:38.471Z"),
"hashedToken" : "2lmOK-REDACTED-ptAyDnWo="
}
]
}
},
"profile" : {
"name" : "XXX XXX"
}
}
In the Server/init.js I have: (I am passing the google project info via settings.json)
configureGoogle = function(config) {
ServiceConfiguration.configurations.remove({
service: "google"
});
ServiceConfiguration.configurations.insert({
service: "google",
clientId: googleConfig.clientId,
secret: googleConfig.secret
});
return;
};
if (googleConfig) {
console.log('Got settings for google', googleConfig)
configureGoogle(googleConfig);
}
So things seem to be working. I can login / out and when I login I get new tokens. I use the accessToken for api calls in other places. The problem I have is that after a hour the token expires and the API calls start to fail.
I would like to refresh the accessToken just before it expires and cant seem to figure out how. I would also like to force a logout after 24 hours so the token does not refresh forever.
Any assistance if appreciated.
Update #1
Thanks to Derek Brown below for pointing me in the right direction. That got me to looking for a meteor google api. I found this one: percolate:google-api
and according to it doc it does what I was looking for:
If the user's access token has expired, it will transparently call the exchangeRefreshToken method to get a new refresh token.
I then ran into a error where the expiresAt Didnt exist in my mongodb and things were not working. I then found this post: Google-API which suggested:
server/publish.js
Meteor.publish(null, function() {
return Meteor.users.find(this.userId, { fields: {
'services.google.accessToken': 1,
'services.google.expiresAt': 1
}});
});
I also made one change from code above:
client/login.js
Template.login.events({
'click #googleLoginButton': function() {
Meteor.loginWithGoogle(
{ requestPermissions: ['email', 'profile'],
requestOfflineToken: 'true',
forceApprovalPrompt: 'true' //<==== This is the change
}
);
}
});
This added with no additional code changed this added the expiresAt and populated it.
Im doing final testing now. I will update.
I have an app with checklists where I want to be able to share checklists with other users. Those users might not be registered yet in my app. Currently my database is structured as follows:
{
"checklists" : {
"-KQKfnuGEhhoSIXyFj71" : {
"admins" : [
{ "C9fdXO6jWTZIxzgbhy0K10TEBqx1" : true },
{"ZQKHTbTlVMQwnZzFkQUcJF84SaA3" : true }
],
"users" : {
"-KQKfnuGEhhoSIXyFj74" : {
"email" : "admin1#example.com",
"uid" : "C9fdXO6jWTZIxzgbhy0K10TEBqx1",
"active" : true,
"name" : "Moses"
},
"-KQKfnuGEhhoSIXyFj75" : {
"email" : "admin2#example.com",
"uid" : "ZQKHTbTlVMQwnZzFkQUcJF84SaA3",
"active" : true,
"name" : "John"
},
"-KQKfnuGEhhoSIXyFj76" : {
"email" : "user1#example.com",
"uid" : "1ZxxYFGzoCPIc7Am07GVRxSN7xT2",
"active" : true,
"name" : "Kate"
},
"-KQKfnuGEhhoSIXyFj77" : {
"email" : "user2#example.com",
"active" : false
}
},
"data" : {
"task1" : "hello",
"task2" : "good bye"
}
}
}
}
My bolt file is as follows:
// admin can do both read/ write to the while checklist
path /checklists/{checklistId} {
read() { isChecklistAdmin(checklistId) }
write() { isChecklistAdmin(checklistId) }
}
// users of checklist can do read/write on data only
path /checklists/{checklistId}/data {
read() { isChecklistUser(checklistId) }
write() { isChecklistUser(checklistId) }
}
// users of checklist can add/ remove themselves from the list by updating active flag
path /checklists/{checklistId}/users/{checklistUserId} {
// all checklist users can read other users
read() { isChecklistUser(checklistId) }
// only admin (implicit) or the user can accept/reject (active) invitation or update their details
write() { isChecklistUser(checklistId) && ref.parent().email == auth.email }
}
// is authenticated user in the list of checklist admins?
isChecklistAdmin(checklistId) { isSignedIn() && root.checklists[checklistId].admins[auth.uid] != null }
// is authenticated user in the list of checklist users (test email ==)
isChecklistUser(checklistId) { isSignedIn() && root.checklists[checklistId].users['*'].email == auth.email }
isSignedIn() { auth != null }
The flow I had in mind was:
Admin adds a node under users which has the email of the invited user
Admin sends an email invite (I would have liked to use Firebase invites but I don't see how to integrate it in this scenario - would love suggestions).
User registers/ logins and updates active flag to true
My question: if you look at the isChecklistUser function, you will find I am using a "wildcard" ("*") since I do not know what that value is. Admin cannot set it at invitation since the user might not be registered yet.
I've got a question on how to combine data from different Firebase database nodes before displaying it on the frontend. I've got a Firebase database with the following structure. (I'm new to a nosql setup, so this looks more relational):
{
"agents" : {
"-KPCmnwzjd8CeSdrU3As" : {
"contactNumber" : "12345",
"name" : "aaa"
},
"-KPCmw6dKuopDlsMVOlU" : {
"contactNumber" : "123",
"name" : "bbb"
},
"-KPCoWcLecpchcFV-vh_" : {
"contactNumber" : "123",
"name" : "ccc"
},
"-KPROMhPatLjVxMdvfLf" : {
"contactNumber" : "256342",
"name" : "blah"
},
"-KPWIFl5qp5FvAeC3YhG" : {
"contactNumber" : "123",
"name" : "eee"
}
},
"listings" : {
"-KPWKTvW3GzFEIT2hUNU" : {
"agent" : "-KPCoWcLecpchcFV-vh_",
"description" : "third",
"reference" : "REF1"
}
}
}
I'm using Firebase SDK 3.2.0 and AngularFire 2.0.1. In my Angular app I am able to get the list of listings, and for each one look up the agent information. The reason I'm not storing the agent info with the listing is I want the ability to update the agent and the change should reflect on all listings. I don't want to have to go and update all listings if the agent telephone number changes (as an example).
In my controller I have the following:
// get the listings
var listingsRef = firebase.database().ref().child('listings');
vm.listings = $firebaseArray(listingsRef);
// this will move to my ui-router as a resolve but for simplicity's sake
// I added it here...
vm.listings.$loaded().then(function(data){
// loop through the listings...
data.forEach(function(listing) {
if (listing.agent) {
// get the agent for the listing
listing.agent = AgentFactory.getAgent(listing.agent);
}
});
});
Right now the data is displaying correctly on the frontend. There is a slight delay with the agent data showing because of the need of the getAgent promise to resolve.
My questions are:
Is this the correct way of getting the agent data? Should I be looping through the listings and for each query the agent data? How do I wait / keep track of all of the getAgents to resolve?
Any help would be appreciated.
Thanks.
I've structured my data similarly. If you want to wait for all the getAgents to resolve you can use $q.all. I'm not entirely sure what your AgentFactory.getAgent is returning, but let's assume it's a $firebaseObject. If that's the case inject $q and then do the following:
vm.listings.$loaded().then(function (data) {
// loop through the listings...
var promises = [];
data.forEach(function (listing) {
if (listing.agent) {
// get the agent for the listing
listing.agent = AgentFactory.getAgent(listing.agent);
promises.push(listing.agent.$loaded());
}
});
return $q.all(promises);
}).then(function (agents) {
//all agents are loaded
});
I'm a complete newbie, and I've been fiddling with the Meteor 1.0 sample todo list app to connect google oauth to it.
When I do so the page no longer renders properly because {{username}} is not set at all.
https://docs.meteor.com/#/full/meteor_users says "username: a unique String identifying the user." but the oauth stuff doesn't create one for you.
Connect service to existing meteor account talks about linking an already existing account to another service, but in this case I just want to use the external service.
https://stackoverflow.com/questions/25182903/meteor-facebook-registration uses onCreateUser() to manually set
user.username = user.services.facebook.name
but this isn't portable across services nor to guarantee uniqueness.
https://github.com/aldeed/meteor-collection2 defines the User schema so that username is mandatory.
When I dump the users table (some fields removed) the google account doesn't have a username, and there is no field that can really take on that value automatically as there could be a clash. Email could be used but I'd rather the username wasn't the email address. Do I just force the user to specify a desired username?
meteor:PRIMARY> db.users.find()
{
"_id" : "YNWt2cATMsKFG7od6",
"createdAt" : ISODate("2014-11-05T11:08:00.406Z"),
"services" : {
"password" : {
},
},
"username" : “a_user”
}
{
"_id" : "CyQsJqcez3kWTRHyQ",
"createdAt" : ISODate("2014-11-05T12:09:40.139Z"),
"profile" : {
"name" : “Alice User”
},
"services" : {
"google" : {
"email" : “a_user#example.com",
"family_name" : “User”,
"gender" : “female",
"given_name" : "Alice”,
"id" : "1115",
"name" : “Alice User,
}
}
}
What is the correct way of handling this?
This is how I did it myself with facebook and google
Accounts.onCreateUser(function (options, user) {
if (options && options.profile) {
user.profile = options.profile;
}
if (user.services) {
var service = _.pairs(user.services)[0];
var serviceName = service[0];
var serviceData = service[1];
console.log("serviceName", serviceName)
if (serviceName == "facebook") {
user.emails = [
{"address": serviceData.email, "verified": true}
];
user.profile = {"first_name": serviceData.first_name, "last_name": serviceData.last_name, "avatar": getFbPicture(serviceData.id)};
}
else if (serviceName == "google") {
user.emails = [
{"address": serviceData.email, "verified": true}
];
user.profile = {"first_name": serviceData.given_name, "last_name": serviceData.family_name, "avatar": getGooglePicture(serviceData.id)};
}
}
console.log("user created :", user)
return user;
});
I do not use username but I use email so that I'm sure that it will be unique. After that
I could allow the user to set his username or display name like Stackoverflow or other services do.
However you could use the email as username and again let the user change it later.
In my application, I am using this to handle the same problem.
username = user.services.facebook.name
user.username=generateUsername(username)
generateUsername = function(username) {
var count;
username = username.toLowerCase().trim().replace(" ", "");
count = Meteor.users.find({"profile.un": username}).count();
if (count === 0) {
return username;
}
else {
return username + (count + 1)
}
This is will create a unique username. After successful signup you can allow the users to change the username and check your db for its existence.
In my application I use
if(user.services.facebook)
this.user = user.services.facebook.name
if(user.services.google)
this.user = user.services.google.name