Adding multiple attachments to email - google-apps-script

I have a script which generates a document, saves as a PDF and attaches to an email. In addition to this attachment, I'm trying to add a second attachment which is an existing PDF saved on google drive (terms). Why is the following code not attaching the second document?
if (email_status == "YES" ) {
//send a pdf copy to customer
var pdfEMAIL = DriveApp.getFileById(doc.getId()).getAs('application/pdf').getBytes();
var terms = DriveApp.getFileById('file ID');
var message = "Hi " + usernamefordoctitle + "!, please kindly find your invoice attached.\nMany Thanks!\nMe";
var emailAdd = sheet.getRange("D2").getValue()
var emailTo = emailAdd; // add customer email here
var subject = "Invoice for " + usernamefordoctitle + " from ME" + " - Invoice Number : " + newInvNumber;
var attach = {fileName:"INVOICE " + newInvNumber + " " + usernamefordoctitle + '.pdf',content:pdfEMAIL, mimeType:'application/pdf'};
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach, terms.next()]});
ss.toast("70%: emailed customer");
Utilities.sleep(sleepINT);
}

You have
terms = DriveApp.getFileById('file ID');
followed by
terms.next()
This is incorrect, because getFileById gets you one specific file with the given Id. You were thinking of other methods like getFilesByName which return a file iterator. The name of the method is a clue to what it returns: getFile versus getFiles.
So, simply attaching
{attachments:[attach, terms]}
will work. You may also want to specify a MimeType like
terms.getAs(MimeType.PDF)
so that, e.g., you send a Google Doc as a PDF.

Related

Script to Generate PDF from Google Form Submission

