Google apps script to email google spreadsheet excel version [duplicate] - google-apps-script

This question already has answers here:
Google app scripts: email a spreadsheet as excel
(2 answers)
Closed 2 years ago.
I would like to write an apps script to email an excel version of my Google Spreadsheet. I know I can save the spreadsheet as an Excel file. I am not sure if I can use the script to email the excel version out as an attachment. How can this be done?

After an answer on another recent post (Thomas van Latum), I tried the suggested doc api and get an interesting result... here is the test code I used and that is working nicely except the file is in xlsx format, not in xls but this is not necessarily an issue these days :
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
function test(){
var id = 'spreadsheet_ID'
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/spreadsheets/Export?key='+id+'&exportFormat=xls',
googleOAuth_('docs',url)).getBlob()
DocsList.createFile(doc).rename('newfile.xls')
}
note : if you don't rename it, its default name is Export.xlsx , it might be more usefull to get its ID to use it later...
so the last line could be like this instead :
var xlsfileID = DocsList.createFile(doc).getId()
EDIT : to trigger the authorization process, try a small function like this, run it from the script editor
function autorise(){
// function to call to authorize googleOauth
var id=SpreadsheetApp.getActiveSpreadsheet().getId();
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/documents/Export?exportFormat=html&format=html&id='+id,
googleOAuth_('docs',url)).getContentText();
}

As I spent about four hours of playing Rumpelstiltskin because none of the typically very old code snippets for the old Spreadsheet versions and the old OAUTH you can find when googling "google docs script send excel attachment" or similar (i.e. you want to take an existing Spreadsheet, convert it to Excel format and send it as email attachment) actually worked, I finally found the solution.
To create the actual attachment content, neither the supposed res.getContent() nor res.getBlob() nor res.getBytes alone worked. These hints are misleading!
The only thing that works for me is response.getBlob().getContent()!
Whole code :
function sendCurrentDocAsEmail() {
var driveService = getDriveService();
var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getName();
var email = "recipient#demo.com"
var subject = "Here be Subjects";
var body = "Don't even think about learning how to code. It's wasted time.";
var file = Drive.Files.get(ssID );
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
});
var attachments = [{
fileName:sheetName+".xlsx",
content: response.getBlob().getBytes(), // this single line has cost me hours!
mimeType:"application//xls",
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
}];
MailApp.sendEmail(email,subject ,body, {attachments:attachments});
}
Where getDriveService() is a function from Google's "OAuth2 for Apps Script" readme on https://github.com/googlesamples/apps-script-oauth2

The latest working version is below. Based on this example, i.e. similar as in previous answer but uses Google Service Account which does not require a human going by link to receive a token. You have to install Oath library from Google, the instructions are pretty clear.
var PRIVATE_KEY = 'xxx'
var CLIENT_EMAIL = 'xxx';
var USER_EMAIL=Session.getActiveUser().getEmail()
function getOathService() {
return OAuth2.createService('GoogleDrive:' + USER_EMAIL)
// Set the endpoint URL.
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the private key and issuer.
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject(USER_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/drive');
}
function sendEmail() {
var oathService = getOathService();
var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var file = Drive.Files.get(ssID );
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var file = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + oathService.getAccessToken()
}
});
var attachments = [{
fileName:'xxx.xls',//TODO DATE
content: file.getBlob().getBytes(),
mimeType:"application//xls",
headers: {
Authorization: 'Bearer ' + oathService.getAccessToken()
}
}];
MailApp.sendEmail('email#domain.com', 'xxx', 'Hi,\n\nPlease see the last data in attachment',{attachments:attachments});
}

The one that worked for me:
var AUTH_TOKEN = "Enter your OAuth_Token";
ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var url = "http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="+ ssID + "&exportFormat=xls";
//Add &gid=x at the end of above url if you only want a particular sheet
var auth = "AuthSub token=\"" + AUTH_TOKEN + "\"";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: auth}});
var attachments = [{fileName:"<Filename>.xls", content: res.getContent(),mimeType:"application/vnd.ms-excel"}];
MailApp.sendEmail("<recipient's email id>", "<email subject>", "<email body>", {attachments: attachments});
Get OAuth documentation & you token from here https://developers.google.com/accounts/docs/OAuth2

