Hi I am trying to copy a template and want it to be shared with the same people (similar to how we do it manually). I am a total noob and get by with just doing the basic stuff, so I am not able to fix
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('Create My Checklist');
menu.addItem('New Checklist', 'createNewGoogleDocs')
menu.addToUi();
}
function createNewGoogleDocs() {
//id of the document template
const googleDocTemplate = DriveApp.getFileById('1qRQ07PDmz1il9IftM9GIJfY37vTusfQZhhNS1BRELJQ');
//id of the folder where the completed documents stored
const destinationFolder = DriveApp.getFolderById('1JufckhwXlAXDAE3_-f60lHQQ-jqe9Mx1')
//Here we store the sheet as a variable
const sheet = SpreadsheetApp
.getActiveSpreadsheet()
.getSheetByName('Data')
//get all of the values as a 2D array
const rows = sheet.getDataRange().getValues();
//Start processing each spreadsheet row
rows.forEach(function(row, index){
//check if this row is the headers, if yes, skip it
if (index === 0) return;
//check if a document has already been generated by looking at 'Document Link', if yes, skip it
if (row[10]) return;
//Using the row data in a template literal, make a copy of the template document in the destinationFolder
const copy = googleDocTemplate.makeCopy("My Checklist - " + row[2] + " - " + row[4] + " - " + row[5], destinationFolder)
//Copy, then open it using the DocumentApp
const doc = DocumentApp.openById(copy.getId())
//Get contents for editing
const body = doc.getBody();
//replace token with values from spreadsheet row
body.replaceText('{{Employee ID}}', row[1]);
body.replaceText('{{Name}}', row[2]);
body.replaceText('{{Level}}', row[3]);
body.replaceText('{{Department}}', row[4]);
body.replaceText('{{Business Unit}}', row[5]);
body.replaceText('{{Location}}', row[6]);
//We make our changes permanent by saving and closing the document
doc.saveAndClose();
//Store the url of our new document in a variable
const url = doc.getUrl();
//Write that value back to the 'Document Link' column in the spreadsheet.
sheet.getRange(index + 1, 11).setValue(url)
})}
I believe your goal is as follows.
You want to share the copied file of const copy = googleDocTemplate.makeCopy("My Checklist - " + row[2] + " - " + row[4] + " - " + row[5], destinationFolder) with other users.
the same people (similar to how we do it manually) means share with the same people.
In this case, how about the following modification?
From:
//Start processing each spreadsheet row
rows.forEach(function(row, index){
//check if this row is the headers, if yes, skip it
if (index === 0) return;
//check if a document has already been generated by looking at 'Document Link', if yes, skip it
if (row[10]) return;
//Using the row data in a template literal, make a copy of the template document in the destinationFolder
const copy = googleDocTemplate.makeCopy("My Checklist - " + row[2] + " - " + row[4] + " - " + row[5], destinationFolder)
//Copy, then open it using the DocumentApp
To:
const emailAddresses = googleDocTemplate.getEditors().map(e => e.getEmail()); // Added
//Start processing each spreadsheet row
rows.forEach(function(row, index){
//check if this row is the headers, if yes, skip it
if (index === 0) return;
//check if a document has already been generated by looking at 'Document Link', if yes, skip it
if (row[10]) return;
//Using the row data in a template literal, make a copy of the template document in the destinationFolder
const copy = googleDocTemplate.makeCopy("My Checklist - " + row[2] + " - " + row[4] + " - " + row[5], destinationFolder)
copy.addEditors(emailAddresses); // Added
//Copy, then open it using the DocumentApp
From your question, I couldn't understand whether you wanted to share the file as the editor. So if you wanted to share it with the viewer, please modify it as follows.
From
copy.addEditors(emailAddresses);
To
copy.addViewers(emailAddresses);
References:
addEditors(emailAddresses)
addViewers(emailAddresses)
Related
Pretty new to using google scripts, so forgive me if I'm doing something silly.
I am trying to write a program to take a large spreadsheet of data and fill an invoice template I have created. I have successfully done this in the past, but now when I tried to apply the app to a new spreadsheet of data, it is no longer functioning properly.
The problem is because the sheet is so large, the scripts times out, then when I go to restart the script, it starts again all the way at the beginning instead of starting where it left off. Originally the function was set to write the URL of the new invoice in the last column of spreadsheet, and the function would skip any rows with entries in that column. This was not happening, so to simplify I added another row that once the invoice is created, the word "DONE" is entered, and then I tried to set it up to skip any row with "DONE" in that column, this still is not working.
Any ideas on how I can get this to work?
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs')
menu.addToUi();
}
function createNewGoogleDocs() {
//This value should be the id of your document template that we created in the last step
const googleDocTemplate = DriveApp.getFileById('1yXfcXTESCHqKsfMcgkhYJ9MdVwYoLYPhH1MRv4RyPk0');
//This value should be the id of the folder where you want your completed documents stored
const destinationFolder = DriveApp.getFolderById('1TXEumNJXfgFzPtKLBOAKJBXG-yNnjseQ')
//Here we store the sheet as a variable
const sheet = SpreadsheetApp
.getActiveSpreadsheet()
.getSheetByName('Export Worksheet')
//Now we get all of the values as a 2D array
const rows = sheet.getDataRange().getValues();
//Start processing each spreadsheet row
rows.forEach(function(row, index){
//Here we check if this row is the headers, if so we skip it
if (index === 0) return;
//Here we check if a document has already been generated by looking at 'Document Link', if so we skip it
if (row[31]=== 'DONE') return;
//Using the row data in a template literal, we make a copy of our template document in our destinationFolder
const copy = googleDocTemplate.makeCopy(`${row[4]}, ${row[0]} Invoice` , destinationFolder)
//Once we have the copy, we then open it using the DocumentApp
const doc = DocumentApp.openById(copy.getId())
//All of the content lives in the body, so we get that for editing
const body = doc.getBody();
//In this line we do some friendly date formatting, that may or may not work for you locale
const friendlyDate = new Date(row[3]).toLocaleDateString();
//In these lines, we replace our replacement tokens with values from our spreadsheet row
body.replaceText('{{Full Address}}', row[4]);
body.replaceText('{{unit}}', row[5]);
body.replaceText('{{Total}}', row[15]);
body.replaceText('{{Account Num}}', row[2]);
body.replaceText('{{Owner 1}}', row[6]);
body.replaceText('{{Owner 2}}', row[7]);
body.replaceText('{{CO Name}}', row[17]);
body.replaceText('{{St Address}}', row[20]);
body.replaceText('{{Address 1}}', row[18]);
body.replaceText('{{City}}', row[21]);
body.replaceText('{{State}}', row[22]);
body.replaceText('{{CO Zip}}', row[23]);
body.replaceText('{{invoice #}}', row[0]);
//We make our changes permanent by saving and closing the document
doc.saveAndClose();
//Store the url of our new document in a variable
const url = doc.getUrl();
//Write that value back to the 'Document Link' column in the spreadsheet.
sheet.getRange(index + 1, 30).setValue(url)
//Write that value back to the 'Document Link' column in the spreadsheet.
sheet.getRange(index + 1, 31).setValue("DONE")
})
}
Try this:
You were checking for DONE in the wrong location
function createNewGoogleDocs() {
const googleDocTemplate = DriveApp.getFileById('1yXfc...');
const destinationFolder = DriveApp.getFolderById('1TXE...');
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Export Worksheet');
const vs = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
vs.forEach((r, i) => {
if (r[30] != 'DONE') {
const copy = googleDocTemplate.makeCopy(`${r[4]}, ${r[0]} Invoice`, destinationFolder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
body.replaceText('{{Full Address}}', r[4]);
body.replaceText('{{unit}}', r[5]);
body.replaceText('{{Total}}', r[15]);
body.replaceText('{{Account Num}}', r[2]);
body.replaceText('{{Owner 1}}', r[6]);
body.replaceText('{{Owner 2}}', r[7]);
body.replaceText('{{CO Name}}', r[17]);
body.replaceText('{{St Address}}', r[20]);
body.replaceText('{{Address 1}}', r[18]);
body.replaceText('{{City}}', r[21]);
body.replaceText('{{State}}', r[22]);
body.replaceText('{{CO Zip}}', r[23]);
body.replaceText('{{invoice #}}', r[0]);
doc.saveAndClose();
const url = doc.getUrl();
sh.getRange(i + 2, 30).setValue(url);
sh.getRange(i + 2, 31).setValue("DONE");
}
});
}
I have the below code to copy a template and share it with the same people who has access to the template.
However, I have two issues here:
I do not want them to receive notification each time a copy is made and shared with them
I would also like to share with another person to edit it. The email of the person who can edit is in the google sheet, in column A. This new person should not also receive notificiation.
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('Create My Checklist');
menu.addItem('New Checklist', 'createNewGoogleDocs')
menu.addToUi();
}
function createNewGoogleDocs() {
//This value should be the id of your document template that we created in the last step
const googleDocTemplate = DriveApp.getFileById('1qRQ07PDmz1il9IftM9GIJfY37vTusfQZhhNS1BRELJQ');
//This value should be the id of the folder where you want your completed documents stored
const destinationFolder = DriveApp.getFolderById('1JufckhwXlAXDAE3_-f60lHQQ-jqe9Mx1')
//Here we store the sheet as a variable
const sheet = SpreadsheetApp
.getActiveSpreadsheet()
.getSheetByName('Data')
//Now we get all of the values as a 2D array
const rows = sheet.getDataRange().getValues();
const emailAddresses = googleDocTemplate.getEditors().map(e => e.getEmail()); // Added
//Start processing each spreadsheet row
rows.forEach(function(row, index){
//Here we check if this row is the headers, if so we skip it
if (index === 0) return;
//Here we check if a document has already been generated by looking at 'Document Link', if so we skip it
if (row[10]) return;
//Using the row data in a template literal, we make a copy of our template document in our destinationFolder
const copy = googleDocTemplate.makeCopy("eResignation Checklist - " + row[2] + " - " + row[4] + " - " + row[5], destinationFolder)
copy.addEditors(emailAddresses);
//Once we have the copy, we then open it using the DocumentApp
const doc = DocumentApp.openById(copy.getId())
//All of the content lives in the body, so we get that for editing
const body = doc.getBody();
//In these lines, we replace our replacement tokens with values from our spreadsheet row
body.replaceText('{{Workday ID}}', row[1]);
body.replaceText('{{Full Name}}', row[2]);
body.replaceText('{{Management Level}}', row[3]);
body.replaceText('{{LoS}}', row[4]);
body.replaceText('{{Cost Centre}}', row[5]);
body.replaceText('{{Office Location}}', row[6]);
//We make our changes permanent by saving and closing the document
doc.saveAndClose();
//Store the url of our new document in a variable
const url = doc.getUrl();
//Write that value back to the 'Document Link' column in the spreadsheet.
sheet.getRange(index + 1, 11).setValue(url)
Drive.Permissions.insert({role: "writer", type: "user", value: row[0]}, copy.getId(), {sendNotificationEmails: false, supportsAllDrives: true});
})}
I believe your goal is as follows.
You want to share the copied file.
The email address for sharing can be retrieved from the column "A".
When the file is shared, you don't want to send a notification email.
In this case, how about the following modification?
Modified script:
In order to use the option of sendNotificationEmails, Drive API is used. So please enable Drive API at Advanced Google services.
From:
sheet.getRange(index + 1, 11).setValue(url)
})}
To:
sheet.getRange(index + 1, 11).setValue(url)
Drive.Permissions.insert({role: "writer", type: "user", value: row[0]}, copy.getId(), {sendNotificationEmails: false}); // Added
})}
Reference:
Permissions: insert
Edit:
From the following replying,
i am really unsure why it says file not found because the copied file is in the shared drive > folder ID: 1JufckhwXlAXDAE3_-f60lHQQ-jqe9Mx1
I noticed that you are using the shared Drive. And from the following replying,
I have edited the code again as per what I see in my script now. it seems to work partially - for the email in row [0] they are not getting any notification const emailAddresses = googleDocTemplate.getEditors().map(e => e.getEmail()); how to disable notification for this group?
I noticed that your script was changed from your initial script. In this case, please modify as follows.
From:
sheet.getRange(index + 1, 11).setValue(url)
})}
To:
sheet.getRange(index + 1, 11).setValue(url)
emailAddresses.concat(row[0]).forEach(v =>
Drive.Permissions.insert({role: "writer", type: "user", value: v}, copy.getId(), {sendNotificationEmails: false, supportsAllDrives: true})
);
})}
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.
Multiple versions of the spreadsheet with the subset of data exist on my drive and I have a way to get the most recent but I thought that if I could capture the ID when I created it I would not have to run that extra code. If I just create a spreadsheet the return is a 'spreadsheet' which I can .getActiveSheet().getRange(. . . ).setValues(. . .)
If I create and immediately getId then get use DriveApp.getFileById the return is a 'file'. It has a mime type of spreadsheet but cannot be used with .getActiveSheet() etc. Why? Is there a way to get the ID when creating a spreadsheet and still work with that new sheet?
function createSubset(fileName) {
//
// var mimeType = 'application/vnd.google-apps.spreadsheet';
baseSs = SpreadsheetApp.getActiveSpreadsheet(); // "StudentRosterForPractice"
baseSheet = baseSs.getSheetByName("Roster");
// there are formulas save below actual data but no in column B
var totDataRows = cntDataRows (baseSheet.getRange('B1:B').getValues()) - 1;
dataSubset = baseSheet.getRange(3, 2, totDataRows, 2).getValues();
// ---- this does NOT work ----
// tempSsId = SpreadsheetApp.create(fileName).getId();
// tempSs = DriveApp.getFileById(tempSsId); // returns file
// Logger.log("tempSs.getMimeType(): " + tempSs.getMimeType() );
//// tempSs.getMimeType(): application/vnd.google-apps.spreadsheet
// tempSs.getActiveSheet().getRange(1, 1, totDataRows, 2).setValues(dataSubset);
//// gets TypeError: tempSs.getActiveSheet is not a function
// return tempSsId;
// ---- this does work ----
tempSs = SpreadsheetApp.create(fileName); // return spreadhsheet
tempSs.getActiveSheet().getRange(1, 1, totDataRows, 2).setValues(dataSubset);
return null;
}
/**
* Number rows with actual data. There is a row of forumulas.
*/
function cntDataRows (colB) {
var count = 0 ;
colB.forEach(
function(item) {
if ( item != "" ) { // no formula in last name column
count = count + 1;
}
}
) ;
Logger.log("totDataRows: " + count);
return count
}
function testCreateSubset() {
var ssName = 'tempSs'; //subset name
tempSsId = createSubset(ssName);
}
Thank you Marios.
My subset spreadsheet only has one sheet so that was not a problem. I am surprised that I can getActiveSheet on the subset even though the script is bound to the base spreadsheet.
There is still a problem however. When I create my subset, return the ID and create the PDF I get these messages
totDataRows: 41
Nov 8, 2020, 8:54:51 AM Info spreadsheet ID: 1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM
Nov 8, 2020, 8:54:51 AM Info In makeBlobPdf spreadsheet ID: 1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM
Nov 8, 2020, 8:54:51 AM Info URL to be fetched: https://docs.google.com/spreadsheets/d/1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM/export?exportFormat=pdf&format=pdf&size=A4&portrait=false&fitw=true&sheetnames=false&printtitle=false&printnotes=false&pagenumbers=false&pagenum=CENTER&gridlines=false&fzr=true&top_margin=0.15&left_margin=0.15&right_margin=0.15&bottom_margin=0.15
but the PDF is empty. If I open the subset and get the ID, hardcode it in the script and create the PDF the messages are:
In makeBlobPdf spreadsheet ID: 1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM
Nov 8, 2020, 8:57:28 AM Info URL to be fetched: https://docs.google.com/spreadsheets/d/1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM/export?exportFormat=pdf&format=pdf&size=A4&portrait=false&fitw=true&sheetnames=false&printtitle=false&printnotes=false&pagenumbers=false&pagenum=CENTER&gridlines=false&fzr=true&top_margin=0.15&left_margin=0.15&right_margin=0.15&bottom_margin=0.15
The ID received in the make PDF code is the same and the URL generated is the same but the PDF where the ID was hardcoded has data and the PDF where it was passed from the create subset is empty. The create PDF code is unchanged.
Works:
var ssName = 'tempSs'; //subset name
// var spreadsheetId = createSubset(ssName);
// Logger.log("spreadsheet ID: " + spreadsheetId );
var spreadsheetId = "1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM";
var blobPdf = makeBlobPdf(spreadsheetId, ssName);
Returns empty PDF:
var ssName = 'tempSs'; //subset name
var spreadsheetId = createSubset(ssName);
Logger.log("spreadsheet ID: " + spreadsheetId );
// var spreadsheetId = "1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM";
var blobPdf = makeBlobPdf(spreadsheetId, ssName);
Corrected createSubset
function createSubset(fileName) {;
baseSs = SpreadsheetApp.getActiveSpreadsheet(); // "StudentRosterForPractice"
baseSheet = baseSs.getSheetByName("Sheet1");
// there are formulas saved below actual data but no in column B
var totDataRows = cntDataRows (baseSheet.getRange('B1:B').getValues()) - 1;
dataSubset = baseSheet.getRange(3, 2, totDataRows, 2).getValues();
tempSsId = SpreadsheetApp.create(fileName).getId();
tempSs = SpreadsheetApp.openById(tempSsId);
tempSs.getActiveSheet().getRange(1, 1, totDataRows, 2).setValues(dataSubset);
return tempSsId;
}
Code that calls createSubset and makeBlobPdf
function createAndMailPdf () {
var ssName = 'tempSs'; //subset name
var spreadsheetId = createSubset(ssName);
Logger.log("spreadsheet ID: " + spreadsheetId );
// var spreadsheetId = "1t16QOEqT2OP8vVmdTueT2hqQjTGdlMQLAc7XnJNWScM";
var blobPdf = makeBlobPdf(spreadsheetId, ssName);
if (MailApp.getRemainingDailyQuota() > 0) {
var emailAddress = "blah blah # blah";
var subject = "With useless PDF file";
var htmlBody = HtmlService.createHtmlOutputFromFile('letterBody').getContent();
var templ = HtmlService.createTemplateFromFile('letterBody');
var message = templ.evaluate().getContent();
MailApp.sendEmail(
{ to: emailAddress,
subject: subject,
htmlBody: message,
attachments: blobPdf}
);
} else {
Logger.log("Mail quota exceeded but the PDF has been saved");
}
}
The makeBlobPdf code:
function makeBlobPdf (spreadsheetId, ssName) {
Logger.log("In makeBlobPdf spreadsheet ID: " + spreadsheetId );
var marginStringValue = '0.15';
var margin = '_margin=' + marginStringValue;
var margins = '&top' + margin + '&left' + margin
+ '&right' + margin + '&bottom' + margin;
var url = 'https://docs.google.com/spreadsheets/d/'
+ spreadsheetId + '/export?'
+ 'exportFormat=pdf&format=pdf'
+ '&size=A4'
+ '&portrait=false'
+ '&fitw=true' // Fit to width
+ '&sheetnames=false'
+ '&printtitle=false'
+ '&printnotes=false'
+ '&pagenumbers=false'
+ '&pagenum=CENTER'
+ '&gridlines=false'
+ '&fzr=true' // Repeat frozen rows
+ margins;
var token = ScriptApp.getOAuthToken();
Logger.log("URL to be fetched: " + url );
var result = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + token
}
});
var attachName = ssName + '.pdf';
var pdfBlob = result.getBlob().setName(attachName);
return pdfBlob;
};
Solution:
The issue in your code is that there is no method getActiveSheet()
for a file. It makes sense since a file can be anything in your
google drive. It wouldn't make sense to have a spreadsheet method like getActiveSheet() being applied to an image or a folder for example. As a result tempSs.getActiveSheet() is invalid since tempSs is a type of file in the deleted part of your code.
Here is the proper way to do that.
If you want to create the file and get its ID directly. And then you can use openById to get the spreadsheet.
function myFunction() {
var fileName = 'testname';
var file = Drive.Files.insert({title: fileName, mimeType: MimeType.GOOGLE_SHEETS}); // file
var newFile = SpreadsheetApp.openById(file.id); // Spreadsheet file
newFile.getActiveSheet().getRange(1, 1, totDataRows, 2).setValues(dataSubset);
}
But keep in mind newFile.getActiveSheet() gives the first sheet in your file. You might want to use newFile.getSheetByName('Sheet1') instead.
To use this code please enable Drive API.
From the question
Can you create spreadsheet in gas & get ID & getActive?
Yes. Bear in mind that running getActiveSheet() for the created spreadsheet will return the firts sheet.
Regarding DriveApp.getFileById(id) it returns a File object not a Spreadsheet object.
The Class File hasn't a getActiveSheet() method. If you wan to use this method one opction is to use SpreadsheetApp.openById(id) or SpreadsheetApp.open(file)
Resources
https://developers.google.com/apps-script/reference/drive/drive-app
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
The main problem you are encountering is that your sheet is being created but is taking some time to set the values in the function createSubset(). Therefore, while the creation of the Spreadsheet is complete, the values of the sheet have not been set yet which is done in the background.
This makes that createSubset() returns the Spreadsheet ID to the other functions and the PDF file is immediately created (but the cell values have not been set yet). This leads to your current behaviour where you are sending an empty Spreadsheets PDF because the cell values had not been set yet.
To force the cells to update with the right values and avoid sending the PDF before the Spreadsheet is all ready you can use the function flush which applies all the pending changes to the Spreadsheet. Use it as follows:
function createSubset(fileName) {;
baseSs = SpreadsheetApp.getActiveSpreadsheet(); // "StudentRosterForPractice"
baseSheet = baseSs.getSheetByName("Sheet1");
// there are formulas saved below actual data but no in column B
var totDataRows = cntDataRows (baseSheet.getRange('B1:B').getValues()) - 1;
dataSubset = baseSheet.getRange(3, 2, totDataRows, 2).getValues();
tempSsId = SpreadsheetApp.create(fileName).getId();
tempSs = SpreadsheetApp.openById(tempSsId);
tempSs.getActiveSheet().getRange(1, 1, totDataRows, 2).setValues(dataSubset);
// FORCE ALL UPDATES IN THE SPREADSHEET BEFORE RETURNING THE SPREADHSEET ID
SpreadsheetApp.flush();
return tempSsId;
}
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.