Email Google spreadsheet failed authorization with Oauth 2.0 - google-apps-script

The below scrip to email a google spreadsheet as a pdf worked perfectly until last week. Now I get this message: "Authorization is required to perform that action". After reading up on it, I understand that Google no longer supports OAuth 1.0.
Can anyone guide me in the right direction towards updating the script for oAuth 2.0?
function EmailSpreadsheetAsPdf() {
// Google spreadsheet (key) + sheet (gid)
var key = "1XJDY-M2oSfIG6AQ3IYv4SwKn_QmPW2m24ZNB38o7vCw";
var gid = "1593627730";
// Email recipient
var emailTo = “testuser#gmail.com";
var subject = “This is the subject”;
var body = “This is the body“;
var filename = “Example" + ".pdf";
// Make OAuth Connection
var oauthConfig = UrlFetchApp.addOAuthService("google");
var scope = "https://docs.google.com/feeds"
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=" + scope);
oauthConfig.setAuthorizationUrl("https://accounts.google.com/OAuthAuthorizeToken");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
var request = {
"method": "GET",
"muteHttpExceptions": true,
"oAuthServiceName": "google",
"oAuthUseToken": "always",
};
// Create the PDF using this hack with special option variables in the URL
// As of 2/4/14 this seems to be the only way to export PDF with custom options (landscape, no gridlines, etc)
// exportFormat = pdf / csv / xls / xlsx
// gridlines = true / false
// printtitle = true (1) / false (0)
// size = legal / letter/ A4 (according to: http://goo.gl/nPrfdj, but doesn't seem to work?? letter only)
// fzr (repeat frozen rows) = true / false
// portrait = true (1) / false (0)
// fitw (fit to page width) = true (1) / false (0)
// add gid if to export a particular sheet - 0, 1, 2,..
//define the params URL to fetch
var params = "?gid=" + gid + "&fitw=true&exportFormat=pdf&format=pdf&size=A4&portrait=false&sheetnames=false&printtitle=false&gridlines=false";
//fetching file url
var blob = UrlFetchApp.fetch("https://docs.google.com/" + "spreadsheets/d/" + key + "/export" + params, request);
var pdf = blob.getBlob().setName(filename);
// Send the email with attachment
GmailApp.sendEmail(emailTo, subject, body, {attachments:[pdf]});
}
Google ressources: https://developers.google.com/identity/protocols/OAuth_ref

I finally solved my own question. I didn't even need to setup anything in the develop console, see my solution below. The fist time you run it, it will promt you to authorize access to your gmail. I hope it will be helpful to someone.
function EmailSpreadsheetAsPdf() {
// Google spreadsheet (key) + sheet (gid)
var key = "1XJDY-M2oSfIG6AQ3IYv4SwKn_QmPW2m24ZNB38o7vCw";
var gid = "1593627730";
// Email recipient
var emailTo = “testuser#gmail.com";
var subject = “This is the subject”;
var body = “This is the body“;
var filename = “Example" + ".pdf";
// Base URL
var url = "https://docs.google.com/spreadsheets/d/" + key + "/export?";
/* Specify PDF export parameters
From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
*/
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
// Convert worksheet to PDF
var pdf = UrlFetchApp.fetch(url + url_ext + gid, {
headers: {
'Authorization': 'Bearer ' + token
}
});
// Send email with PDF attachment
GmailApp.sendEmail(emailTo, subject, body, {
attachments:[pdf]
});
}

Related

Everything works but the subject line blank... why? What am I missing?

