Loopback Verify User with lb-mandrill-connector: Uncaught AssertionError: template name should be defined - email

I'm trying to send an email with mandrill through loopback user.verify method but I'm stucked in this error:
Uncaught AssertionError: template name should be defined
Googleing around I found this PR on loopback: https://github.com/strongloop/loopback/pull/1517/files
but it seems that has not been merged as it brake TravisCI tests...
From that PR, I tried this workaround in my code:
let options = {
type: 'email',
port: '80',
to: ac.email,
from: app.get('emailFrom'),
subject: 'Thanks for registering.',
verifyHref: verifyHref,
template: null,
user: ac
};
var template = loopback.template(path.resolve(__dirname, '../../server/views/verify.ejs'));
options.html = template(options);
But it seems that loopback set a default template if no specified, any suggestion?
I'm running on loopback#2.18

The solution here is not to do the user.verify but generate the token and then call user.save and proceed with the call to mandrill.
context.req.app.models.User.generateVerificationToken(user, function (err,token) {
if (err) {
return next(err);
}
user.verificationToken = token;
user.save(function (err) {
if (err) {
next(err);
}
else {
var url = 'http://' + config.host + ':' + config.port + '/api/users/confirm?uid=2&redirect=/&token=' + token;
var mandrill_client = new mandrill.Mandrill('XXXX');

Shouldn't it be
let options = {
type: 'email',
port: '80',
to: ac.email,
from: app.get('emailFrom'),
subject: 'Thanks for registering.',
verifyHref: verifyHref,
template: path.resolve(__dirname, '../../server/views/verify.ejs'),
user: ac
};
user.verify(options, function(err, response) {
if (err) {
next(err);
return;
}
console.log('> verification email sent:', response);
// render the response template
context.res.render('response', {
title: 'Signed up successfully',
content: 'Please check your email and click on the verification link before logging in.',
redirectTo: '/',
redirectToLinkText: 'Log in'
});
});
The loopback method user.verify compiles and renders the ejs template (via loopback.template) and sends the email via the Email datasource. Unfortunately you can't supply a different template engine .. it's hardcoded to ejs. I wanted to use nunjucks so had to override loopback.template to return the render method of nunjucks so that when loopback.template is called from user.verify it works as expected.

I just stumbled accross the issue today. Mandrill provides built-in template functionality where you you provide dynamic templates within your mandrill account and specify variables to populate using the mandrill connector. When template is provided template.name must exist or an assert statement will fail.
This is taken straight from the code:
/**
* Send transactional email with options
*
* Basic options:
*
* {
* from: { name: "evenemento", email: "crew#evenemento.co" },
* to: "hello#evenemento.co",
* subject: "Ho ho",
* text: "Plain text message",
* html: "<b>Html messages</b> put here"
* }
*
* Full list of options are available here:
* https://mandrillapp.com/api/docs/messages.nodejs.html#method=send
*
* if option template is set than message will be send as template:
*
* {
* from: { name: "evenemento", email: "crew#evenemento.co" },
* to: "hello#evenemento.co",
* subject: "Ho ho",
* template: {
* name: "signup-confirm",
* content: {
* name: "NewUser Name",
* accountId: "123456"
* }
* }
* }

Related

Strapi custom email template

I'm using nodemailer for email submission and running from my localhost. I have email services created manually in the following dir /api/email/services/Email.js
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: 'example.com',
port: 587,
secure: false,
auth: {
user: 'user',
pass: 'password',
},
});
module.exports = {
send: (from, to, subject, html) => {
const options = {
from,
to,
subject,
html
};
return transporter.sendMail(options);
},
};
So then I can use it like strapi.services.email.send(from, email, subject, html);
Usually, I write my html template in the code line const html = '<p>Email testing</p>' to be passed in the email services. But I don't want to do this for every email submission from different controllers.
So, I created a html template in /config/email-templates/custom-email.html and tried to call it like const html = path.join(__dirname + '/../../../config/email-templates/custom-email.html');.
When I run it, the email can be sent successfully but it cannot render the html. Instead of a rendered html, it's showing the full path of the custom-email.html as the email message. Is this method possible to achieve in strapi?
Instead of passing the path to the file, you need to pass the actual content. In the first case const html = '<p>Email testing</p>' , you are actually passing the content , but in the second case you are passing the file path.
Modified send method could look something like below:
send: (from, to, subject, htmlpath) => {
const readHTMLFile = (path, callback)=> {
fs.readFile(path, {encoding: "utf-8"}, function (err, html) {
if (err)
return callback(err);
else
return callback(null, html);
});
}
readHTMLFile(htmlpath, function(err, html) {
const options = {
from,
to,
subject,
html
};
return transporter.sendMail(options);
}); }

Use DataFields in Rest URL in ExtJS to access Context.io API

I have two Question Regarding Rest API in EXTJS.
How can I use fields to make rest URL dynamic?
How can I add authentication key to access Context.io in my Rest.Proxy?
This is my solution, but I am not sure if I have done it properly, or not. I am pretty new in ExtJS, so my question may be basic, but I appreciate your help.
Ext.define("EmailFolders", {
extend: "Ext.data.Model",
fields: ["id", "label"],
proxy: {
type: "rest",
url: "lite/users/:" + id + "/email_accounts/:" + label + "/folders"
},
reader: {
type: "json"
},
headers: {
CONSUMER_KEY: "KEY FROM CONTEX.IO",
CONSUMER_SECRET: "SECRET FROM CONTEXT.IO"
}
});
You could use store.getProxy() to make rest URL dynamic and to pass the authentication keys in headers. Proxy have methods
proxy.setUrl() to sets the value of url.
proxy.setHeaders() to sets the value of headers.
You can check here with working fiddle
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
let url = 'https://jsonplaceholder.typicode.com/users';
// Set up a model to use in our Store
Ext.define('User', {
extend: 'Ext.data.Model',
proxy: {
type: 'ajax',
reader: {
type: 'json',
rootProperty: ''
}
}
});
Ext.define('MyStore', {
extend: 'Ext.data.Store',
model: 'User',
listeners: {
beforeload: function (store) {
var proxy = store.getProxy();
//if you want, you can also set here url inside of beforeload
//proxy.setUrl(url);
/*
* You can use {proxy.setHeaders} to set the values from CONTEX.IO
* After ajax request see your request parameter in network analysis below 2 headers are passed in request header
*/
proxy.setHeaders({
CONSUMER_KEY: "KEY FROM CONTEX.IO",
CONSUMER_SECRET: "SECRET FROM CONTEXT.IO"
});
}
}
});
let store = new MyStore();
//Set the dynamic url here
//This {url} will be dynamic whatever you want to pass
store.getProxy().setUrl(url);
store.load(function (data) {
console.log(data);
alert('Open console to see reposne..!')
});
/*
You can also pass url inside of load funtion
*/
new MyStore().load({
url: url + '/' + 1,
callback: function (data) {
console.log(data);
}
});
}
});