I'd previously written a script for a google sheet that triggered when its related google form had a form submitted. When the form was submitted, a PDF would generate and be emailed to the designated person. I created a new version to auto-generate a Digital Millennium Copyright Act notice, but something seems to not work with the script anymore (the original isn't working anymore either) and I can't figure out how to fix it.
The error I'm getting is TypeError: Cannot read property 'range' of undefined (line 2, file "Code")
9/28/2021 I added console.log(rg.getA1Notation()) to the code as instructed and submitted a form. The Execution log for it shows me the below -
Code below -
const rg = e.range;
console.log(rg.getA1Notation());
const sh = rg.getSheet();
//Get all the form submitted data
const Email= e.namedValues['Email Address'][0];
const LinkOrig = e.namedValues['Link(s) for where the original work appears'][0];
const AttachOrig = e.namedValues['Copies of the original copyrighted work'][0];
const Domain = e.namedValues['Infringing Domain'][0];
const LinkInfring = e.namedValues['Link(s) for where infringing image appears online'][0];
const Contact = e.namedValues['Contact Information'][0];
const WHOIS = e.namedValues['WHOIS Search results'][0];
const Date = e.namedValues['Date'][0];
const Location = e.namedValues['Where are you based?'][0];
//Build a new DMCA Form from the template
//Folder ID (save destination) and file IDs (template ID + new doc ID)
const DMCAFolderID = 'googledrivefolderidhere';
const DMCALibFolder = DriveApp.getFolderById(DMCAFolderID);
const TemplateFileID = 'googledrivetemplateidhere';
const newFilename = 'DMCA Notice -' + TemplateFileID + 'Domain';
//Make a copy of the template file
const newTemplateFileID = DriveApp.getFileById(TemplateFileID).makeCopy(newFilename, DMCALibFolder).getId();;
//Get the DMCA Notice body into a variable
var document = DocumentApp.openById(newTemplateFileID);
var body = document.getBody();
//Replace all the {{ }} text in the BlinkLib body
body.replaceText('{{LinkOrig}}', LinkOrig);
// body.replaceText('{{AttachOrig}}', AttachOrig);
body.replaceText('{{LinkInfring}}', LinkInfring);
body.replaceText('{{ContactInfo}}', Contact);
body.replaceText('{{WHOISResults}}', WHOIS);
body.replaceText('{{date}}', Date);
body.replaceText('{{location}}', Location);
document.saveAndClose();
// define email variables
var subject = 'DMCA Notice - ' + Domain;
var msgHtml =
"Hi " + Name + "," + "<br/>" + "<br/>" +
"Please find your DMCA Notice attached." + "<br/>" + "<br/>" +
"Sincerely," + "<br/>" +
"Your Bada** Self" + "<br/>"
;
var attachment = DriveApp.getFileById(newTemplateFileID);
//send email with the file
GmailApp.sendEmail(Email, subject, msgHtml, {htmlBody: msgHtml, attachments: [attachment.getAs(MimeType.PDF)]});
} ```
Cannot read property 'range' of undefined indicates that you are trying to run your code manually
However, const rg = e.range; indicates that you are retrieving the range as an event object - which can be retrieved only when a trigger fires
Summary:
When using event objects like e.range you cannot run your code manually, you need to let the execution be triggered automatically. This implies that your funciton is being called on a trigger - e.g. an onEdit trigger.

Get file from Google Drive based of Google Form info submission

I have created a Google Form containing two fields (First Name, Last Name)
I have PDF files in Google Drive/specific folder, PDF files named as "First Name"+"Last Name"
I want to get PDF file from my Google Drive based on form submission and get the URL to be sent by email.
I need the way to get PDF file based on form user submission.
I have written the below code which shall send the PDF as email attachment in place of the URL of PDF. I hope this code can help you. This code is a very basic code which can be further advanced as per your needs. Please let me know if you need any further help.
function pdfextractor() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Get active spreadsheet.
var lastrow = ss.getLastRow();
var sheetName = ss.getRange("B" + lastrow).getValue() + " " + ss.getRange("C" + lastrow).getValue(); //considering format 'FirstName LastName' stored in column B & C of spreasheet generated by submission of google forms.
var pdfName = sheetName + ".pdf";
ss.getRange("D" + lastrow).setValue(pdfName);
var files = DriveApp.getFilesByName(pdfName);
var body = "This is a test email";
var subject = "PDF File "
var email = "admin#gmail.com"
while (files.hasNext()) {
var file = files.next();
}
GmailApp.sendEmail(email, subject, body, {
attachments: [file]
});
}
You can also change the email of each mail you want to send if stored with the sheet.
One way to do it is to create trigger that fires when the form is submitted. Based on this event, you can get the responses from the form submission, use them to search the file in Drive and send it attached to an email.
Here's an example that you can adapt to build what you need:
function onSubmit(e) {
// Get submitted responses
let itemResponses = e.response.getItemResponses();
// Get form answers (assume the form has three question in this order: First Name, Last Name, Email)
let firstName = itemResponses[0].getResponse();
let lastName = itemResponses[1].getResponse();
let email = itemResponses[2].getResponse();
// Get file (first file found with specified name)
let searchResult = DriveApp.searchFiles(`title contains \'${firstName+lastName}\'`);
let respondentFile = searchResult.next();
if (!respondentFile) throw 'File not found';
// Send email with respondent's file
MailApp.sendEmail(email, 'Your PDF File', 'Your PDF file is attached.', {
name: 'Automatic Emailer Script',
attachments: [respondentFile.getAs(MimeType.PDF)]
});
}

Google Spreadsheet Cannot retrieve the next object: iterator has reached the end

