Google Appscript approvals for public document - google-apps-script

I have a google spreadsheet that is shared with all members on our domain. I'd like to have a script which will send the spreadsheet as an email attachment whenever one of the users runs the script.
Following this question as a guide, I arrived at the following code:
function sendEmail() {
var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getName();
//var email = Session.getUser().getEmail();
var email = "xxxxx#example.com";
var subject = "Order Form";
var body = "Please find the attached Order Form for drop-ship delivery.";
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 url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="
+ ssID + "&gid=0&portrait=true" +"&exportFormat=xls";
var result = UrlFetchApp.fetch(url , requestData);
var contents = result.getContent();
MailApp.sendEmail(email,subject ,body, {attachments:[{fileName:sheetName+".xls", content:contents, mimeType:"application//xls"}]});
}
This code works, but only if I run the code from the script editor first (which involves authorizing access to the google mail account), and then authorizing the script when using the script.
So, I passed along the document to the order department, and again, it only works when EACH USER authorizes the script from the script editor, and then authorizes the script when using it.
Is there a way to eliminate the "authorizing by way of the script editor"? I'd really not like to have to go into each users account to authorize this script for them (as well as have to remember to do the same for any new user created)?
I appreciate any help offered!

Unfortunately NOT. see also my question and the answer by one of the google developer advocates.
You can vote for the issue I opened to push for a solution.

Related

The script works from the sheet, but not as addon with the same code

The project is published as a Sheets add-on. A trusted tester (also a spreadsheet editor) launches an add-on and it does not work. He receives the message "You do not have access to perform that action. Please ask the owner of this item to grant access to you".
If the editor starts the built-in script in the same spreadsheet with the same code, then everything works.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sName = ss.getName();
var eds = ss.getEditors();
var owner = eds[0];
var ed = eds[1];
var sheet = ss.getSheetByName("Task");
var emailAddress = owner.getEmail();
var subject = "Done! - " + sName;
var localDate = Date();
sheet.getRange(12, 2, 1, 1).setValue(localDate);
var values = sheet.getRange("B2:K11").getValues();
var message = "#" + values + "#" + localDate + "#";
MailApp.sendEmail(emailAddress, subject, message);
ss.removeEditor(ed);
Please advise where to dig.
Installing a add-on doesn't grant all permissions to the add-on. It also needs to be enabled. Published add-ons run in AuthMode.NONE unless enabled. Try adding a menu item and running the function there first to enable it in the document.
Add-on Authorization

Creating a group with Admin SDK Directory API in Google Apps Script doesn't work "On form submit"

I've read through all of the relevant pages in the Admin ADK Directory API documentation and several questions on stackoverflow, and I'm still stuck.
I am the super admin of my Google Apps domain, and I want users in my domain to be able to create their own Google Groups. I made a Google Form where the user specifies the name and email of the group. Then the Google Form Responses sheet has an "On form submit" trigger that invokes my code to create the group.
This code works when I run createGroupTest() from the Script Editor. It creates the group in my Google apps domain immediately.
This code does not work when the "On form submit" trigger runs the onFormSubmit(e) function. I get the email from the catch(e) saying Exception: Failed to authenticate for service: Groups.
Does anyone know what is causing the oauth authentication to work from within the Script Editor but not when invoked by the onFormSubmit function?
function onFormSubmitTest() {
var t = new Date();
t = t.getTime();
onFormSubmit([t, "AAA Test Group " + t], ["aaa.testgroup." + t + "#mydomain.com"], ["me#mydomain.com"]);
}
var consumerKey = "mydomain.com";
var consumerSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
var domainName = "mydomain.com";
function onFormSubmit(e) {
var timestamp = e.values[0];
var groupName = e.values[1];
var groupEmail = e.values[2];
var owner = e.values[3];
owner = owner.split("#")[0];
var description = 'test';
var requestBody = {email: groupEmail, name: groupName, description: description};
var scope = "https://www.googleapis.com/auth/admin.directory.group";
var fetchArgs = googleOAuth_("Groups", scope);
fetchArgs.method = "POST";
fetchArgs.contentType = "application/json";
fetchArgs.payload = JSON.stringify(requestBody);
fetchArgs.muteHttpExceptions = true;
var url = 'https://www.googleapis.com/admin/directory/v1/groups?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
UrlFetchApp.fetch(url, fetchArgs);
}
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(consumerKey);
oAuthConfig.setConsumerSecret(consumerSecret);
return {oAuthServiceName:name, oAuthUseToken:'always'};
}
I figured it out: I had failed to include the domain extension in the groupEmail string (because my Google Form only asks the user to fill in the group email name without the domain extension).