I think this must be very easy to see, but I cannot see it! This script sends out a sheet as a PDF. It works well, except that the subject line is blank. I've tried substituting an actual string for the variable in the class gmailApp at the bottom, but the subject line is still blank when the email arrives. Help! Thank you.
P.S.-- note that I took out the email address for privacy.
function emailPDF(email, subject, body, sheetName) {
var email = "...";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Client');
var cost = sheet.getSheetValues(1,1,1,1).toLocaleString();
var subject = ss.getName();
var body = "Hi Serenity, the material cost is $" + cost +" Thank you!";
// Base URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
/* Specify PDF export parameters
From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
*/
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers : {
'Authorization' : 'Bearer ' + token
}
}).getBlob().setName(ss.getName() + ".pdf");
// save PDF to drive
// var newFile = DriveApp.createFile(response).setName(sheet.getName() + ".pdf")
if (MailApp.getRemainingDailyQuota() > 0)
GmailApp.sendEmail(email, subject, body, {
htmlBody : body,
attachments : [response]
});
}
I've figured it out... this was just petty error. The spreadsheet has a lot of scripts. I picked up this script from a month ago, started from scratch and accidentally gave it the same function name. Both scripts were being called simultaneously, causing this result. Everything works now.
I appreciate your comment Lamblichus... I was so focused on the script being wrong that I didn't realize what was happening. Then I realized that it can't be wrong. You got me thinking the right way. Best.

MailApp.sendEmail emails the user who is not a recipient

(Note: I edited the title for clarity)
I've been trying to figure out what's causing this problem but to no avail.
I created a Google script that allows a Requester to submit a Google sheet for approval. Upon submission it emails the Requester that it has been submitted. It also emails the Approver with the option to APPROVE or DISAPPROVE. My problem: this same exact mail for Approver is also received by the Requester.
Here's a snapshot of the code for your review and kind support:
function emailSpreadsheetAsPDF(time, approverEmail, requesterEmail, financeEmail, approvalNeeded) {
var timestamp = time;
var approverEmail = approverEmail;
var requesterEmail = requesterEmail;
var financeEmail = financeEmail;
var approvalNeeded = approvalNeeded;
Logger.log("Approver: " + approverEmail);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form"); // Enter the name of the sheet here
//*******************************************
//* Enter the name of the TEMP folder ID here
//*******************************************
var TEMPfolderid = "xxxyyyzzz"; // TEMP folder to store the PDF export
var body4Requester = "\n\nAttached is a PDF copy of the Price Maintenance Form that you submitted. \n\nPlease note that you will be automatically notified via email when: \n(a) Your request is approved (or disapproved), and \n(b) When it is approved and already processed in SAP. \n\nAPAC Finance Team";
var body4Finance = "\n\nAttached is a PDF copy of the Price Maintenance Form submitted by " + requesterEmail + ". It does not require an approval as per current policy.\n\nKindly process.";
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
// Specify PDF export parameters
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=A4' // paper size legal / letter / A4
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers : {
'Authorization' : 'Bearer ' + token
},
muteHttpExceptions: true
});
var pdfFile = response.getBlob();
//Create the file...
var file = DriveApp.createFile(pdfFile).setName(timestamp + ".pdf");
//Store the file's ID for retrieval later during approval and post-SAP processing
var PDFfileID = file.getId();
//Save the file in TEMP folder
DriveApp.getFolderById(TEMPfolderid).addFile(file);
//Now, get the file and email as attachment
var blob = DriveApp.getFileById(file.getId());
// Email with Approve/Disapprove link
var url = ScriptApp.getService().getUrl();
var options = '?approval=%APPROVE%&timestamp=%TIMESTAMP%&reply=%EMAIL%&pdffileid=%FILEID%'
.replace("%TIMESTAMP%",encodeURIComponent(timestamp))
.replace("%EMAIL%",requesterEmail)
.replace("%FILEID%",PDFfileID);
var approve = url+options.replace("%APPROVE%","Approved");
var disapprove = url+options.replace("%APPROVE%","Disapproved");
var html = "<body>"+
"Attached is the Price Change request<br /><br />"+
"Approve<br />"+
"Disapprove<br />"+
"</body>";
if (approvalNeeded == "Yes") { //send email with Approve and Disapprove option to Approver
MailApp.sendEmail(approverEmail, "TEST ONLY: Price Maintenance Form for your approval", body4Requester, {
htmlBody: html,
attachments : [blob]
});
MailApp.sendEmail(requesterEmail, "TEST ONLY: Your Price Maintenance request has been submitted", body4Requester, {
attachments : [blob]
});
} else { //send straight to Finance
MailApp.sendEmail(financeEmail, "TEST ONLY: Price Maintenance Form for processing. No approval neeed", body4Finance, {
cc:requesterEmail,
attachments : [blob]
});
}}
Not sure if it's relevant -- I deployed this as a web app with the setting "Execute the app as User accessing the web app".
Many thanks in advance for any advice.
I tried replacing MailApp.sendEmail with GmailApp.sendEmail.
It worked.