I have a Spreadsheet that I use to make a list of worked hours per client. I use a script to make a pdf of the sheet and directly places the pdf in the client folder that is related to the pdf it just created. It knows which folder it needs to use by the client name that it takes from the spreadsheet (name in Spreadsheet is the same as the folder name).
Everything goes super smooth... As long as the client name is 1 word (for example WeSellApples), as soon as the client name has more words (for example We Do Not Sell Apples) I get the error Cannot retrieve the next object: iterator has reached the end.
Because everything works perfect with one-word names I have no clue why this happens.
function createPDF() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet();
var gebruikSheet1 = sheet1.getSheetByName("Urenregistratie maak pdf");
var ui = SpreadsheetApp.getUi();
var result = ui.alert( // Asks if I'm sure I want to save
"Opslaan als PDF?",
"Sla " +sheet1.getName()+ " " + "week " + gebruikSheet1.getRange("L6").getValue() +
" " + gebruikSheet1.getRange("C6").getValue() +".pdf op als PDF",
// Shows the name so I can double check if I selected the right client,
// client name is in C6 en weeknumber in L6. Both cell values are dynamic (with Vlookup)
ui.ButtonSet.OK_CANCEL);
if (result == ui.Button.OK) {
var hideSheet = sheet1.getSheetByName('Uren bijhouden');
// to make sure only the second sheet is exported as pdf
hideSheet.hideSheet();
var docblob = SpreadsheetApp.getActiveSpreadsheet().getAs("application/pdf");
docblob.setName(sheet1.getName()+ " " + "week " + gebruikSheet1.getRange("L6").getValue() +
" " + gebruikSheet1.getRange("C6").getValue() + ".pdf");
var file = DriveApp.createFile(docblob);
ui.alert(sheet1.getName()+ " " + "week " + gebruikSheet1.getRange("L6").getValue()
+ " " + gebruikSheet1.getRange("C6").getValue() + " is opgeslagen");
} else {
ui.alert("Opslaan als .pdf is niet gelukt");
}
hideSheet.showSheet();
// file to move
var klantmap = gebruikSheet1.getRange("C6").getValue();
// the client name is in cell C6
var id = DriveApp.getFoldersByName(klantmap).next().getId();
// script can find id of client folder without problem as long as client name
// is one word
// this is the line where the error occures when client name contains more words
Logger.log(id);
DriveApp.getFolderById(id).addFile(file); // add file to client folder
DriveApp.getRootFolder().removeFile(file); // removes file from root folder
}
I would like to recieve some tips where to look to figure out the problem myself.
After regrouping here a bit I noticed that:
this line: var docblob = SpreadsheetApp.getActiveSpreadsheet().getAs("application/pdf");
I think it should be like this: var docblob = SpreadsheetApp.getActiveSpreadsheet().getBlob().getAs("application/pdf");

I would like to populate an email message with the contents of a specific, static, cell in sheets

I am sending a confirmation email when someone signs up for an appointment on a Google form. I would like to have the email message be populated with the contents of a specific cell. This means I would be able to type an email message into cell A2 and when someone fills out a form, it will send that message to them as the confirmation email.
I have two different email messages I am using, confirmation email and reminder email. I would like both codes to populate the same message.
function confirmationEmail(e) {
var userName = e.values[2];
var userEmail = e.values[3] + ";user#gmail.com";
var date = e.values[4];
var studentName = e.values[1];
var body1 = "Hello " + userName + ",";
var body2 = studentName + " has been registered for a physical at High School for the following date/time:";
var body3 = "Please park in the large student parking lot on the east side of the High School.";
var body4 = "Thanks!";
var signature = "Name" + "\n" + "High School" + "\n" + "user#gmail.com" + "\n" + "***-***-****";
var file1 = DriveApp.getFilebyId("1WDxic1meHzEGSjybJ2SS1h2MqGAhIAK4");
var file2 = DriveApp.getFilebyId("1v4GQAP8PkTPQRPoYIdsEOMBr2ZvRO1eocsqixyZ42gA");
GmailApp.sendEmail(userEmail,
"Registration",
body1 + "\n\n" + body2 +"\n\n" + date + "\n\n" + body3 + "\n\n" + body4 + "\n\n" + signature,
{attachments:[file1, file2]})
}
This code works perfectly already, however, I have some co-workers even less savvy than I. It would work best if they could just fill in the cell with the contents of the message to be able to send this out. So ideally, "body3" would be written into a cell within sheets and populated into the email.
So I guess you would want something like this:
var body3 = SpreadsheetApp.getActiveSheet().getRange('A2').getValue();
You should probably include some conditional statements in the code to prevent the sending of the email in the event that cell 'A2' is blank and include an alert to inform the user that it needs to be filled in.
Perhaps something like this:
if(body1 && body2 && body3){
//send email
}else{
SpreadsheetApp.getUi().alert('Invalid or Missing Content');
}
Truthy

E-mail sheet preview with formatting in HTML body + attach sheet

