email Google Doc as PDF attachment - javascript

abc#example.comAfter struggling for a while with this I am calling for help from the crowd.
I am trying to attach 2 Google Docs to an email as PDFs. I see many examples in regard to Google Sheets and have successfully been able to email copies of sheets as PDF, but I have not been able to port this over to Docs.
In multiple different scenarios, the PDF attachments are either a copy of my original template Doc or an image capture of "One Account. All of Google" sign in page. When I look at the links that are generated (Logger.log) and use them, a copy of the correct Doc is downloaded.
Below is an example of my scenario in which I am trying to create the email attachments. See function emailDocAsPDF() towards the bottom
Thanks in advance for any guidance on this issue.
function myFunctionUpdated() {
var testTemplateId = searchDriveFile('Test Template');
Logger.log('test template id = ' + testTemplateId);
var fileName = 'Copy of Test Template'
Logger.log(fileName);
DriveApp.getFileById(testTemplateId).makeCopy(fileName);
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = ss.getRange(1, 1, 3, 2).getValues();
var copyNewTemplateId = searchDriveFile(fileName);
var copyTemplate = DocumentApp.openById(copyNewTemplateId);
var copyTemplateBody = copyTemplate.getBody().editAsText();
for (i=0; i<range.length; i++) {
copyTemplateBody.replaceText(range[i][0], range[i][1]);
}
copyTemplate.saveAndClose();
emailDocAsPDF(fileName)
}
// Searched Google Drive for a file name and returns the file ID.
function searchDriveFile(fileName) {
var files = DriveApp.searchFiles(
'title = "'+ fileName +'"');
while (files.hasNext()) {
var file = files.next();
var id = file.getId();
return id;
}
}
// Send document in an email as PDF
function emailDocAsPDF(fileName) {
var staticDoc = 'FILE-ID';
var attachmentDoc = UrlFetchApp.fetch("https://docs.google.com/document/d/" + copyTemplateId + "/export?format=pdf");
Logger.log("https://docs.google.com/document/d/" + copyTemplateId + "/export?format=pdf");
var attachmentStaticDoc = UrlFetchApp.fetch("https://docs.google.com/document/d/" + staticDoc + "/export?format=pdf");
Logger.log("https://docs.google.com/document/d/" + staticDoc + "/export?format=pdf");
var fileBlob = [];
fileBlob[0] = attachmentDoc.getBlob().getAs('application/pdf');
fileBlob[1] = attachmentStaticDoc.getBlob().getAs('application/pdf');
var body = "Bird is the WORD!! <br>" +
"<a href='http://www.example.com'>Visit Example</a>";
if (MailApp.getRemainingDailyQuota() > 0)
GmailApp.sendEmail("email#example.com", "Test Documents Email", body, {
htmlBody: body,
attachments:[fileBlob[0],fileBlob[1]]
});
}
EDIT -- Successful Updates with code provided by Sandwich.
// Send document in an email as PDF
function emailDocAsPDF(fileName) {
var staticDoc = 'FILE-ID';
var copyTemplateId = searchDriveFile(fileName);
var blobs = [];
var doc = DriveApp.getFileById(copyTemplateId);
blobs[0] = doc.getBlob().getAs('application/pdf').setName('MyAttachment1.pdf');
var doc = DriveApp.getFileById(staticDoc);
blobs[1] = doc.getBlob().getAs('application/pdf').setName('MyAttachment2.pdf');
var zipBlob = Utilities.zip(blobs).setName('Documents.zip');
var recipient = 'abc#example.com';
var subject = 'Test Email';
var body = 'Bird is the WORD';
MailApp.sendEmail(recipient, subject, body, {attachments: [zipBlob]});
}

To attach any kind of document as a PDF, call getBlob on the file, specify content type as with .getAs('application/pdf') and optionally set a name with setName. That's all:
var doc = DriveApp.getFileById('ID_HERE');
var blob = doc.getBlob().getAs('application/pdf').setName('MyAttachment.pdf');
var recipient = 'user#example.com';
var subject = 'Email subject';
var body = 'Text here';
MailApp.sendEmail(recipient, subject, body, {attachments: [blob]});
You were trying to use "export as PDF via URL query", which is a poorly documented feature that is sometimes useful for Sheets (setting the boundaries of exported region), but is unnecessary for Docs, if it even exists for them. The above method should work for any kind of content stored in Google Drive.
By the way, it is preferable to use MailApp instead of GmailApp when sending email, because it keeps the level of authorization to what you need: sending emails. With GmailApp, the script is also granted permission to access and delete all of your existing emails, which is unadvisable for scripts composed by trial, error, and copying code from the Internet.

Related

I have an error code in my getBody() function and I dont know why