print spreadsheet as pdf with no margins

I am trying to make things easier for my team so I want to have them print out a given area from my Google Sheets document into a pdf with predefined margins/printing settings. The printed area is not on the active spreadsheet.
I just moved over from Excel to Spreadsheets. I know some VBA but do not really know that much about GAS. I did quite a lot of research but all the samples I found are based on UiApp which not supported any longer.
I found some threads (where I think messages are missing?):
https://plus.google.com/u/1/115432608459317672860/posts/DQTbozyTsKk?cfem=1&pageId=none
Google Apps Script print sheet with margins
The last one I found was this one (PDF margins - Google Script) where I added the settings for margins (does it work that way though? I could not try it out yet because I do not know how to download the pdf. I tried do research but couldn't find anything..
var report = SpreadsheetApp.getActive();
var pdfName = "Angebot";
var sheetName = "Angebot";
var sourceSheet = report.getSheetByName(sheetName);
SpreadsheetApp.getActiveSpreadsheet().toast('Creating the PDF');
// export url
var url = 'https://docs.google.com/spreadsheets/d/'+report.getId()+'/export?exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=A4' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='+sourceSheet.getSheetId(); // the sheet's Id
+ '&top_margin=0'
+ '&left_margin=0'
+ '&right_margin=0'
+ '&bottom_margin=0'
var token = ScriptApp.getOAuthToken();
// request export url
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var theBlob = response.getBlob().setName(pdfName+'.pdf');
//var attach = {fileName:'Monthly Report.pdf',content:pdf, mimeType:'application/pdf'};
var name = report.getRange("H1:H1").getValues(); // Get Name
var emailTo = report.getRange("H2:H2").getValues(); // Get email
var period = report.getRange("H3:H3").getValues(); // Get Reporting Period
var subject = " - TEST Monthly Report - " + period; // Construct the Subject Line
var message = "Hi " + name + ", here is your latest report for " + period; // email body text
// download pdf
}

onFormSubmit trigger sending last row submission for simultaneous submission

Still very new to Google App script but managed to set up a form that populates a spreadsheet and then sends out an email with a pdf attachment with a report based on the users' data. My issue is that on simultaneous submission. The form attachment is emailed to the last respondent the number of times equal to the number of simultaneous submissions. Luckily based on the last respondent's information
I have tried SpreadsheetApp.flush() as well as LockService.getScriptLock() but did not get the problem resolved (possibly due to incorrect use). I include herewith the code I have.
Herewith the updated code
//automatically send email with cash flow attached when form is submitted
function onFormSubmit(e) {
// change onFormSubmit() to onFormSubmit(e) when based on event otherwise just onFormSubmit()
var sourceSpreadSheet = SpreadsheetApp.openById("spreadsheetID");
var responseSheet = ss.getSheetByName("Form responses 1") ;
var data = e.range.getValues();
//var replied = data.offset(0, e.range.getNumColumns(),0,0);
//Logger.log(data)
//change to data = e.range when based on event alternatively responseSheet.getRange("A5:AA26").getValues();
//Get a lock on this script, because we're about to modify a shared resource
var lock = LockService.getScriptLock()
//Wait 30 seconds for other processes to finish
lock.waitLock(30000);
//Logger.log(data[0][2]); //- email address
var email = data[0][2];
var name = data[0][1];
var subject = "DelQS - Estimated cash flow for your project";
//Generate attachment
hideSheetsforPrint();
//additional parameters for exporting the sheet as a pdf
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
// Print either the entire Spreadsheet or the specified sheet if optSheetId is provided
+ ('&id=' + sourceSpreadSheet())
// following parameters are optional...
+ '&size=A4' // paper size
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=false&printtitle=false&pagenumbers=false' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&printnotes=false' // remove notes
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
}
}
var response = UrlFetchApp.fetch("https://docs.google.com/spreadsheets/" + url_ext, options);
var blob = response.getBlob().setName('Estimated cash flow.pdf');
showAllSheets();
var projectName = data[0][3]; //project name (optional)
var htmlBody = HtmlService.createTemplateFromFile("notificationCashFlowEmail");
//set the values for placeholders in the html file
htmlBody.name = name;
//evaluate and get the html file
var emailHtml = htmlBody.evaluate().getContent();
GmailApp.sendEmail(email, subject, emailHtml, {htmlBody: emailHtml,attachments: [blob]});
var d = new Date();
//responseSheet.getRange(replied).setValue(d); //optional to below line
responseSheet.getRange(responseSheet.getLastRow(),responseSheet.getLastColumn()).setValue(d);
SpreadsheetApp.flush();
//Release the lock so that other processes can continue
lock.releaseLock();
}