Use the following snippet of code after modifying it to suit your needs
var file = DocsList.getFileById(FILE_ID);
var attachment = file.getAs('application/vnd.ms-excel');
MailApp.sendEmail("abcd#example.com", "Subject", " Body" , {"fileName": "Your_file_name" , "mimeType" : "application/vnd.ms-excel" , "content":attachment.getBytes() } );
Note that this code is not tested, so please feel free to fix an error or two that might pop up

Related

Why is this Web App showing an error "Script function not found: events"?

The user clicks on a button, which has main() assigned to it.
It runs with no errors, but the protections are not removed or applied as it seems to get stuck because of the error mentioned in the question.
Here are the functions borrowed and adapted from Stackoverflow:
// This is the main function. Please set this function to the run button on Spreadsheet.
function main() {
//DriveApp.getFiles(); // This is a dummy method for detecting a scope by the script editor.
const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const sheetName = activeSheet.getName();
if (sheetName === 'Itens_para_Costurar') {
var token = ScriptApp.getOAuthToken()
var url = ScriptApp.getService().getUrl();
var options = {
'method': 'post',
'headers': { 'Authorization': 'Bearer ' + token },
muteHttpExceptions: true
};
UrlFetchApp.fetch(url + "&key=removeprotectListaCortadas", options); // Remove protected range
listaPecasCortadas();//Atualiza a lista de peças cortadas
SpreadsheetApp.flush(); // This is required to be here.
UrlFetchApp.fetch(url + "&key=addprotectListaCortadas", options); // Add protected range
}
}
function doGet(e) {
if (e.parameter.key == "removeprotectListaCortadas") {
var listaPecasCortadasSht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Itens_para_Costurar'); // Please set here.
// Remove protected range.
var protections = listaPecasCortadasSht.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
console.log('Protection Name: ' + protections[i].getDescription())
protections[i].remove();
}
} else {
// Add protected range.
var ownersEmail = Session.getActiveUser().getEmail();
var protection = listaPecasCortadasSht.getRange('A8:J100').protect();
var editors = protection.getEditors();
for (var i = 0; i < editors.length; i++) {
var email = editors[i].getEmail();
if (email != ownersEmail) protection.removeEditor(email);
}
}
return ContentService.createTextOutput("ok");
}
I would appreciate any light towards the solution.
Modification points:
If you were using my answer, when I posted it, ScriptApp.getService().getUrl() returned the endpoint of the developer mode. But, in the current stage, it doesn't return the endpoint of the developer mode. And, in this case, the invalid endpoint is returned. Ref
In your request body, 'method': 'post', is used. But, in your script, doGet is used. I thought that this might be the reason for your current issue.
In your script, var url = ScriptApp.getService().getUrl(); is used. But, using url, you are using UrlFetchApp.fetch(url + "&key=addprotectListaCortadas", options);. In this case, the query parameter is not correct.
When these points are reflected in your script, how about the following modification?
From:
var token = ScriptApp.getOAuthToken()
var url = ScriptApp.getService().getUrl();
var options = {
'method': 'post',
'headers': { 'Authorization': 'Bearer ' + token },
muteHttpExceptions: true
};
UrlFetchApp.fetch(url + "&key=removeprotectListaCortadas", options); // Remove protected range
To:
var url = "###"; // Please manually set your Web Apps URL here.
var options = { muteHttpExceptions: true };
UrlFetchApp.fetch(url + "?key=removeprotectListaCortadas", options);
If your Web Apps is deployed as Execute the app as: Me and Who has access to the app: Anyone, when the Web Apps URL is used, the access token is not required to be used. If you want to use the developer mode, please use the access token.
Note:
This answer is for your current issue. So, I cannot check your all script. Please be careful about this.
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".

Is it possible to access a deployed Protected Google Web App via URL without logging in from browser each time?