So I'm creating this code where the template is selected through a series of If statements. so it is a changing variable. I am simply trying to now replace text in the selected template and I keep getting the same error that getBody() is not a function. Any help is greatly appreciated!
var doc = DriveApp.getFileById(templateId);
var copy = doc.makeCopy();
var destination = DriveApp.getFolderById('1mGCx4yXX_NnLHsHsGWBGkzwAVhG-cTrc');
destination.addFile(copy);
copy.setName(regno + ' statistical analysis');
var copiedTemplateId = copy.getId();
var body = doc.getBody();
var header = doc.getHeader();
DriveApp.getFileById returns a File.
So, doc is of type File.
There is no "getBody" function for a File, at least it doesn't exist in the documentation: https://developers.google.com/apps-script/reference/drive/file
This works:
function myfunk() {
const regno = "test";
var file = DriveApp.getFileById("fileid");
var destination = DriveApp.getFolderById("folderid");
let name = regno + ' statistical analysis';
var copy = file.makeCopy(name, destination);
var copiedTemplateId = copy.getId();
let doc = DocumentApp.openById(copiedTemplateId);
var body = doc.getBody();
var header = doc.getHeader();
Logger.log('id: %s, body: %s, header: %s',copiedTemplateId,body,header);
}
File was a Google Document and a copied was created in the appropriate directory and renamed correctly.

Adding image from spreadsheet into an e-mail with Apps Script

I'm creating an automatic e-mail which includes many data that change every week.
I'm new in Apps Script and I would like to add an image at the end of the e-mail.
Here the code :
// Drive where is stored the image
const folder = DriveApp.getFolderById("1XXXXXXXXX");
// Retrieve ID file where is stored the image
const file = folder.getFilesByName("file")
const fileIDs = [];
while (file.hasNext()) {
var files = file.next();
fileIDs.push(files.getId());
}
var ssFile = SpreadsheetApp.openById(fileIDs[0]);
SpreadsheetApp.setActiveSpreadsheet(ssFile);
//Spreadsheet
var mail = ssFile.getSheetByName("Mail");
//Retrieve image from the spreadsheet
var retrieveImage = mail.getImages()[0];
var arrayImage = new Array();
var image = {};
arrayImage[0] = retrieveImage.getAs('image/png')
image["image"+0] = arrayImage[0];
//Fonction to send mail
function sendEmailS(){
var message = "Test";
message += "<img src='cid:image" +0+ "'> <br>";
GmailApp.sendEmail("email#email.com", "subject", "",
{
htmlBody: message,
inlineImages: image
}
);
}
I've got the error that getAs is not a function. Could help me or give me any clue to finish my script ?
Issue and workaround:
From your showing script and the error of I've got the error that getAs is not a function., I thought that the reason for your issue is due to that the image cannot be retrieved as a blob from Spreadsheet.
In the current stage, unfortunately, there is no method for directly retrieving the image on Spreadsheet as a blob. So, in this answer, I would like to propose a workaround. In this workaround, a Google Apps Script library is used. This library supports for the processes that the current Google services cannot directly achieve.
Usage:
1. Install Google Apps Script library.
Please install DocsServiceApp of Google Apps Script library. You can see how to install it at here.
2. Modified script.
When your script is modified using this library, it becomes as follows.
function sendEmailS() {
// Drive where is stored the image
const folder = DriveApp.getFolderById("1XXXXXXXXX");
// Retrieve ID file where is stored the image
const file = folder.getFilesByName("file")
const fileIDs = [];
while (file.hasNext()) {
var files = file.next();
fileIDs.push(files.getId());
}
var ssFile = SpreadsheetApp.openById(fileIDs[0]);
SpreadsheetApp.setActiveSpreadsheet(ssFile);
//Spreadsheet
var mail = ssFile.getSheetByName("Mail");
//Retrieve image from the spreadsheet
var retrieveImage = mail.getImages()[0];
var arrayImage = new Array();
var image = {};
const anchor = retrieveImage.getAnchorCell().getA1Notation();
const res = DocsServiceApp.openBySpreadsheetId(fileIDs[0]).getSheetByName("Mail").getImages();
const obj = res.find(({ range: { a1Notation } }) => a1Notation == anchor);
if (!obj) return;
arrayImage[0] = obj.image.blob;
image["image" + 0] = arrayImage[0];
//Fonction to send mail
var message = "Test";
message += "<img src='cid:image" + 0 + "'> <br>";
GmailApp.sendEmail("email#email.com", "subject", "",
{
htmlBody: message,
inlineImages: image
}
);
}
3. Testing.
When this script is run, an image of mail.getImages()[0] is retrieved as a blob. And, an email is sent using the retrieved image blob.
Reference:
DocsServiceApp of Google Apps Script library
Maybe some ideas here for you?....
...this gets image file from G.drive and emails it...
function emailImage(){
fileList = DriveApp.getFilesByName('imageNameInDrive.jpg');
while (fileList.hasNext()) { image = fileList.next().getId(); }
var insertImage = DriveApp.getFileById(image).getBlob();
var message = 'Test<br>';
message += '<img src="cid:insertImage" > <br>';
GmailApp.sendEmail("email#gmail.com", "subject", "",
{
htmlBody: message,
inlineImages: {
insertImage: insertImage
}
}
);
}
In addition to Tanaike's answer, which in my opinion would be a good workaround, there is an open Feature Request for converting Spreadsheet images to BlobSource.
Remember to hit the +1 button to tell Google that you are also interested.
Update OverGridImage to support BlobSource interface