I have applied this solution to e-mail the rows of a google sheet as part of the HTML body.
Unfortunately, it errors out because there is a limit set on the HTML body. The number of rows in my data is dynamic.
Limit Exceeded: Email Body Size. (line 209, file "SideBar")
Is there a way of getting around this? I would be OK with providing a preview of the rows, let's say 10 rows with all columns, on the HTML body and then providing a link to view the rest. Because the content on the sheet changes, the link should not be to that sheet. Instead I was thinking of saving a copy of the sheet as a new file on their own drive and linking to that. Another option is attaching an HTML file that has all the rows.
Here is what I currently have:
function emailBreakdown(emailUser, bodyAdd){
SpreadsheetApp.getActiveSpreadsheet().toast('Please wait while information is refreshed.');
if(emailUser == null){emailUser = 'xxxxx#yyyyyy.com'}
if(bodyAdd == null){bodyAdd = 'Testing running of function on script editor'}
var ss = SpreadsheetApp.getActive();
var detailsSht = ss.getSheetByName("Details");
var dataRange = detailsSht.getDataRange();
var curDate = Utilities.formatDate(new Date(), "GMT+1", "MM/dd/yyyy");
var subject = 'Summary - Exported Data ' + curDate;
var body = '<br />---------------------------------------------------------------<br />You have received an export of a dataset from the Summary dashboard. Please see below:<br /><br />' //provide link to the whole dashboard.
convSheetAndEmail(dataRange, emailUser, body, bodyAdd, subject);
}
function convSheetAndEmail(rng, emailUser, body, bodyAdd, subject){
var HTML = SheetConverter.convertRange2html(rng);
MailApp.sendEmail(emailUser, subject, '', {htmlBody : bodyAdd + body + HTML});
}
The following is code I've been able to assemble through further research. It works well and addresses my requests. Here is what it does:
Attaches a sheet. In this case an xls file. Chose this over HTML. To allow user to manipulate in excel if needed.
Provides a preview of 10 lines as HTML in the body of the e-mail.
Preview and attached file preserves format.
What it does not address:
Save file to user's personal drive.
Here it goes:
function emailBreakdown(emailUser, bodyAdd){
SpreadsheetApp.getActiveSpreadsheet().toast('Please wait while information is refreshed.');
//If running on script editor the variables will not be transferred. Assign values below:
if(emailUser == null){emailUser = 'xxxxxx#yyyyyyy.com'}
if(bodyAdd == null){bodyAdd = 'Testing running of function on script editor'}
var ss = SpreadsheetApp.getActive();
var detailsSht = ss.getSheetByName('Details');
var dataRange = detailsSht.getRange('A1:FS11'); //For the preview we are only looking at the first 10 rows of data.
var curDate = Utilities.formatDate(new Date(), 'GMT+1', 'MM/dd/yyyy');
//Gather data to convert specific sheet to excel document so it can be attached to the e-mail
var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var sheetID = detailsSht.getSheetId().toString()
var requestData = {'method': "GET", 'headers':{'Authorization':'Bearer ' + ScriptApp.getOAuthToken()}};
var url = 'https://docs.google.com/spreadsheets/d/' + ssID + '/export?format=xlsx&id=' + ssID + '&gid=' + sheetID;
var result = UrlFetchApp.fetch(url , requestData);
var contents = result.getContent();
//Assemble E-mail components
var subject = 'Summary - Exported Data ' + curDate;
var body = bodyAdd +
'<br /><br /><hr style="border: none;border-top: 3px double #333;color: #333;overflow: visible;text-align: center;height: 5px;"><br />' +
'You have received an export of a dataset from the Summary dashboard. Below is a preview of the dataset:<br /><br />'
var afterBody = '<br /><br /><b>You can view the full dataset through the attached XLS file.</b>'
convSheetAndEmail(ss, contents, dataRange, emailUser, body, afterBody, subject);
};
function convSheetAndEmail(ss, contents, rng, emailUser, body, afterBody, subject){
var HTML = SheetConverter.convertRange2html(rng);
//Send email
MailApp.sendEmail(
emailUser,
subject,
'',{
htmlBody : body + HTML + afterBody,
name: 'Full Data Export',
attachments:[{fileName:'Export Data - ' + ss.getName() + '.xls', content:contents, mimeType:'application//xls'}]
}
);
};
Apart from the resource listed in the question, I also borrowed code from here.