Script to Generate PDF from Google Form Submission - google-apps-script

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.

Related

Generate & send a png or jpg from a Google Form Submission (script attached to Google Sheet that references a Google Doc template)

I have a google form survey that I send to my app members. I want to include a way for them to create a custom Instagram story post that I'd auto-generate based on their input and email to them to post. The template I use to auto-generate it is a google doc with a placeholder where I plug in their custom input.
My script currently is able to send them the post as a PDF, but that's really frustrating to post on an Instagram story (if not impossible - I spent 20 mins trying to do it).
Is it possible to have the file send as a jpg or png? Or to add a step converting the PDF to a jpg or png?
function onSubmit(e) {
const rg = e.range;
const sh = rg.getSheet();
//Get all the form submitted data
//Note: This data is dependent on the headers. If headers, are changed update these as well.
const Email = e.namedValues["What's your email address?"][0];
const Name = e.namedValues["What's your name?"][0];
const Text = e.namedValues["If you want to make a custom Instagram story sharing what your experience was like, feel free to drop your text here!"][0];
//Build a new story from the template
//Folder ID (save destination) and file IDs (template ID + new doc ID)
const StoryFolderID = '1ghiNSWwkUS7WJ5wwnEON5g0-zRueYlfd';
const StoryFolder = DriveApp.getFolderById(StoryFolderID);
const TemplateFileID = '1IJ-i8VeazzmWlxyjyXXKHCLrA3pT1fgu4Zy_m74OgHc';
const newFilename = 'Story -' + TemplateFileID;
//Make a copy of the template file
const newTemplateFileID = DriveApp.getFileById(TemplateFileID).makeCopy(newFilename, StoryFolder).getId();;
//Get the body into a variable
var document = DocumentApp.openById(newTemplateFileID);
var body = document.getBody();
//Replace all the {{ }} text in the story body
body.replaceText('{{text}}', Text);
document.saveAndClose();
// define email variables
var subject = 'Instagram Story';
var img = DriveApp.getFileById("1d2jofSzEsxuUUEw5g-B684AKMALqyy-b").getBlob();
var msgHtml =
"Hi " + Name + "!" + "<br/>" + "<br/>" +
"Please find your custom story image attached." ;
var attachment = DriveApp.getFileById(newTemplateFileID);
//send email with the file
GmailApp.sendEmail(Email, subject, msgHtml, {htmlBody: msgHtml, inlineImages: {sampleImage: img}, attachments: [attachment.getAs(MimeType.PDF)]});
}
About Is it possible to have the file send as a jpg or png? Or to add a step converting the PDF to a jpg or png?, in this case, I thought that my this blog might be useful. In this blog, a PDF data is converted to PNG images for every page. When this is reflected in your script, how about the following modification?
Modified script:
In this sample, Drive API is used. So, please enable Drive API at Advanced Google services.
/**
* This is a method for converting all pages in a PDF file to PNG images.
* PNG images are returned as BlobSource[].
* IMPORTANT: This method uses Drive API. Please enable Drive API at Advanced Google services.
*
* #param {Blob} blob Blob of PDF file.
* #return {BlobSource[]} PNG blobs.
*/
async function convertPDFToPNG_(blob) {
// Convert PDF to PNG images.
const cdnjs = "https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js";
eval(UrlFetchApp.fetch(cdnjs).getContentText()); // Load pdf-lib
const setTimeout = function (f, t) {
// Overwrite setTimeout with Google Apps Script.
Utilities.sleep(t);
return f();
};
const data = new Uint8Array(blob.getBytes());
const pdfData = await PDFLib.PDFDocument.load(data);
const pageLength = pdfData.getPageCount();
console.log(`Total pages: ${pageLength}`);
const obj = { imageBlobs: [], fileIds: [] };
for (let i = 0; i < pageLength; i++) {
console.log(`Processing page: ${i + 1}`);
const pdfDoc = await PDFLib.PDFDocument.create();
const [page] = await pdfDoc.copyPages(pdfData, [i]);
pdfDoc.addPage(page);
const bytes = await pdfDoc.save();
const blob = Utilities.newBlob(
[...new Int8Array(bytes)],
MimeType.PDF,
`sample${i + 1}.pdf`
);
const id = DriveApp.createFile(blob).getId();
Utilities.sleep(3000); // This is used for preparing the thumbnail of the created file.
const link = Drive.Files.get(id, { fields: "thumbnailLink" }).thumbnailLink;
if (!link) {
throw new Error(
"In this case, please increase the value of 3000 in Utilities.sleep(3000), and test it again."
);
}
const imageBlob = UrlFetchApp.fetch(link.replace(/\=s\d*/, "=s1000"))
.getBlob()
.setName(`page${i + 1}.png`);
obj.imageBlobs.push(imageBlob);
obj.fileIds.push(id);
}
obj.fileIds.forEach((id) => DriveApp.getFileById(id).setTrashed(true));
return obj.imageBlobs;
}
async function onSubmit(e) {
const rg = e.range;
const sh = rg.getSheet();
//Get all the form submitted data
//Note: This data is dependent on the headers. If headers, are changed update these as well.
const Email = e.namedValues["What's your email address?"][0];
const Name = e.namedValues["What's your name?"][0];
const Text = e.namedValues["If you want to make a custom Instagram story sharing what your experience was like, feel free to drop your text here!"][0];
//Build a new story from the template
//Folder ID (save destination) and file IDs (template ID + new doc ID)
const StoryFolderID = '1ghiNSWwkUS7WJ5wwnEON5g0-zRueYlfd';
const StoryFolder = DriveApp.getFolderById(StoryFolderID);
const TemplateFileID = '1IJ-i8VeazzmWlxyjyXXKHCLrA3pT1fgu4Zy_m74OgHc';
const newFilename = 'Story -' + TemplateFileID;
//Make a copy of the template file
const newTemplateFileID = DriveApp.getFileById(TemplateFileID).makeCopy(newFilename, StoryFolder).getId();;
//Get the body into a variable
var document = DocumentApp.openById(newTemplateFileID);
var body = document.getBody();
//Replace all the {{ }} text in the story body
body.replaceText('{{text}}', Text);
document.saveAndClose();
// define email variables
var subject = 'Instagram Story';
var img = DriveApp.getFileById("1d2jofSzEsxuUUEw5g-B684AKMALqyy-b").getBlob();
var msgHtml =
"Hi " + Name + "!" + "<br/>" + "<br/>" +
"Please find your custom story image attached.";
var attachment = DriveApp.getFileById(newTemplateFileID);
const imageBlobs = await convertPDFToPNG_(attachment.getBlob());
//send email with the file
GmailApp.sendEmail(Email, subject, msgHtml, { htmlBody: msgHtml, inlineImages: { sampleImage: img }, attachments: imageBlobs });
}
When onSubmit is run, the PDF data of attachment.getBlob() is converted to PNG images and the PNG images are used with attachments: imageBlobs.
Reference:
Converting All Pages in PDF File to PNG Images using Google Apps Script (Author: me)