How to add a Google Drive folder to "My Drive" section to other users

I have a Google Drive herarchichal folder and subfolders structure owned by me and I want to add it to the "My Drive" section in all users in our Google Apps for Business domain automatically.
How can I do this? I heard about Google Apps Script and AddToFolder function.
Please, can you help me?
Thanks in advance.
This is very easy to do if each user could just access a link and authorize a script (that you build) to do the job for them (place a shared folder in their root folder).
But if it's a lot of users, you are the admin of the domain, and you really want to do it all automatically without anyone doing a thing, it is possible but probably very difficult to do. I mean, you need to access the Drive API directly and set oAuth 2.0 to impersonate your users, because the Apps Script built-in DocsList API does not have this impersonation feature. If you're really going for it, take a look at this other question.
First, set up a simple web app. The Google App Script editor even has a template that gets you most of the way there.
Second, implement something like the following and call it from the handler function.
function addRequiredFolders() {
var root = DocsList.getRootFolder();
var folderIds = ["somefolderid", "anotherfolderid"];
folderIds.map( function(id) { DocsList.getFolderById(id).addToFolder(root) } );
}
I've tested a variant of this up to this point. The next step is to publish the Web App for your domain, and email it out to people or otherwise distribute it. I assume they will have the unpleasant step of needing to grant the web app permission to access their documents.
Right now I've implemented this feature using "Google Documents List API". I know that this API is deprecated but for now it works.
(the code is not finished)
(...)
//var user = Session.getActiveUser().getUserLoginId() OR
var user = e.parameter.user_email
var TB_folder = e.parameter.TB_folder
var TB_sub_folder = e.parameter.TB_sub_folder
var base = 'https://docs.google.com/feeds/';
var fetchArgs = googleOAuth_('docs', base);
fetchArgs.method = 'POST';
var rawXml = "<?xml version='1.0' encoding='UTF-8'?>" + "<entry xmlns='http://www.w3.org/2005/Atom'>"
+ "<category scheme='http://schemas.google.com/g/2005#kind' "
+ "term='http://schemas.google.com/docs/2007#folder'/>"
+ "<title>" + TB_folder +"</title>"
+ "</entry>";
fetchArgs.payload = rawXml;
fetchArgs.contentType = 'application/atom+xml';
fetchArgs.contentLength = 245;
// POST a https://docs.google.com/feeds/default/private/full
var url = base + user + '/private/full/?v=3&alt=json';
var content = UrlFetchApp.fetch(url, fetchArgs).getContentText()
var json = Utilities.jsonParse(content)
var folder = json.entry.gd$resourceId.$t // -> I get "folder:folder_id"
var id_folder = folder.split(':')[1]
var folder_created_by = json.entry.gd$lastModifiedBy.email.$t
var folder_owner = json.entry.author['0'].email.$t
(...)
Now, you have the folder ID and can use it to create another subfolder or a file...
You need this function :
//Google oAuth
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("XXXXXXXX");
oAuthConfig.setConsumerSecret("XXXXXXXXXXXXXXXXXXX");
//oAuthConfig.setConsumerKey("anonymous");
//oAuthConfig.setConsumerSecret("anonymous");
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
You can create a file or add a user to a file (add a wriker).
Now I want to implement this functionality with " Drive API " . If someone has done it would be nice to get some help.
Sergi

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

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

Mail Document via apps script

It is possible to mail the contents of a google document via apps script? I can see how to mail a link to the document, which works fine if the recipient has access.
It is possible to do this manually
- checkmark the document
- from the More.. button select Share... then Email as Attachment...
- from the next screen select the attach as drop down and select Paste the item itself into the email.
However, I can't figure out how to do this through a script.
This is fairly straightforward and there are a few examples available on the internet but basically here is how to proceed to send it as a pdf attachment:
var docName = DocumentApp.openById(docID).getName();
var pdf = DocsList.getFileById(docID).getAs('application/pdf').getBytes();
var attach = {fileName: docName+".pdf",content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(emailadress, 'Your document as PDF ('+docName+')', 'see attachment', {attachments:[attach]});
I hope this example is clear enough.
EDIT : following your comment, I think this post by Henrique on the old Google group forum should fulfill your needs.( It's a workaround that I use a lot in many scripts and it works beautifully... the only annoying detail is that it needs to be authorized from the script editor (not the usual red border popup).
Here is how it goes :
function emailDocTest() {
var id = 'Doc-Very-Long-ID-Here';
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();
var emailAdress = Session.getEffectiveUser().getEmail();
MailApp.sendEmail(emailAdress, 'test doc send by mail as html', 'html only', {htmlBody:doc});
}
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"};
}