I've created an email that sends when a button is clicked however I'm not sure how I take the users name and get it into the email. There is variable data emailData however that is on the server side. How do I get the users first name into the email?
Path: database schema
"profile": {
"firstName": "SomeGuy",
}
Path: server/email.js
// In your server code: define a method that the client can call
Meteor.methods({
sendEmail: function (to, from, subject, text) {
check([to, from, subject, text], [String]);
// Let other method calls from the same client start running,
// without waiting for the email sending to complete.
this.unblock();
SSR.compileTemplate( 'htmlEmail', Assets.getText( 'html-email.html' ) );
var emailData = {
name: "Doug Funny",
};
Email.send({
to: to,
from: from,
subject: subject,
html: SSR.render( 'htmlEmail', emailData )
});
}
});
Path: private/html-email.html
Hi {{name}},
This is a test email
Path: client/emailButton.js
Template.emailButton.events({
'click .send-email-button': function () {
Meteor.call('sendEmail',
'test#email.com',
'test#email.com',
'Hello from Meteor!',
'This is just some text. If removed this email send stops working');
}
});
UPDATE
Path: client/emailButton.js
'submit #myForm': function () {
var otheruserId = FlowRouter.getParam('id');
Meteor.call('sendEmail',
'test#email.com',
'Hello from Meteor!',
otheruserId);
}
If you want the username of user who made the request then you can use Meteor.user() or Meteor.userId() like this,
Meteor.methods({
sendEmail: function (to, from, subject, text) {
check([to, from, subject, text], [String]);
// Let other method calls from the same client start running,
// without waiting for the email sending to complete.
this.unblock();
SSR.compileTemplate( 'htmlEmail', Assets.getText( 'html-email.html' ) );
var user = Meteor.user();
// OR
// var userId = Meteor.userId();
// var user = Meteor.users.findOne({ _id: userId });
var emailData = {
name: (user && user.profile && user.profile.firstName) || ""
};
Email.send({
to: to,
from: from,
subject: subject,
html: SSR.render( 'htmlEmail', emailData )
});
}
});
UPDATE: If it is for different user
Since you have the other user's id on the client side, you need send that as a parameter to the Meteor.method. See the method below with additional parameter userId
Meteor.methods({
sendEmail: function (to, from, subject, text, userId) {
check([to, from, subject, text], [String]);
// Let other method calls from the same client start running,
// without waiting for the email sending to complete.
this.unblock();
SSR.compileTemplate( 'htmlEmail', Assets.getText( 'html-email.html' ) );
var user = Meteor.users.findOne({ _id: userId });
var emailData = {
name: (user && user.profile && user.profile.firstName) || ""
};
Email.send({
to: to,
from: from,
subject: subject,
html: SSR.render( 'htmlEmail', emailData )
});
}
});
Now on the client side, you can do,
Meteor.call("sendEmail", to, from , subject, text, otheruserId);
Related
I'm trying to send the email verification link after the user is created on my flutter app, but the email isn't sent and in my Cloud Functions Log I'm receiving the message when I deploy:
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{"code":9,"message":"FAILED_PRECONDITION"},"authenticationInfo":{"principalEmail":"*************"},"requestMetadata":{"callerIp":"186.216.140.62","callerSuppliedUserAgent":"FirebaseCLI/6.5.0,gzip(gfe),gzip(gfe)","requestAttributes":{"time":"2019-03-29T23:21:10.130Z","auth":{}},"destinationAttributes":{}},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","authorizationInfo":[{"permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}},{"resource":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail","permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}}],"resourceName":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail","request":{"#type":"type.googleapis.com/google.cloud.functions.v1.UpdateFunctionRequest","function":{"labels":{"deployment-tool":"cli-firebase"},"eventTrigger":{"eventType":"providers/cloud.firestore/eventTypes/document.create","resource":"projects/pppppp-9800a/databases/(default)/documents/users/{userId}","service":"firestore.googleapis.com"},"sourceUploadUrl":"https://storage.googleapis.com/gcf-upload-us-central1-dc1829cf-3a07-4951-be81-1a15f892ed8d/8ea3f162-c860-4846-9064-04a855efca2f.zip?GoogleAccessId=service-73683634264#gcf-admin-robot.iam.gserviceaccount.com&Expires=1553903464&Signature=******************","name":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail"}}}
My code:
exports.sendVerificationEmail = functions.firestore.document('users/{userId}').onCreate((snap, context) => {
const user = snap.data();
console.log("----------------------");
console.log("user created: " + user.uidColumn);
admin.auth().generateEmailVerificationLink(user.email).then((link) => {
console.log("**********" + link);
sendVerificationEmail(user.emailColumn, link);
return 0;
}).catch(e => {
console.log(e);
})
return 0;
});
function sendVerificationEmail(email, link) {
var smtpConfig = {
host: 'smtp.gmail.com',
port: 465,
secure: true, // use SSL
auth: {
user: 'myappemail#gmail.com',
pass: 'password'
}
};
var transporter = nodemailer.createTransport(smtpConfig);
var mailOptions = {
from: "qeaapp#gmail.com", // sender address
to: email, // list of receivers
subject: "Email verification", // Subject line
text: "Email verification, press here to verify your email: " + link,
html: "<b>Hello there,<br> click here to verify</b>" // html body
};
transporter.sendMail(mailOptions, function (error, response) {
if (error) {
console.log(error);
} else {
console.log("Message sent: " + response.message);
}
return 0;
});
return 0;
}
When I the the command firebase deploy I get the message functions: failed to update function sendVerificationEmail
HTTP Error: 400, Change of function trigger type or event provider is not allowed
I'm new in JS and I don't know what these erros mean
Delete your first function called sendVerificationEmail, then redeploy. It looks like you maybe initially deployed it as something other than a Firestore trigger.
How do I get the verification link from the default Meteor method into a custom email method I have that uses sendgrid stmp.
here is the meteor verification method that works well on its own and has the link I want:
sendVerificationLink() {
let userId = Meteor.userId();
if ( userId ) {
return Accounts.sendVerificationEmail( userId );
}
},
Here is my custom method that uses sendgrid, everything works except for I cant figure out how to get the link with the custom token:
'signupEmail' (submission) {
this.unblock();
const link = ''
const message = `welcome ${submission.firstname} `
const text = `welcome ${submission.firstname}. Please verify your
account ${link}`
Email.send({
from: "hi#test.com",
to: submission.email,
subject: message,
text: text,
html: text,
});
}
Just in case anyone is looking for this in the future I found an answer on Meteor forums: https://forums.meteor.com/t/how-to-get-verification-link-for-custom-sent-verification-email/22932/2
Basically I added a token record and saved it in the database. Then used the token with the method: Accounts.urls.verifyEmail which created the link to insert in the email.
Here is my final method:
'signupEmail' (submission) {
this.unblock();
let userId = Meteor.userId();
var tokenRecord = {
token: Random.secret(),
address: submission.email,
when: new Date()};
Meteor.users.update(
{_id: userId},
{$push: {'services.email.verificationTokens': tokenRecord}}
);
const verifyEmailUrl = Accounts.urls.verifyEmail(tokenRecord.token);
const message = `welcome ${submission.firstname} `
const text = `welcome ${submission.firstname}. Please verify your account ${verifyEmailUrl}`
Email.send({
from: "hi#test.com",
to: submission.email,
subject: message,
text: text,
html: text,
});
},
So I'm using Firebase-UI to authenticate and sign in users, I need to use the account chooser in order for them to sign in to a different Google account (not using the account chooser results in it auto-signing them in), however, I want to either prevent it from displaying + saving accounts, or remove them on sign out.
And this is the Firebase-UI web I'm using: Firebase UI web
This isn't a huge issue when the application is running on a user's machine, however, it will also be running on a public machine with many users signing in an out, and we can't have them saved as an easy one-click sign in. The biggest security issue is the fact that I can also log into their emails once they've authenticated with Google. We want it to forget them once they sign out.
My sign-in flow:
<script type="text/javascript">
// FirebaseUI config.
var uiConfig = {
callbacks: {
signInSuccess: function (user, credential, redirectUrl) {
var userSignIn = {
displayName: user.displayName,
email: user.email,
emailVerified: user.emailVerified,
photoURL: user.photoURL,
uid: user.uid,
phoneNumber: user.phoneNumber
};
/* POST signed in user to Login Controller*/
var csrfToken = $('input[name="csrfToken"]').attr('value');
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('Csrf-Token', csrfToken);
}
});
$.ajax({
url: '/signedIn',
type: 'POST',
data: JSON.stringify(userSignIn),
contentType: 'application/json',
error: function(err) {
console.log(err);
}
});
return true;
}
},
signInSuccessUrl: '/Dashboard',
signInOptions: [{
provider: firebase.auth.GoogleAuthProvider.PROVIDER_ID,
scopes: ['https://www.googleapis.com/auth/calendar']
}],
// Terms of service url.
tosUrl: '/Terms'
};
// Initialize the FirebaseUI Widget using FirestoreDB.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
// The start method will wait until the DOM is loaded.
ui.start('#firebaseui-auth-container', uiConfig);
</script>
Sign-out flow:
initApp = function () {
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
if (window.location.pathname === "/Login" || window.location.pathname === "/") {
window.location.href = '/Dashboard';
}
$('#sign-out').show();
} else {
// User is signed out.
$('#sign-out').hide();
disableLinks();
switch(window.location.pathname){
case "/Login":
case "/Terms":
case "/Help":
break;
default:
window.location.href = '/Login';
}
}
}, function (error) {
console.log(error);
});
};
window.addEventListener('load', function () {
initApp();
document.getElementById('sign-out').addEventListener('click', function () {
firebase.auth().signOut().then(function() {
sessionStorage.clear();
localStorage.clear();
window.location = "/Logout";
}).catch(function(error) {
console.log(error);
});
});
});
On sign out from Firebase Auth, redirect to Google single sign out URL:
firebase.auth().signOut()
.then(function() {
window.location.assign('https://accounts.google.com/Logout');
})
.catch(function(error) {
console.log(error);
});
This gives you logged in user's email address via accounts-password.
Meteor.user().emails[0].address
How can I get email addresses on the client/server when users use third party services to login? E.g. facebook, google.
I want to call the following method from the client
Meteor.methods({
sendEmail: function() {
var userEmail;
if(Meteor.user().emails[0].address) {
return userEmail = Meteor.user().emails[0].address;
} else if (Meteor.user().services.google.email) {
return userEmail = Meteor.user().services.google.email;
} else if (Meteor.user().services.facebook.email) {
return userEmail = Meteor.user().services.facebook.email;
}
Email.send({
to: userEmail,
from: "example#gmail.com",
subject: "some subject",
text: "sometext"
});
}
});
I get TypeError: Cannot read property '0' of undefined
I don't like this but it works this way
Meteor.methods({
sendEmail: function() {
this.unblock();
var currentUser = Meteor.user();
if (currentUser && currentUser.emails && currentUser.emails[0]
&& currentUser.emails[0].address) {
var userEmail = currentUser.emails[0].address;
Email.send({
to: userEmail,
from: "something#gmail.com",
subject: "something",
text: "something"
});
} else if (currentUser && currentUser.services && currentUser.services.google
&& currentUser.services.google.email) {
var userEmail = currentUser.services.google.email;
Email.send({
to: userEmail,
from: "something#gmail.com",
subject: "something",
text: "something"
});
} else if (currentUser && currentUser.services && currentUser.services.facebook
&& currentUser.services.facebook.email) {
var userEmail = currentUser.services.facebook.email;
Email.send({
to: userEmail,
from: "something#gmail.com",
subject: "something",
text: "something"
});
}
}
});
I think it just assumes that it can send an email to empty string/null/undefined. I tried to throw Meteor.Error on if(!userEmail) no luck. If someone can make this code neater I'd appreciate it.
I have the following code in my Meteor app where I create new users, assign them 'basic' role. Yet I am having a trouble showing on the client side errors returned while processing Accounts.createUser, can someone please tell me how I can return errors returned by Accounts.createUser while having it on the server as my code below. Thanks
/server/users.js
Meteor.methods({
'createMemberAccount': function (data, role) {
var userId;
Meteor.call('createNewAccount', data, function(err, result) {
if (err) {
return err;
}
console.log('New account id: '+ result);
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createNewAccount': function (adminData) {
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
}
});
/client/signup.js
Template.signupForm.events({
'submit #signup-form': function(e, t){
e.preventDefault();
var userData = {};
userData.email = $(e.target).find('[name=email]').val();
userData.password = $(e.target).find('[name=password]').val();
userData.roles = ['basic'];
Meteor.call('createMemberAccount', userData, 'basic', function(err, userId) {
if (!err) {
console.log('All OK');
} else {
console.log('Error: ' + err.message);
}
});
return false;
}
});
Since You are creating an static rol "basic", you don't need to do that pair of methods, and Meteor.calls, instead you can use
So, use the v on the client side, just like this.
Template.register.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#account-email').value
, password = t.find('#account-password').value;
// Trim and validate the input
Accounts.createUser({email: email, password : password}, function(err){
if (err) {
// Inform the user that account creation failed
} else {
// Success. Account has been created and the user
// has logged in successfully.
}
});
return false;
}
});
If you see there is not any role yet incude, so now on the server.js use the onCreateUser method.
//Server.js
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.role = "basic"
return user;
});
Now thats is more easy, and with less code, if you are trying to create 2 differents roles like "Admin" and "Basic", just on the client side create a profile field named "profile.roles" and do a if statement on the onCreateUser.
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
This part returns the userId once it is created, it doesn't return any errors when it fails.
When it fails, the returned value will be undefined
Also, in the server, we cannot use callbacks with Accounts.createUser
If you want find the errors, you have to use Accounts.createUser in client side.
Coming to this late, but on the server side, you can assign the createUser to a variable and it will return the new user’s _id; then you can check if that exists. For example (server side only):
let email = 'foo#bar.com';
let password = 'bar';
let profile = {firstName: 'foo', lastName: 'bar'};
let newId = Accounts.createUser({
password: password,
email: email,
profile: profile
});
if (!newId) {
// New _id did not get created, reason is likely EMail Already Exists
throw new Meteor.Error(403, "Cannot create user: " + error.reason);
}
else {
// Stuff here to do after creating the user
}
The Meteor.Error line will be passed back as an error in the callback on the client side, so you can reflect that error to the browser.