I've deployed a protected web app, and I'd like to trigger it without logging in each time:
I'd like to access the web app URL without logging in:
Based on this document, it's not possible without logging in from browser:
https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script/blob/master/README.md
If the script of Web Apps uses some scopes, client users have to
authorize the scopes by own browser.
I'm assuming scopes means the web app is protected.
I've tried this: https://github.com/gsuitedevs/apps-script-oauth2/blob/master/samples/GoogleServiceAccount.gs but it asks for "request access"
If I click on request access, then it shows me this:
At this point, I'm thinking it's not possible to setup a service account with scope to trigger a protected deployed web app without authenticating through a browser each time. Can anyone confirm this?
My assumption is that the web app scope is https://www.googleapis.com/auth/drive since it has access to all drive's files.
Update: (What I tried but didn't work)
I matched the scope from the script:
To the service account:
The blurred area above is the client id i got from:
I've generated the access token using this script:
function accessTokens(){
var private_key = "-----BEGIN PRIVATE KEY-----*****\n-----END PRIVATE KEY-----\n"; // private_key of JSON file retrieved by creating Service Account
var client_email = "****#****.iam.gserviceaccount.com"; // client_email of JSON file retrieved by creating Service Account
var scopes = ["https://www.googleapis.com/auth/documents","https://www.googleapis.com/auth/forms","https://www.googleapis.com/auth/script.external_request","https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"]; // Scopes
var url = "https://www.googleapis.com/oauth2/v3/token";
var header = {
alg: "RS256",
typ: "JWT",
};
var now = Math.floor(Date.now() / 1000);
var claim = {
iss: client_email,
scope: scopes.join(" "),
aud: url,
exp: (now + 3600).toString(),
iat: now.toString(),
};
var signature = Utilities.base64Encode(JSON.stringify(header)) + "." + Utilities.base64Encode(JSON.stringify(claim));
var jwt = signature + "." + Utilities.base64Encode(Utilities.computeRsaSha256Signature(signature, private_key));
var params = {
method: "post",
payload: {
assertion: jwt,
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
},
};
var res = UrlFetchApp.fetch(url, params).getContentText();
Logger.log(res);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
sheet.getRange(1, 3).setValue(JSON.parse(res)['access_token']);
}
And still has the same error, it asks for request access.
After a couple days into this, I've figured it out (with help of course).
Get the scope from your deployed web app script: File > Project Properties > Scopes
Add the scope along with https://www.googleapis.com/auth/drive in page Manage API client access https://admin.google.com/AdminHome?chromeless=1#OGX:ManageOauthClients (use comma delimited to add multiple scopes: http...,http..., etc.)
For the client name, get the client id from the service account page in your admin console: https://console.developers.google.com
Deploy your script Publish > Deploy as Web App
After generating access token(instruction below), append the access token with your deployed web app url &access_token=YOURTOKENHERE
Use this script with a google sheet, it will generate the access_token in cell A1 of Sheet1 (Replace the 4 variables with the info relevant to you):
function accessTokens(){
var private_key = "-----BEGIN PRIVATE KEY-----n-----END PRIVATE KEY-----\n"; // private_key of JSON file retrieved by creating Service Account
var client_email = "*****#****.iam.gserviceaccount.com"; // client_email of JSON file retrieved by creating Service Account
var scopes = ["https://www.googleapis.com/auth/documents","https://www.googleapis.com/auth/forms","https://www.googleapis.com/auth/script.external_request","https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/drive"]; // Scopes
var impersonate_email = "" //impersonate email
var url = "https://www.googleapis.com/oauth2/v4/token";
var header = {
alg: "RS256",
typ: "JWT",
};
var now = Math.floor(Date.now() / 1000);
var claim = {
iss: client_email,
sub: impersonate_email,
scope: scopes.join(" "),
aud: url,
exp: (now + 3600).toString(),
iat: now.toString(),
};
var signature = Utilities.base64Encode(JSON.stringify(header)) + "." + Utilities.base64Encode(JSON.stringify(claim));
var jwt = signature + "." + Utilities.base64Encode(Utilities.computeRsaSha256Signature(signature, private_key));
var params = {
method: "post",
payload: {
assertion: jwt,
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
},
};
var res = UrlFetchApp.fetch(url, params).getContentText();
Logger.log(res);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
sheet.getRange(1, 1).setValue(JSON.parse(res)['access_token']);
}

Export (or print) with a google script new version of google spreadsheets to pdf file, using pdf options

I'm trying to make a google script for exporting (or printing) a new version of google spreadsheet (or sheet) to pdf, with page parameters (portrait/landscape, ...)
I've researched about this and found a possible solution here.
There are several similar solutions like this, but only work with old version of google spreadsheet.
Please, consider this code:
function exportAsPDF() {
//This code runs from a NEW version of spreadsheet
var oauthConfig = UrlFetchApp.addOAuthService("google");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=https://spreadsheets.google.com/feeds/");
oauthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oauthConfig.setConsumerKey("anonymous"); oauthConfig.setConsumerSecret("anonymous");
var requestData = { "method": "GET", "oAuthServiceName": "google","oAuthUseToken": "always" };
var ssID1="0AhKhywpH-YlQdDhXZFNCRFROZ3NqWkhBWHhYTVhtQnc"; //ID of an Old version of spreadsheet
var ssID2="10xZX9Yz95AUAPu92BkBTtO0fhVk9dz5LxUmJQsJ7yPM"; //ID of a NEW version of spreadsheet
var ss1 = SpreadsheetApp.openById(ssID1); //Old version ss object
var ss2 = SpreadsheetApp.openById(ssID2); //New version ss object
var sID1=ss1.getActiveSheet().getSheetId().toString(); // old version sheet id
var sID2=ss2.getActiveSheet().getSheetId().toString(); // new version sheet id
//For Old version, this runs ok.
var url1 = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="+ssID1+"&gid="+sID1+"&portrait=true"+"&exportFormat=pdf";
var result1 = UrlFetchApp.fetch(url1 , requestData);
var contents1=result1.getBlob();
var pdfFile1=DriveApp.createFile(contents1).setName("FILE1.pdf");
//////////////////////////////////////////////
var url2 = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="+ssID2+"&gid="+sID2+"&portrait=true"+"&exportFormat=pdf";
var result2 = UrlFetchApp.fetch(url2 , requestData);
var contents2=result2.getBlob();
var pdfFile2=DriveApp.createFile(contents2).setName("FILE2.pdf");
}
It works right and generates the file “FILE1.pdf”, that can be opened correctly. But for the new version of spreadsheet, it results in error 302 (truncated server response) at “var result2 = UrlFetchApp.fetch(url2 , requestData);”. Well, it’s ok because the url format for the new version doesn’t include the “key” argument. A correct url for new versions must be like "https://docs.google.com/spreadsheets/d/"+ssID2+"/export?gid="+sID2+"&portrait=true&format=pdf"
Using this for url2 (var url2 = "https://docs.google.com/spreadsheets/d/"+ssID2+"/export?gid="+sID2+"&portrait=true&format=pdf") it fails again with error “Authorization can’t be performed for service: google”.
Well, this error could be due to an incorrect scope for the RequestTokenUrl. I’ve found the alternative scope https://docs.google.com/feeds and set it: oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=https://docs.google.com/feed/");
After the code runs again, a new error happens at the line with UrlFetchApp.fetch(url2 , requestData);: “Error OAuth” … I don’t know how to continue … I’ve tested hundreds of variations without good results.
Any ideas? is correct the scope docs.google.com/feeds for new version of spreadsheets? is correct the oauthConfig?
Thanks in advance.
Here is my spreadsheet-to-pdf script. It works with the new Google Spreadsheet API.
// Convert spreadsheet to PDF file.
function spreadsheetToPDF(id,index,url,name)
{
SpreadsheetApp.flush();
//define usefull vars
var oauthConfig = UrlFetchApp.addOAuthService("google");
var scope = "https://docs.google.com/feeds/";
//make OAuth connection
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oauthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
//get request
var request = {
"method": "GET",
"oAuthServiceName": "google",
"oAuthUseToken": "always",
"muteHttpExceptions": true
};
//define the params URL to fetch
var params = '?gid='+index+'&fitw=true&exportFormat=pdf&format=pdf&size=A4&portrait=true&sheetnames=false&printtitle=false&gridlines=false';
//fetching file url
var blob = UrlFetchApp.fetch("https://docs.google.com/a/"+url+"/spreadsheets/d/"+id+"/export"+params, request);
blob = blob.getBlob().setName(name);
//return file
return blob;
}
I've had to use the "muteHttpExceptions" parameter to know exactly the new URL. With this parameter, I downloaded my file with the HTML extension to get a "Moved permanently" page with my final url ("https://docs.google.com/a/"+url+"/spreadsheets/d/"+id+"/export"+params").
And note that I am in an organization. So I've had to specify its domain name ("url" parameter, ie "mydomain.com").
(Copied from this answer.)
This function is an adaptation of a script provided by "ianshedd..." here.
It:
Generates PDFs of ALL sheets in a spreadsheet, and stores them in the same folder containing the spreadsheet. (It assumes there's just one folder doing that, although Drive does allow multiple containment.)
Names pdf files with Spreadsheet & Sheet names.
Uses the Drive service (DocsList is deprecated.)
Can use an optional Spreadsheet ID to operate on any sheet. By default, it expects to work on the "active spreadsheet" containing the script.
Needs only "normal" authorization to operate; no need to activate advanced services or fiddle with oAuthConfig.
With a bit of research and effort, you could hook up to an online PDF Merge API, to generate a single PDF file. Barring that, and until Google provides a way to export all sheets in one PDF, you're stuck with separate files.
Script:
/**
* Export one or all sheets in a spreadsheet as PDF files on user's Google Drive,
* in same folder that contained original spreadsheet.
*
* Adapted from https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579#c25
*
* #param {String} optSSId (optional) ID of spreadsheet to export.
* If not provided, script assumes it is
* sheet-bound and opens the active spreadsheet.
* #param {String} optSheetId (optional) ID of single sheet to export.
* If not provided, all sheets will export.
*/
function savePDFs( optSSId, optSheetId ) {
// If a sheet ID was provided, open that sheet, otherwise assume script is
// sheet-bound, and open the active spreadsheet.
var ss = (optSSId) ? SpreadsheetApp.openById(optSSId) : SpreadsheetApp.getActiveSpreadsheet();
// Get URL of spreadsheet, and remove the trailing 'edit'
var url = ss.getUrl().replace(/edit$/,'');
// Get folder containing spreadsheet, for later export
var parents = DriveApp.getFileById(ss.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Get array of all sheets in spreadsheet
var sheets = ss.getSheets();
// Loop through all sheets, generating PDF files.
for (var i=0; i<sheets.length; i++) {
var sheet = sheets[i];
// If provided a optSheetId, only save it.
if (optSheetId && optSheetId !== sheet.getSheetId()) continue;
//additional parameters for exporting the sheet as a pdf
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
+ '&gid=' + sheet.getSheetId() //the sheet's Id
// following parameters are optional...
+ '&size=letter' // paper size
+ '&portrait=true' // 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
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
}
}
var response = UrlFetchApp.fetch(url + url_ext, options);
var blob = response.getBlob().setName(ss.getName() + ' - ' + sheet.getName() + '.pdf');
//from here you should be able to use and manipulate the blob to send and email or create a file per usual.
//In this example, I save the pdf to drive
folder.createFile(blob);
}
}
Thank you!
Variant 2 works with me with options:
var requestData = {
"oAuthServiceName": "spreadsheets",
"oAuthUseToken": "always"
};
Then:
var ssID = ss.getId();
var sID = ss.getSheetByName(name).getSheetId();
//creating pdf
var pdf = UrlFetchApp.fetch("https://docs.google.com/spreadsheets/d/" + ssID + "/export?gid=" + sID + "&portrait=false&size=A4&format=pdf", requestData).getBlob();
//folder to created pdf in
var folder = DriveApp.getFolderById(id);
//creating pdf in this folder with given name
folder.createFile(pdf).setName(name);
I can change image size, orientation etc. with listed parameters perfectly.

Get Google Document as HTML

I had a wild idea that I could build a website blog for an unsophisticated user friend using Google Drive Documents to back it. I was able to create a contentService that compiles a list of documents. However, I can't see a way to convert the document to HTML. I know that Google can render documents in a web page, so I wondered if it was possible to get a rendered version for use in my content service.
Is this possible?
You can try this code :
function getGoogleDocumentAsHTML(){
var id = DocumentApp.getActiveDocument().getId() ;
var forDriveScope = DriveApp.getStorageUsed(); //needed to get Drive Scope requested
var url = "https://docs.google.com/feeds/download/documents/export/Export?id="+id+"&exportFormat=html";
var param = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions:true,
};
var html = UrlFetchApp.fetch(url,param).getContentText();
Logger.log(html);
}
Node.js Solution
Using the Google APIs Node.js Client
Here's how you can get a google doc as html using google drive's node.js client library.
// import googleapis npm package
var google = require('googleapis');
// variables
var fileId = '<google drive doc file id>',
accessToken = '<oauth access token>';
// oauth setup
var OAuth2 = google.auth.OAuth2,
OAuth2Client = new OAuth2();
// set oauth credentials
OAuth2Client.setCredentials({access_token: accessToken});
// google drive setup
var drive = google.drive({version: 'v3', auth: OAuth2Client});
// download file as text/html
var buffers = [];
drive.files.export(
{
fileId: fileId,
mimeType: 'text/html'
}
)
.on('error', function(err) {
// handle error
})
.on('data', function(data) {
buffers.push(data); // data is a buffer
})
.on('end', function() {
var buffer = Buffer.concat(buffers),
googleDocAsHtml = buffer.toString();
console.log(googleDocAsHtml);
});
Take a look at the Google Drive V3 download docs for more languages and options.
Google docs currently has a function to do this.
Just download to zip(.html) and you can have a zip archive with html & image (if inserted)
I know this is not solution based on code, but its working :)
There is no direct method in GAS to get an HTML version of a doc and this is quite an old enhancement request but the workaround described originally by Henrique Abreu works pretty well, I use it all the time...
The only annoying thing in the authorization process that needs to be called from the script editor which makes it uneasy to use in a shared application (with "script unable" users) but this only happens once ;).
There is also a Library created by Romain Vialard that makes things (a bit) easier... and adds a few other interesting functions.
Here is a little snipped for the new version of goole AOuth following the idea posted by Enrique:
function exportAsHTML(){
var forDriveScope = DriveApp.getStorageUsed(); //needed to get Drive Scope requested
var docID = DocumentApp.getActiveDocument().getId();
var url = "https://docs.google.com/feeds/download/documents/export/Export?id="+docID+"&exportFormat=html";
var param = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions:true,
};
var html = UrlFetchApp.fetch(url,param).getContentText();
return html;
}
and then use the usual mailApp:
function mailer(){
var docbody = exportAsHTML();
MailApp.sendEmail({
to: "email#mail.com",
subject: "document emailer",
htmlBody: docbody });
}
Hope the new workaround helps
JD
You may use the solution here
/**
* Converts a file to HTML. The Advanced Drive service must be enabled to use
* this function.
*/
function convertToHtml(fileId) {
var file = Drive.Files.get(fileId);
var htmlExportLink = file.exportLinks['text/html'];
if (!htmlExportLink) {
throw 'File cannot be converted to HTML.';
}
var oAuthToken = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(htmlExportLink, {
headers:{
'Authorization': 'Bearer ' + oAuthToken
},
muteHttpExceptions: true
});
if (!response.getResponseCode() == 200) {
throw 'Error converting to HTML: ' + response.getContentText();
}
return response.getContentText();
}
Pass as fileId, the id of the google doc and to enable advanced drive services follow the instructions here.
I've had this problem as well. The HTML that the Document HTML Export spits out is really ugly, so this was my solution:
/**
* Takes in a Google Doc ID, gets that doc in HTML format, cleans up the markup, and returns the resulting HTML string.
*
* #param {string} the id of the google doc
* #param {boolean} [useCaching] enable or disable caching. default true.
* #return {string} the doc's body in html format
*/
function getContent(id, useCaching) {
if (!id) {
throw "Please call this API with a valid Google Doc ID";
}
if (useCaching == null) {
useCaching = true;
}
if (typeof useCaching != "boolean") {
throw "If you're going to specify useCaching, it must be boolean.";
}
var cache = CacheService.getScriptCache();
var cached = cache.get(id); // see if we have a cached version of our parsed html
if (cached && useCaching) {
var html = cached;
Logger.log("Pulling doc html from cache...");
} else {
Logger.log("Grabbing and parsing fresh html from the doc...");
try {
var doc = DriveApp.getFileById(id);
} catch (err) {
throw "Please call this API with a valid Google Doc ID. " + err.message;
}
var docName = doc.getName();
var forDriveScope = DriveApp.getStorageUsed(); // needed to get Drive Scope requested in ScriptApp.getOAuthToken();
var url = "https://docs.google.com/feeds/download/documents/export/Export?id=" + id + "&exportFormat=html";
var param = {
method: "get",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions:true,
};
var html = UrlFetchApp.fetch(url, param).getContentText();
// nuke the whole head section, including the stylesheet and meta tag
html = html.replace(/<head>.*<\/head>/, '');
// remove almost all html attributes
html = html.replace(/ (id|class|style|start|colspan|rowspan)="[^"]*"/g, '');
// remove all of the spans, as well as the outer html and body
html = html.replace(/<(span|\/span|body|\/body|html|\/html)>/g, '');
// clearly the superior way of denoting line breaks
html = html.replace(/<br>/g, '<br />');
cache.put(id, html, 900) // cache doc contents for 15 minutes, in case we get a lot of requests
}
Logger.log(html);
return html;
}
https://gist.github.com/leoherzog/cc229d14a89e6327336177bb07ac2980
Perhaps this would work for you...
function doGet() {
var blob = DriveApp.getFileById('myFileId').getAsHTML();
return HtmlService.createHtmlOutput(blob);
}