Why do simultaneous Google web app users contaminate results

I wrote my first web app using Google App Maker, which edits a spreadsheet file to create a report customized for the user, and allows the user to download a copy of the report. The program is really simple, it just:
enters the user's user account name into a certain cell of the Sheets document,
flushes the spreadsheet (which dynamically updates it according to the user account name),
creates a PDF of the updated spreadsheet - which is a test score report,
and allows the user to download a pdf of their report.
It all works fine, except when several students run the web app simultaneously. If that happens, some of the students end up with another student's report.
My understanding was that changes made to a Google Sheets document by the server script in my web app would not be saved to the document unless I saved them intentionally. So my hope was that simultaneous users would not have any impact on each other.
I'm really stuck as to how to resolve this issue. Here's my code:
App startup script (runs as soon as the app is loaded):
downloadReport();
Client script
function showDownloadLink(downloadURL){
console.log(downloadURL);
var spinnerWidgets = app.pages.appPage.descendants.progressSpinner;
spinnerWidgets.visible = false;
var linkWidgets = app.pages.appPage.descendants.downloadPDFLink;
linkWidgets.href = downloadURL;
linkWidgets.visible = true;
}
function downloadReport() {
var status = app.pages.appPage.descendants.downloadStatus;
var user = app.user.username;
var downloadLink = google.script.run.withSuccessHandler(showDownloadLink)
.createDownload(user);
}
Server script:
// Spreadsheet file must have a tab called Report
function createDownload(user) {
///*
// SET SPREADSHEET FILE ID
var ssID = 'xxxxxxxxx_SHEETS_FILE_ID_GOES_HERE_xxxxxxxxxx';
var ss = SpreadsheetApp.openById(ssID);
SpreadsheetApp.setActiveSpreadsheet(ss);
var sheet = ss.getSheetByName("Report");
SpreadsheetApp.setActiveSheet(sheet);
// determines timezone
var timeZone = Session.getScriptTimeZone();
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
// gets the destination folder using spreadsheet location (folder)
var ssFolder = DriveApp.getFileById(ssID).getParents();
var folder = ssFolder.next();
var folderId = folder.getId();
var destination = DriveApp.getFolderById(folderId);
// Logger.log('destination name: ' + destination.getName());
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
var formattedDate = Utilities.formatDate(new Date(), timeZone , "yyyy-MM-dd' 'HH:mm:ss");
// gets the name of the original file and appends the ID and the timestamp stored in formattedDate
var PDFfileName = ss.getName()+formattedDate+ "-" + user;
// inputs ID into daughter spreadsheet reference cell to personalize report
ss.getSheetByName("Report").getRange(3, 1).setValue(user); // Puts ID into cell A3
SpreadsheetApp.flush(); // Updates the spreadsheet with changes and refreshes report
// Export URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ssID);
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&top_margin=0.30' // sets top margin of pdf file
+ '&bottom_margin=0.28' // sets bottom margin of pdf file
+ '&left_margin=0.43' // sets left margin of pdf file
+ '&right_margin=0.43' // sets right margin of pdf file
+ '&fitw=true' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
// Take snapshot and save to PDF
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
//convert the response to a blob and store in our array
var blob = response.getBlob().getAs('application/pdf').setName(PDFfileName + '.pdf');
var newPDFFile = folder.createFile(blob);
newPDFFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// Link URL
var downloadURL = "https://drive.google.com/uc?export=download&id="+newPDFFile.getId();
return downloadURL;
}