Blob would be exported before changes are made to the files

I wrote a google script to generate resume and cover letter faster. It does the following
use ui.prompt to input content
create a folder and copy docs to the folder
replace keyword to content
convert docs to pdf in the folder
However, somehow the docs would be converted to PDFs before the keywords were replaced.
I have tried below but to no avail:
async awaits to wait for the main function before starting exporting PDFs
use utilities.sleep() to delay the exports
I have the main function and export function below for your information.
function jobHuntAutomation() {
//UI prompts for documents
var ui = DocumentApp.getUi();
var companyName = ui.prompt('Cover Letter', 'Enter Company Name: \n', ui.ButtonSet.OK);
var positionName = ui.prompt('Cover Letter', 'Enter Position Name: \n', ui.ButtonSet.OK);
var hiringManagerName = ui.prompt('Cover Letter', 'Enter Hiring Manager Name: \n', ui.ButtonSet.OK);
//Create some file name strings
var folderName = `${companyName.getResponseText()}_${positionName.getResponseText()}`
var coverLetterName = `${companyName.getResponseText()}_${positionName.getResponseText()}_Coverletter`
var resumeName = `${companyName.getResponseText()}_${positionName.getResponseText()}_Resume`
//Get new folder object and Id
var newFolder = DriveApp.getFolderById(mainFolderId).createFolder(folderName);
var newFolderId = newFolder.getId();
var newFolderUrl = newFolder.getUrl()
//Make a copy
console.log('Making Copies')
DocumentApp.getUi().showModalDialog(modalMessage('Making Copies...'), 'Status');
var coverLetterId = DriveApp.getFileById(coverLetterTemplateId).makeCopy(coverLetterName, newFolder).getId();
var resumeId = DriveApp.getFileById(resumeTemplateId).makeCopy(resumeName, newFolder).getId();
//Status
DocumentApp.getUi().showModalDialog(modalMessage('Updating Content...'), 'Status');
//Get the coverLetter document body as a variable
var coverLetterBody = DocumentApp.openById(coverLetterId).getBody();
//Get the resume document body as a variable
var resumeBody = DocumentApp.openById(resumeId).getBody();
//Update Content
doc.replaceText(keyword, input.getResponseText())
.
.
.
//you get the idea
//export PDFs
convertToPdf(resumeId, newFolder)
convertToPdf(coverLetterId, newFolder)
DocumentApp.getUi().showModalDialog(folderLink(newFolderUrl), 'Link');
}
function convertToPdf(fileId, dest) {
doc = DriveApp.getFileById(fileId);
docblob = doc.getAs('application/pdf');
console.log(`Converting ${doc.getName()}...`)
/* Add the PDF extension */
docblob.setName(doc.getName() + ".pdf");
// add file to the dest Folder
dest.createFile(docblob);
}
It would be much appreciated if someone knows a solution to export PDFs AFTER the keywords are replaced. TIA
I figured it out. the docs need to be saveAndClose() before exporting so the changes would be flushed

Sending only one sheet or active sheet as pdf via email

