I'm modifying a Mail Merge project in Google Apps Script. The problem I'm facing is how to make the email body display inline images. Currently, all inline images present in the original email template are displayed as attachments after GmailApp.sendEmail() is called.
My guess is that the inline images can be displayed if I find some way to convert the imgVars array into a JSON object like this (taken from the example in GAS documentation):
MailApp.sendEmail(
"[email protected]",
"Logos",
"",
{ htmlBody:
"inline Google Logo<img src='cid:googleLogo'> images! <br/> inline YouTube Logo <img src='cid:youTubeLogo'>",
inlineImages:
{ googleLogo: googleLogoBlob,
youTubeLogo: youtTubeLogoBlob
}
}
);
So what I'm trying to do is convert an array like this:
var array = { item1, item2, item3 };
To a JSON object like this:
var json = { item1Name: item1,
item2Name: item2,
item3Name: item3
};
Here is a code snippet from the Mail Merge I'm working on:
//---------------------------------------------------------------
// If there are inline images in the body of the email
// Find them and store them in an array, imgVars
//---------------------------------------------------------------
if(emailTemplate.search(/<\img/ != -1)) {
var inlineImages = {};
// Extract all images from the email body
var imgVars = emailTemplate.match(/<img[^>]+>/g);
// For each image, extract its respective title attribute
for (i in imgVars) {
var title = imgVars[i].match(/title="([^\"]+\")/);
if (title != null) {
title = title[1].substr(0, title[1].length-1);
for (j in attachments) {
if (attachments[j].getName() == title) {
inlineImages[title] = attachments[j].copyBlob();
attachments.splice(j,1);
}
}
var newImg = imgVars[i].replace(/src="[^\"]+\"/,"src=\"cid:"+title+"\"");
emailTemplate = emailTemplate.replace(imgVars[i],newImg);
}
}
}
objects = getRowsData(dataSheet, dataRange);
for (var i = 0; i < objects.length; ++i) {
var rowData = objects[i];
if(rowData.emailSent != "EMAIL_SENT") {
// Replace markers (for instance ${"First Name"}) with the
// corresponding value in a row object (for instance rowData.firstName).
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = fillInTemplateFromObject(selectedTemplate.getSubject(), rowData);
GmailApp.sendEmail(rowData.emailAddress, emailSubject, emailText,
{name: e.parameter.name,
attachments: attachments,
htmlBody: emailText,
cc: cc,
bcc: bcc,
inlineImages: inlineImages});
Some comments:
That is syntactically incorrect, an array literal should be:
[...]
The backslash before
img
is unnecessary, the pattern should would be better with a trailing space and be case insensitive (since HTML is case insensitive and usually presented with tag names in capital letters), so/<img /i
Parsing HTML with a regular expression is not a good idea, much better to convert the HTML to a document fragment and deal with that.
Note that String.prototype.match returns an array.
Using for..in with an array is not recommended for a number of reasons, such as the members may not be returned in any particular order (it may be different in different browsers) and for..in will return all enumerable properties of the array and its
[[Prototype]]
so if you are in a browser that has hadArray.prototype
modified by a "shim" or "monkey patch" then those properties will be enumerated too, so:may well be attempting to call match on a property whose value is a reference to a function and so throw an error. At the very least a hasOwnProperty test should be included, but better to just use a plain for loop.
It seems attachments is an array too, so use a plain for loop here too for the same reasons as above. It's also not a good idea to use for..in (which may visit properties in an unexpected order) with splice, which infers you are expecting a particular order. Note that in some browsers, for..in will visit array members in the order they were added to the array, not in numeric order. Other browsers will always visit numeric properties in a particular order but not others.
Much of this will be a lot simpler if the HTML is converted to a document fragment, then DOM methods can be used to extract the img elements and access their properties to build an object. Then native methods can be used to convert the object to JSON if required.