How to read mail text in mail listener 2?

conf.js
var MailListener = require("mail-listener2");
var mailListener = new MailListener({
username: "*****#office365.com",
password: "******",
host: "outlook.office365.com",
port: 993, // imap port
tls: true,
fetchUnreadOnStart: true,
tlsOptions: {rejectUnauthorized: false},
mailbox: "INBOX",
searchFilter: "UNSEEN",
markSeen: true
});
mailListener.on("server:connected", function () {
console.log("imapConnected");
});
mailListener.on("server:disconnected", function () {
console.log("imapDisconnected");
});
(function () {
var count = 0;
mailListener.on("mail", function (mail, seqno, attributes) {
var mailuid = attributes.uid,
toMailbox = 'Inbox',
i = ++count;
if (i > 1) {
mailListener.stop(); // start listening
return;
}
console.log('email parsed', {
i: i,
subject: mail.subject,
from: mail.from,
text:mail.text,
seqno: seqno,
uid: attributes.uid,
attributes: attributes
});
expect(mail.subject).toEqual("FW: Secure One-Time-Password for Account Login");
var pattern = new RegExp(/Please use (\w+)/g);
var regCode = pattern.exec(mail.text)[1];
console.log(regCode);
console.log('attempting to mark msg read/seen');
mailListener.imap.addFlags(mailuid, '\\Seen', function (err) {
if (err) {
console.log('error marking message read/SEEN');
return;
}
//console.log('moving ' + (seqno || '?') + ' to ' + toMailbox);
//mailListener.imap.move(mailuid, toMailbox, function (err) {
if (err) {
console.log('error moving message');
return;
}
console.log('moved ' + (seqno || '?'), mail.subject);
});
});
});
})
();
mailListener.start(); // start listening
setTimeout(function () {
mailListener.stop(); // start listening
}, 60 * 1000);
I am reading all the details except text and the text is in html table format.
Instead of text i am getting undefined message.If needed i will add html code also.
If i am forwarding the same mail to gmail from office 365 and reading the mail from gmail i am able to get text.
Error:
subject: 'test mail',
from: [ { address: 'otp#gmail.com', name: 'gmail.com' } ],
body: undefined,
seqno: 2,
uid: 18,
attributes:
{ date: 2017-06-14T16:22:06.000Z,
flags: [ '\\Seen' ],
uid: 18,
modseq: '3914',
'x-gm-labels': [],
'x-gm-msgid': '1570197813730673685',
'x-gm-thrid': '1570197813730673685' } }
[21:56:13] E/launcher - Cannot read property '1' of null
[21:56:13] E/launcher - TypeError: Cannot read property '1' of null
I see that this is an old issue, but I am facing the same problem and using the nearly the same function.
I want to get the content of the e-mail with a link and the email is in HTML. So: mail.text doesn't and won't work in this case.
Solution is really simple and it works for me straight forward: mail.html