Hello I have been trying to write my first script to send just one of the sheet from one of my google document but everytime I end up sending all the sheets instead. The document has a main page daily where i have the list of emails and then 7 sheets called monday, tuesday, etc
This is the code i use - can you help?
function emailActivesheetAsPDF() {
DocumentApp.getActiveDocument();
DriveApp.getFiles();
const ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1_3VpJaFliJVYt1JL3cLSKw2ftsmsLeDjznHJ66-wq9w/edit");
const value = ss.getSheetByName("Daily").getRange("B5").getValue();
const email = value.toString();
const subject = 'Yesterday Handover';
const body = "Please find attached yesterday handover";
const url = 'https://docs.google.com/spreadsheets/d/1_3VpJaFliJVYt1JL3cLSKw2ftsmsLeDjznHJ66-wq9w/export?';
const exportOptions =
'exportFormat=pdf&format=pdf'
'&size=legal'
'&portrait=true'
'&fitw=false'
'&sheetnames=false&printtitle=false'
'&pagenumbers=false&gridlines=false'
'&fzr=false'
'&gid=1263775066';
var params = {method:"GET",headers:{"authorization":"Bearer "+ ScriptApp.getOAuthToken()}};
var response = UrlFetchApp.fetch(url+exportOptions, params).getBlob();
GmailApp.sendEmail(email, subject, body, {
htmlBody: body,
attachments: [{
fileName: "Handover" + ".pdf",
content: response.getBytes(),
mimeType: "application/pdf"
}]
});
}
Explanation / Issue:
You forgot to concatenate the options in the exportOptions variable and therefore exportOptions gets only the value of &size=legal and ignores the rest of the parameters including gid which you specify the sheet id. One solution is to concatenate the options via +.
I also improved your code to make it more flexible. For example, instead of defining a sheet by its gid in the code, you can define it by its name in the variable sheetToPrint and then GAS will get its gid and use it as an export option for the pdf.
Solution:
function emailActivesheetAsPDF() {
const sheetToPrint = "Daily"; // name of the sheet to print
const ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1_3VpJaFliJVYt1JL3cLSKw2ftsmsLeDjznHJ66-wq9w/edit");
const ssID = ss.getId();
const email = ss.getSheetByName("Daily").getRange("B5").getValue();
const subject = 'Yesterday Handover';
const body = "Please find attached yesterday handover";
const shID = ss.getSheetByName(sheetToPrint).getSheetId();
const url = "https://docs.google.com/spreadsheets/d/"+ ssID + "/export?format=pdf&id="+ssID;
const exportOptions =
'&size=legal'+
'&portrait=true'+
'&fitw=false'+
'&sheetnames=false&printtitle=false'+
'&pagenumbers=false&gridlines=false'+
'&fzr=false'+
'&gid='+shID;
var params = {method:"GET",headers:{"authorization":"Bearer "+ ScriptApp.getOAuthToken()}};
var response = UrlFetchApp.fetch(url+exportOptions, params).getBlob();
GmailApp.sendEmail(email, subject, body, {
htmlBody: body,
attachments: [{
fileName: "Handover" + ".pdf",
content: response.getBytes(),
mimeType: "application/pdf"
}]
});
}

Save Gmail attachments on Google Drive

I would like to save gmail attachments(.pdf files) from a specific email in a specific Google Drive folder. I also need to rename file with a string composed by some string of the email.
I have developed a simple script using Google Apps Script with some functions.
This is the main function I have wrote:
function GmailToDrive() {
var query = '';
query = 'in:inbox from:noreply#agyo.io has:nouserlabels ';
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var parentFolder;
if (threads.length > 0) {
parentFolder = getFolder_(folderName);
}
var root = DriveApp.getRootFolder();
for (var i in threads) {
var mesgs = threads[i].getMessages();
for (var j in mesgs) {
//get attachments
var attachments = mesgs[j].getAttachments();
var message_body = mesgs[j].getBody();
for (var k in attachments) {
var attachment = attachments[k];
var isDefinedType = checkIfDefinedType_(attachment);
if (!isDefinedType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
file.setName(renameFile_(attachment, message_body))
parentFolder.addFile(file);
root.removeFile(file);
}
}
threads[i].addLabel(label);
}
}
The checkIfDefinedType_(attachment) function checks if the attachments is a .pdf file and the renameFile_(attachment, message_body) rename the attachment extracting some string from the email.
The script seems to be correctly developed but sometimes I have two or more same attachments saved in my google drive folder.
Stefano, I had the same issue, if this is the same code as adapted from here.
I removed the line for (var i in fileTypesToExtract) { which was causing duplicates for me. It was running the query for each of the file types.
// `has:pdf` searches for messages with PDF attachments
var query = 'has:pdf in:inbox from:noreply#agyo.io has:nouserlabels ';
var results = Gmail.Users.Messages.list(userId, {q: query});
results.messages.forEach(function (m) {
var msg = GmailApp.getMessageById(m.id);
msg.getAttachments().forEach(function (a) {
var fileName = a.getName();
fileName = saveAttachmentToFolder(folder, a, fileName, msg.getDate(), input.tz);
});
});
function saveAttachmentToFolder(folder, attachment, fileName, date, timezone) {
if (timezone) {
fileName = standardizeName(attachment, date, timezone);
}
Logger.log(fileName);
folder.createFile(attachment.copyBlob()).setName(fileName);
}
The code snippet above is based on a Gmail add-on that I created, specifically for saving attachments to labeled folders in Drive: https://github.com/ellaqezi/archiveByLabel/blob/main/Code.gs#L24
In the label field, you can define nested directories to create in Drive e.g. foo/bar.
In the query field, you can copy the parameters as you would use them in Gmail's search bar.

Categories