exporting spreadsheet to pdf then saving the file in google drive

I'm having problems saving to google drive after printing a google spreadsheet to pdf.
If i just put the "printurl" string into a browser, it will automatically give me the file. But I want it to be saved to google drive automatically. I've tried this code by borrowing code from other posts that emails a spreadsheet as PDF. But this produces a pdf that is unable to be opened. What am I doing wrong?
function printpdf() {
var spreadsheet_id="0Aiy1DTQRndx6dDRidXoxNzlXZFhxd2FITTlBbnUybnc";
var settings = '&fitw=true&portrait=false&exportFormat=pdf&gid=0&gridlines=false';
var printurl = 'https://spreadsheets.google.com/feeds/download/spreadsheets/Export? key=' + spreadsheet_id + settings;
var result=UrlFetchApp.fetch(printurl);
var content=result.getContent();
var file=DocsList.createFile("temp",content,"application/pdf");
}
Here is an update to this question under the new oauth2 format.
Printing spreadsheet to PDF then saving file in Drive using OAuth2
You can do it in a much simpler fashion
function printpdf(){
var spreadsheet_id="0Aiy1DTQRndx6dDRidXoxNzlXZFhxd2FITTlBbnUybnc";
var spreadsheetFile = DocsList.getFileById(spreadsheet_id);
var blob = spreadsheetFile.getAs('application/pdf');
DocsList.createFile(blob);
}
Note that the DocsList.createFile(blob) works only with Google Apps accounts.
did you mean it like that?
var id = SpreadsheetApp.getActiveSpreadsheet().getId();
var sheetName = getConfig(SHEET_NAME_CELL);
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
if (!dataSheet) {
Browser.msgBox("Can't find sheet named:" + sheetName);
return;
}
var dataSheetIndex = dataSheet.getSheetId();
//this is three level authorization
var oauthConfig = UrlFetchApp.addOAuthService("google");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=https://spreadsheets.google.com/feeds/");
oauthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
//even better code
//oauthConfig.setConsumerKey(ScriptProperties.getProperty("consumerKey"));
//oauthConfig.setConsumerSecret(ScriptProperties.getProperty("consumerSecret"));
var requestData = {
"method": "GET",
"oAuthServiceName": "google",
"oAuthUseToken": "always"
};
var url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + id + "&gid=" + dataSheetIndex + "&fitw=true&size=A4&portrait=true&sheetnames=false&printtitle=false&exportFormat=pdf&format=pdf&gridlines=false";
//Save File to Google Drive
var seplogoBlob = UrlFetchApp.fetch(url, requestData).getBlob().setName("Filename.pdf");
DocsList.createFile(seplogoBlob);