Please explain this code is for Articles.events.publish

I'm looking for help to understand this code from the sample module Articles in the mean.io generated app. I can't figure out what Articles.events.publish is for.
file: packages/core/articles/server/controllers/articles.js
create: function(req, res) {
var article = new Article(req.body);
article.user = req.user;
article.save(function(err) {
if (err) {
return res.status(500).json({
error: 'Cannot save the article'
});
}
Articles.events.publish({
action: 'created',
user: {
name: req.user.name
},
url: config.hostname + '/articles/' + article._id,
name: article.title
});
res.json(article);
});
}
It's used to send data to stacksight. For detail, you can refer Module's constructor in node_modules/meanio/lib/core_modules/module/index.js, and you can find stacksight under node_modules/meanio/node_modules/stacksight.
But it will NOT send these information by default, it needs to request app id and API token from stacksight first.

Getting error sending email in nodejs with emailjs

[error: can't set headers after they are sent.]
createCredentials() is deprecated, use tls.createSecureContext instead
{[Error: bad response on command '-']
code:2
smtp : '550 5.3.4 Requested action not token; To continue sending messages,
please sign in to your account.\n}
I have been trying to send email in nodejs with emailjs and nodemailer but i keep on getting the error above.
transportEmail: email.server.connect({
user: "ghConnectUs#outlook.com",
password:"******",
host: "smtp-mail.outlook.com",
tls: {ciphers: "SSLv3"}
})
note: i have include all modules.
i'm hoping somebody can point me to the right path. i just want to send me using outlook or gmail in node app.
Below is the code for sending email from nodejs app (through Gmail):
Using Nodemailer v1.3.4:
var nodemailer = require("nodemailer");
var transporter = nodemailer.createTransport({
service: "Gmail",
auth: {
user: "email_id_of_gmail_account",
pass: "password_of_gmail_account"
}
});
var mailOptions = {
from: 'sender_email_id', // sender address
to: 'receiver_email_id, some_other_email_if_requierd', // list of receivers
cc: 'cc_email_id'
subject: 'subject text', // Subject line
text: 'body plain text', // plaintext body
html: '<b>body html</b>' // html body
};
var sendEMail = function () {
transporter.sendMail(mailOptions, function(error, info){
if(error){
console.log(error);
}else{
console.log('Message sent: ' + info.response);
}
});
};
sendEmail();
Note: if you are using Nodemailer version 0.7 or lower, then transporter object will created like this:
var transporter = nodemailer.createTransport('SMTP', {
service: "Gmail",
auth: {
user: "email_id_of_gmail_account",
pass: "password_of_gmail_account"
}
});
Using emailJs v0.3.16:
var email = require("/node_modules/emailjs/email.js");
var server = email.server.connect({
user: "emailId_of_gmail_account",
password: "password_of_gmail_account",
host: "smtp.gmail.com",
ssl: true // in case outlook, use "tls: {ciphers: "SSLv3"}"
});
var message = {
text: "body text",
from: "senderName <sender's_email_id>",
to: "receiverName <receiver_email_id>",
subject: "subject text",
attachment: // optional
[
{data: "<html>i <i>hope</i> this works! html </html>", alternative: true},
{path: "path/to/file.zip", type:"application/zip", name:"renamed.zip"}
]
};
var sendEMail = function () {
server.send(message, function (err, message) {
console.log(err || message);
});
};
sendEmail();