Auto-Generate PDF with Image (actual image, not link) and Data Input in Google Form

I'm creating a google form one can fill out to auto-generate a Digital Millennium Copyright Act notice as a PDF. The notice requires the original image in question to be included, so the form includes an upload file question. I want the generated PDF to include the actual image that was uploaded in the form, but right now all I get is the google drive link to the file's location.
How can I get the actual image to appear in the PDF? I'm attaching a screenshot of the Google Form, email output, and PDF template (marked with where the image should go) for reference.
Here's the script I have on the Google Sheet that's populated by the Google Form submissions:
function onSubmit(e) {
const rg = e.range;
console.log(rg.getA1Notation());
const sh = rg.getSheet();
//Get all the form submitted data
//Note: This data is dependent on the headers. If headers, are changed update these as well.
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 = 'folderidhere';
const DMCALibFolder = DriveApp.getFolderById(DMCAFolderID);
const TemplateFileID = 'templateidhere';
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 template 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," + "<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)]});
}```
Assuming that {{AttachOrig}} is your placeholder and AttachOrig the URL of the file
You need to:
Obtain the image blob from the URL by opening it with DriveApp and getting the blob
Find the placeholder in your template
Replace the text by an empty string
Find the element that contains the placeholder
Get the element's parent paragraph
Insert the image blob into the parent paragraph
Sample:
var AttachOrig = "https://drive.google.com/file/d/1f8EFG6G5zNd6by_fNEecSh2D1My_p-_p/view";
function replacePlaceHolderThroughImage() {
...
var document = DocumentApp.openById(newTemplateFileID);
var body = document.getBody();
var placeholder = "{{AttachOrig}}";
var id = AttachOrig.match(/[\w\_\-]{25,}/)[0];
var img = DriveApp.getFileById(id);
var position = body.findText(placeholder);
var element = position.getElement();
element.asText().setText("");
element.getParent().asParagraph().insertInlineImage(0, img.getBlob());
}

Problems generating PDFs from Google Forms after it is submitted

I am creating a Google Script that fills a Google Doc template based on the fields of a Google Forms and sends the generated PDF via email to the user.
All of the steps that I followed are explained in detail here: Hacking it: Generate PDFs from Google Forms
The script (obtained from the article) is:
function onSubmit(e) {
const rg = e.range;
const sh = rg.getSheet();
//Get all the form submitted data
//Note: This data is dependent on the headers. If headers, are changed update these as well.
const cName = e.namedValues['Client Name'][0];
const cEmail = e.namedValues['Client Email'][0];
const cAddress = e.namedValues['Client Address'][0];
const cMobile = e.namedValues['Client Mobile'][0];
const sendCopy = e.namedValues['Send client a copy?'][0];
const paymentType = e.namedValues['What is your agreed upon payment schedule?'][0];
const fixedCost = e.namedValues['What was your agreed upon cost for the project?'][0];
const hourlyRate = e.namedValues['Hourly Rate'][0];
const manHours = e.namedValues['Total man hours'][0];
const services = e.namedValues['Select the services'][0];
//Consequential Data
const tax = 18.5
var subtotal = 0;
var taxAmt = 0;
var payableAmt = 0;
//if the user has selected hourly payment model
//Note: Be careful that the responses match the elements on the actual form
switch (paymentType ){
case 'Hourly Rate':
subtotal = hourlyRate*manHours;
taxAmt = subtotal * (tax/100);
payableAmt = +subtotal + +taxAmt;
break;
case 'Fixed Cost':
subtotal = fixedCost;
taxAmt = fixedCost * (tax/100)
payableAmt = +fixedCost + +taxAmt;
break;
}
const invoiceID = 'IN' + Math.random().toString().substr(2, 9);
var formattedDate = Utilities.formatDate(new Date(), "IST", "dd-MMM-yyyy");
//Set the consequential data in the columns of the spreadsheet for record keeping
//Note: These variable are dependent on the sheet's columns so if that changes, please update.
const row = rg.getRow();
const payableAmtCol = 2; //B
const invoiceIDCol = 3; //C
sh.getRange(row,payableAmtCol).setValue(payableAmt);
sh.getRange(row,invoiceIDCol).setValue(invoiceID);
//Build a new invoice from the file
//Folder and file IDs
const invoiceFolderID = '<invoice-folder-id>';
const invoiceFolder = DriveApp.getFolderById(invoiceFolderID);
const templateFileID = '<template-id>';
const newFilename = 'Invoice_' + invoiceID;
//Make a copy of the template file
const newInvoiceFileID = DriveApp.getFileById(templateFileID).makeCopy(newFilename, invoiceFolder).getId();;
//Get the invoice body into a variable
var document = DocumentApp.openById(newInvoiceFileID);
var body = document.getBody();
//Replace all the {{ }} text in the invoice body
body.replaceText('{{Invoice num}}', invoiceID);
body.replaceText('{{Date}}', formattedDate);
body.replaceText('{{Client Name}}', cName);
body.replaceText('{{Client Address}}', cAddress);
body.replaceText('{{Client Mobile}}', cMobile);
body.replaceText('{{Client Email}}', cEmail);
body.replaceText('{{Services}}', services.split(', ').join('\n'));
body.replaceText('{{Subtotal}}', subtotal);
body.replaceText('{{Tax Value}}', taxAmt);
body.replaceText('{{Total}}', payableAmt);
//In the case of hourly rate payment type, let's add an additional message giving the rate and the man hours.
if(paymentType.includes('Hourly Rate')){
//It should look something like this on the invoice
//Hourly Rate
//Rate of Rs.1200/hour
//Completed 50 man hours
const message = paymentType + '\nRate of Rs.' + hourlyRate + '/hour\nCompleted ' + manHours + ' man hours';
body.replaceText('{{Payment Type}}', message);
} else {
body.replaceText('{{Payment Type}}', paymentType);
}
document.saveAndClose();
//send email with the file
var attachment = DriveApp.getFileById(newInvoiceFileID);
GmailApp.sendEmail(cEmail, '<subject>,
'<body>',
{attachments: [attachment.getAs(MimeType.PDF)]});
}
The code works fine. Now I need that the user can edit its response after he press "Send Form" on Google Forms. So I decided to check "Respondents can edit after submit". Then I need to send the document again via GmailApp with the edited fields. So I created a new trigger: Edit (from a Spreadsheet). The other trigger is Form submit.
However I have a problem. When the user edits a field and press, again, "Send Form", the trigger "Edit" is activated with the following error: Failed to send email: no recipient.
If I go to the Spreadsheet responses I can see the edited row (because the cell has a comment "The person who responded has updated this value"), and the column mail is not edited but it stills throwing the exception.
How can we solve this problem if cEmail was never edited?
Searchs
I could find some interesting answers:
Failed to send email: no recipient
Google scripts “Failed to send email: no recipient” but email arrives
Failed to send email: no recipient, what is wrong with my code?
They seem to describe that a blank row can be generated when the trigger "Edit" is activated. However I don't see why this could happen, and how I can solve it since the Spreadsheet Responses is automatically edited after a new user submit an answer.
When a form response is edited the on form submit event object properties values and namedValues only include values for those questions that were edited.
To fix the error Failed to send email: no recipient, replace
GmailApp.sendEmail(cEmail, '<subject>,
'<body>',
{attachments: [attachment.getAs(MimeType.PDF)]});
by
const recipientIdx = 1; // This is the 0 based index of the column having the recipient email address
const recipient = cEmail ? cEmail : e.range.getValues().flat()[recipientIdx]
GmailApp.sendEmail(recipient , '<subject>',
'<body>',
{attachments: [attachment.getAs(MimeType.PDF)]});
P.S. Instead of hardcoding the value assigned to recipientIdx you might use some code to get it based on the column headers.
NOTE: The above only will prevent the error mentioned in the question. In order to make the script work you will have to apply the same idea for all the fields: Read the missing values from the spreadsheet by using the e.range.getValues().flat().
Related
How to check if current submission is editing response or a new response
How to check if current form submission is editing response

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.

Email Google Form Results not working

I am using a script to have form results e-mailed to me every time the form is submitted. Entire code is posted in Google Groups here:
http://groups.google.com/a/googleproductforums.com/d/topic/apps-script/1rObNfuez6k/discussion
Here's the current code:
function contactUsMailer2(e) {
// var ss = SpreadsheetApp.getActive();
// SpreadsheetApp.setActiveSpreadsheet(ss);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data'); //default sheets - change if you
var setup = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Setup'); //rename the sheets
var width = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data').getLastColumn(); //get "width" of sheet, used to enumrate fields
var myEmailAddress = setup.getRange('B3').getValue(); //get email recipient(s) from "Setup" (default)
var myEmailSubject = setup.getRange('B4').getValue(); //get a subject line from "Setup" (default)
//from row 1 of "Data"
var sheetURL = SpreadsheetApp.getActiveSpreadsheet().getUrl(); //get sheet url to include in email
var formURL = SpreadsheetApp.getActiveSpreadsheet().getFormUrl(); //get form url to include in email
var column = 'A';
var message = '';
var htmlBody = '<html><body><table>'; //generated email will have both an HTML table and a plain-text part
//to be RFC compliant. beginning of html and table.
for (var c=1; c<=width; ++c) {
var data = sheet.getRange(1, c, 1, 1).getValue(); //this gets the "filed" names from the 1st row of "Sheet1"
try {
//retrieve field data and build the HTML and plain-text emails
var message = message + data + ': ' + e.namedValues[data].toString() + '\r\n'; //plain-text
var htmlBody = htmlBody + '<tr><td><b>' + data + ': </b></td><td>' + e.namedValues[data].toString() + '</td></tr>'; //HTML
}
catch (a) {
var message = message + data + ': n/a\r\n';
var htmlBody = htmlBody + '<tr><td><b>' + data + ': </b></td><td>n/a</td></tr>';
}
var column = String.fromCharCode(column.charCodeAt() + 1); //increment column letter
}
var htmlBody = htmlBody + '</table>'; //close table
var message = message + '\r\n\r\nData from form located at ' + formURL + '\r\n'; //put form url in text email
var message = message + 'Data posted to spreadsheet at ' + sheetURL; //put sheet url in text email
var htmlBody = htmlBody + '<br><br>Data from form located here<br>'; //put form url in HTML email
var htmlBody = htmlBody + 'Data posted to spreadsheet here<br>'; //put sheet url in HTML email
var htmlBody = htmlBody + '</body></html>'; //close html
MailApp.sendEmail(myEmailAddress, myEmailSubject, message, {htmlBody: htmlBody}) ; //send the email
}
function showid() {
Browser.msgBox("The sheet ID is:\r\n\r\n" + SpreadsheetApp.getActiveSpreadsheet().getId());
}
Script now works fine with the original spreadsheet, but when I try to use it with a new form and spreadsheet it fails to send the e-mail. The form fill in the spreadsheet just fine and we get the usual e-mail stating that the spreadsheet has been updated, but the script e-mail doesn't come through.
Funny thing is when I debug the script it does send the e-mail, just null entries in all fields (which is what I would expect). Debug doesn't report any errors in the script itself. What am I missing?
Have you authorized the script in your new form ? Since your script is required to send out an email it requires explicit authorization.
Try removing the quote around
for (var c=1; c<=width; ++c) {
var data = sheet.getRange(column + '1').getValue(); //this gets the "filed" names from the 1st row of "Sheet1"
I do something similar in a script and the following works for me.
var rangeval = "B"+(i+1);
var nameFld = filesheet.getRange(i+1,1).getValue();
Joe
Make sure trigger is set to "On Submit". Mine was set to "On Open".