When you enter an importrange function manually into a spreadsheet you receive a pop up and must 'allow access'.
However, I'm trying to find a way to do this via a script because I'm creating many spreadsheets, each with a query-importrange function (I 'own' the spreadsheet which has data to import). There's too many for me to manually 'allow access' via the pop up and update the function to include the query function.
Hence, I'm looking for a function call in apps script that can perform the same action that the pop up did. Code segment example below.
Does anyone know of a function that can 'allow access'?
Stefan
// create new spreadsheet file
...
var ss = createSpreadsheet(fileName);
var spreadsheet = SpreadsheetApp.open(ss);
var sheet = spreadsheet.getSheetByName("Sheet1");
// Add student as Viewer
spreadsheet.addViewer(studentEmail);
// Add ImportRange function
var sheet = spreadsheet.getSheets()[0];
var cell = sheet.getRange("A1");
var filter = "select * where Col3='" + studentEmail + "'";
var qry = '=QUERY(importRange("' + fileKey + '","14-15S2!A1:AE");"' + filter + '";1)';
cell.setValue(qry);
// I need a function to 'allow access' here, so the function can be allowed access. Otherwise, it throws an error.
...
#Franzi suggested using undocumented approach which works and does not require making a donor/source spreadsheet public. Here's how you can do it from Google App Script:
function addImportrangePermission() {
// id of the spreadsheet to add permission to import
const ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
// donor or source spreadsheet id, you should get it somewhere
const donorId = '1GrELZHlEKu_QbBVqv...';
// adding permission by fetching this url
const url = `https://docs.google.com/spreadsheets/d/${ssId}/externaldata/addimportrangepermissions?donorDocId=${donorId}`;
const token = ScriptApp.getOAuthToken();
const params = {
method: 'post',
headers: {
Authorization: 'Bearer ' + token,
},
muteHttpExceptions: true
};
UrlFetchApp.fetch(url, params);
}
Usually there is no need, but in some rare cases you might want to add the required oauthScopes in appscript.json manifest:
...,
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.external_request"
],
...
I had a problem similar to this and found the answer was to alter the permissions of the spreadhseet file from which you are importing data (the "filekey" in your example").
This is the google app script that made "Allow Access" go away for me:
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW)
I'm doing it automatically using the gspread python library by calling the addimportrangepermissions endpoint.
sheet.client.request(
'post',
f'https://docs.google.com/spreadsheets/d/{sheet.spreadsheet.id}/externaldata/addimportrangepermissions',
params={'donorDocId': external_sheet.spreadsheet.id}
)
I know it's not via the apps script, but it could provide you some hints on how to do it there.
Note: I didn't find any doc about this, so I though this could help anyone trying to do the same in any platform (app script, python, etc.).
This code is based on the answer by #kishkin:
/**
* #param {string} fileId - id of the spreadsheet to add permission to import
* #param {string} donorId - donor or source spreadsheet id, you should get it somewhere
*/
function addImportrangePermission_(fileId, donorId) {
// adding permission by fetching this url
var url = 'https://docs.google.com/spreadsheets/d/' +
fileId +
'/externaldata/addimportrangepermissions?donorDocId=' +
donorId;
var token = ScriptApp.getOAuthToken();
var params = {
method: 'post',
headers: {
Authorization: 'Bearer ' + token,
},
muteHttpExceptions: true
};
UrlFetchApp.fetch(url, params);
}
It is compatible with the ES5 engine and easily used as a function outside.
All I did was share the 'source' sheets - the sheets from which ImportRange grabs its data - with the people in my organsation who are using the gsheet template. I edited the template by clicking the authorise access button and all sheets that I have made from the template since have worked.
The ImportRange function works without me having to re-authorise access within each new sheet created from the template. I hope it works for others in my organisation.
Related
I'm trying to send table charts on a daily basis to my slack channel from my google spreadsheet. The automation works when I am creating some normal charts - e.g. column charts or line charts - but it shows an error if I try the same thing with a table chart. Should I change any of my code to have this running?
function myFunction(){
var sheetUrl2 = "https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxx/edit#gid=xxxxx"
var ss2 = SpreadsheetApp.openByUrl(sheetUrl2);
var sheet2 = ss2.getSheetByName("MyDailySheet");
var charts2 = sheet2.getCharts();
var chartImage2 = charts2[0].getBlob().getAs('image/jpeg').setName("graph.png");
sendSlack(chartImage2);
}
function sendSlack(chart){
var url = 'https://slack.com/api/files.upload';
var token = 'xoxp-xxxxxxxxxxxxxxxxxxxxxxxxxx';
var channel = '#general';
var payload = {
'token' : token,
'channels' : channel,
'file' : chart,
'filename' : 'DailyUsers - Last 30 Days'
};
var params = {
'method' : 'post',
'payload' : payload
};
var response = UrlFetchApp.fetch(url, params);
}
Error message:
Exception: Service Spreadsheets failed while accessing document with id xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
myFunction # DailyChart02.gs:7
This code works perfectly and sends a chart to my slack channel.
My slack app has these token scopes authorized: (bot) chat:write chat:write.public files:write (User) chat:write files:write
My table chart looks something like this. It shows perfectly on my spreadsheet file.
Issue and workaround:
I have had the same situation with you. In the current stage, it seems that the table chart cannot be directly retrieved as the image blob. This might be the current specification. So as a current workaround, I would like to propose the following flow. In this workaround, Google Slides is used as a wrapper. By the way, in your script, getAs('image/jpeg') is used. But it seems that setName("graph.png") is used. So in this modification, the mimeType uses image/png from the filename.
Retrieve the table chart from Google Spreadsheet.
Create new Google Slides as a temporal file.
Insert the table chart from Google Spreadsheet to Google Slides.
Retrieve the inserted chart on Google Slides as an image blob.
Remove the temporal Google Slides.
Use the blob to the Slack API.
By this flow, the table chart can be retrieved as the image blob. When your script is modified, it becomes as follows.
Modified script:
Please modify myFunction() as follows.
function myFunction(){
var sheetUrl2 = "https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxx/edit#gid=xxxxx"
var ss2 = SpreadsheetApp.openByUrl(sheetUrl2);
var sheet2 = ss2.getSheetByName("MyDailySheet");
var charts2 = sheet2.getCharts();
// --- I added below script.
var s = SlidesApp.create("temporalSlides");
var chartImage2 = s.getSlides()[0].insertSheetsChartAsImage(charts2[0]).getBlob().setName("graph.png");
DriveApp.getFileById(s.getId()).setTrashed(true);
// ---
sendSlack(chartImage2);
}
Note:
In this case, how about reporting it to Google issue tracker as the future request? Ref
Reference:
insertSheetsChartAsImage(sourceChart)
I have created a script in google scripts, to take a spreadsheet and fill out multiple google docs successfully. I am trying to apply this logic to filling out an HTML form, basically, we need user-generated info (in our google sheet) to fill out an HTML form on a web page.
How would I get the following function to not only open but write in the data?
This is where I am at (just using an example webpage):
function testNew(){
var js = " \
<script> \
window.open('https://colorlib.com/etc/cf/ContactFrom_v1/index.html', '_blank', 'width=800, height=600'); \
google.script.host.close(); \
</script> \
";
var html = HtmlService.createHtmlOutput(js)
.setHeight(10)
.setWidth(100);
SpreadsheetApp.getUi().showModalDialog(html, 'Now loading.'); // If you use this on Spreadsheet
// DocumentApp.getUi().showModalDialog(html, 'Now loading.'); // If you use this on Document
// SlidesApp.getUi().showModalDialog(html, 'Now loading.'); // If you use this on Slides
}
This is an example of what I did with the google docs, trying to replicate in forms:
function myFunction() {
var data = Sheets.Spreadsheets.Values.get('google sheet ID HERE', 'A2:R300');
// google doc template id, already got deal memo
var templateId = 'google doc ID HERE';
// loop through the values "i" is looping the rows, "#" is the column example: 0=a,1=b
for (var i = 0; i < data.values.length; i++) {
var date = data.values[i][0];
var email = data.values[i][1];
// grab the google doc template, create a copy, and generate the new id
var documentId = DriveApp.getFileById(templateId).makeCopy().getId();
// change the name of the new file
DriveApp.getFileById(documentId).setName(companyName+ '-' + projectName+ '-' + 'Insurance Report');
// get the document body as a variable
var body = DocumentApp.openById(documentId).getBody();
// replace values with google sheet data
body.replaceText('##Date##', date);
body.replaceText('##Email##', email);
I have many functions that I have written that interact with a lot of third party forms and websites. Don't let it fool you that all that a form is, is a human-readable way to "POST" to a url. The easier way to do this is to use the UrlFetchApp.fetch(url, options) function in Google Apps script.
I use Chrome but other browsers have this functionality as well:
open Chrome, navigate to the form and then press F12 to open up the developers window.
Click on 'Network'
fill out the form manually and then review the traffic in the 'Network' window and find the POST that your browser sent to the site,
With that highlighted, you will see a couple other tabs for 'Headers','Preview','Response'
In the 'Headers' tab, scroll to the bottom and it will show you what the request looked like. Screen shot this and send these variables through the UrlFetchApp.fetch() to the website as the 'payload' and formatted like the function below.
Look at the 'Request URL' at the top of that same 'Headers' tab and you will use that as the URL below:
function senddatatoform() {
var url = 'http://theurlthattheformpoststohere.com'; // this is the 'request url' as shown in your browser (not always the url of the form).
var payload = {
datapoint1: datapoint1value,
datapoint2: datapoint2value,
... //continue with all required post data
}
var options = {
method: "POST",
payload: payload
}
var response = UrlFetchApp.fetch(url,options);
/*this is where what you need to do next might vary -- if you're looking for a 'success' page, you might write some code here to verify that the http response is correct based on your needs.
*/
}
I am thinking of creating a google slides to Figma exporter using Google App Script. Starting out I would first like to route the shapes created in from google Slides to figma. How would I go about setting up my file? And I don't know how to set up the Oauth api communication between Google and Figma or if it's even possible.
I believe that I can start with:
References
Figma reference
https://github.com/figma/plugin-samples/blob/master/react/src/code.ts
google app script reference
https://github.com/gsuitedevs/apps-script-samples/blob/master/slides/style/style.gs#L30
Get Figma Shape
var file=projectid.key()
var=figma rectangle= file()
await figma.loadFontAsync({ family: "Roboto", style: "Regular" })
name;
var figmaShape = {
figma.ui.onmessage = msg => {
if (msg.type === 'create-rectangles') {
const nodes = []
for (let i = 0; i < msg.count; i++) {
const rect = figma.createRectangle()
rect.x = i * 150
rect.fills = [{type: 'SOLID', color: {r: 1, g: 0.5, b: 0}}]
figma.currentPage.appendChild(rect)
nodes.push(rect)
}
figma.currentPage.selection = nodes
figma.viewport.scrollAndZoomIntoView(nodes)
}
figma.closePlugin()
}
};
Get Google Docs File Shape
var powerpointfile = driveApp.getFileById = ("### Slide file ID ###")
function powerPointShape = () {
var slide = SlidesApp.getActivePresentation().getSlides()[0];
var shape = slide.insertShape(SlidesApp.ShapeType.TEXT_BOX, 100, 200, 300,
getObjectId.element(SHAPE);
};
Create new Figma file#
file.getSlides.shape = (powerPointShape, ) => {
this.powerPointShape.getRigh()=this.figmaShape(rect.x);
this.powerPointShape.getleft()=this.figmaShape(rect.y);
}
But from there would I also want to get the file id from google app script to a Figma File?
and after looking at: https://github.com/alyssaxuu/figma-to-google-slides/blob/master/Chrome%20Extension/background.js I wonder if I would have to create a chrome extension or a google Slides plugin.
How about this answer?
Issue and workaround:
Unfortunately, it seems that the shapes of Google Slides cannot be put to the page of Figma file. Because it seems that there are no methods of API for putting the shapes. But it was found that that the pages of Figma file can be retrieved as the image using Figma API.
In this answer, I would like to propose the sample script that the pages of Figma file can be put to the Google Slides as the image using Figma API with the access token. So you can directly use Figma API with Google Apps Script.
Usage:
1. Retrieve access token
You can see the method for retrieving the access token at here. Although there is also OAuth2 for retrieving the access token, in your situation, I thought that the method for directly generating the access token on the site might be suitable. So in this answer, the generated access token on the site is used. Please retrieve the access token as follows.
Generate a personal access token
Login to your Figma account.
Head to the Account Settings from the top-left menu inside Figma.
Find the Personal Access Tokens section.
Click Create new token.
A token will be generated. This will be your only chance to copy the token, so make sure you keep a copy of this in a secure place.
The access token is like #####-########-####-####-####-############. At Google Apps Script, the authorization is done by headers: {"X-Figma-Token": accessToken}.
2. Retrieve file key
In order to retrieve the Figma file using Figma API, the file key is required. You can retrieve the file key from the URL of the file.
The URL of the file is like https://www.figma.com/file/###/sampleFilename. In this case, ### is the file key.
3. Run script
The sample script is as follows. Before you run the script, please set the variables of accessToken and fileKey.
function myFunction() {
var accessToken = "###"; // Please set your access token.
var fileKey = "###"; // Please set the file key.
var baseUrl = "https://api.figma.com/v1";
var params = {
method: "get",
headers: {"X-Figma-Token": accessToken},
muteHttpExceptions: true,
};
var fileInfo = JSON.parse(UrlFetchApp.fetch(baseUrl + "/files/" + fileKey, params));
var children = JSON.parse(UrlFetchApp.fetch(baseUrl + "/images/" + fileKey + "?format=jpg&scale=3&ids=" + fileInfo.document.children.map(function(c) {return c.id}).join(","), params));
if (!children.err) {
var s = SlidesApp.create("sampleSlide");
var slide = s.getSlides()[0];
var keys = Object.keys(children.images);
keys.forEach(function(c, i) {
slide.insertImage(children.images[c]);
if (i != keys.length - 1) slide = s.insertSlide(i + 1);
})
} else {
throw new Error(children);
}
}
When myFunction() is run, at first, the file information is retrieved with the file key fileKey. Then, all pages are retrieved from the retrieved file information, and the retrieved pages are put to each slide of new Google Slides.
I think that the action of this script is similar to the script which is shown at the bottom of your question.
Note:
This is a sample script. So please modify it for your actual situation.
References:
Figma API
Class UrlFetchApp
Class SlidesApp
If I misunderstood your question and this was not the direction you want, I apologize.
I want to add a custom casting feature to my Google Home. What I've done so far was the tutorial using Dialogflow from this page: https://developers.google.com/actions/dialogflow/first-app
Then I found a repo about casting videos to Chromecast, only from PC and not from Google Home: https://github.com/googlecast/CastHelloVideo-chrome/blob/master/helloVideos.js
Then I tried combining the two. I've only tried to connect to my Chromecast so far, but I get this error every time:
API Version 2: Failed to parse JSON response string with 'INVALID_ARGUMENT' error: ": Cannot find field.".
I made the tutorial work, so I didn't change anything on Dialogflow, only the index.js file.
This is my file:
'use strict';
process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').DialogflowApp;
const functions = require('firebase-functions');
// a. the action name from the make_name Dialogflow intent
const NAME_ACTION = 'game_cast';
// b. the parameters that are parsed from the make_name intent
const COLOR_ARGUMENT = 'color';
const NUMBER_ARGUMENT = 'number';
exports.gameCast = functions.https.onRequest((request, response) => {
const app = new App({request, response});
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body: ' + JSON.stringify(request.body));
// c. The function that generates the silly name
function gameCast (app) {
let number = app.getArgument(NUMBER_ARGUMENT);
let color = app.getArgument(COLOR_ARGUMENT);
// app.tell('Alright, your silly name is ' +
// color + ' ' + number +
// '! I hope you like it. See you next time.');
launchApp();
}
// d. build an action map, which maps intent names to functions
let actionMap = new Map();
actionMap.set(NAME_ACTION, gameCast);
app.handleRequest(actionMap);
});
The launchApp() function call in the gameCast function would connect to my Chromecast. Additionally I added the whole content of helloVideos.js, so there wouldn't be any missing functions that launchApp() may uses. (I didn't include my original file here, as the file helloVideos.js has 617 lines of code.)
Any suggestions would be greatly appreciated! Thanks!
" Cannot find field" comes when your google action intent name is different from gamecast on the api.ai console.
You can goto google cloud back end https://console.cloud.google.com and check the logging error.
I am terribly confused on how one is to write a javascript client (non-gadget) to a private Google Spreadsheet using supported APIs? I have no difficulties getting an OAuth2 Drive API client going, but then there is no spreadsheet support!
https://developers.google.com/apis-explorer
This issue crudely asks for the spreadsheet API to appear on that page:
http://code.google.com/p/google-api-javascript-client/issues/detail?id=37
I am probably missing something obvious, so thank you for your kindness to help me...
Update:
Wow, this is kicking my behind! So, I am going down the path of attempting to take the access_token from the Oauth2 workflow and then set the gdata API Authorization header like so:
service = new google.gdata.client.GoogleService('testapp');
service.setHeaders({'Authorization': 'Bearer '+ access_token});
Unfortunately, chrome console shows that this header is not actually getting sent to google when I do something like
service.getFeed(url, cb, eb);
Uffff!
In order to get information from Google Spreadsheets, just send a GET request to the relevant link with the access token attached. The urlLocation is found by going to Google Drive and copying the long string of digits and letters in the url after the word "key=".
Also I used jQuery in this example.
Code:
var urlLocation = ''; //Put the Spreadsheet location here
var url = 'https://spreadsheets.google.com/feeds/list/' + urlLocation + '/od6/private/full?access_token=' + token;
$.get(url, function(data) {
console.log(data);
});
In order to get a JSON representation, use this instead:
var urlLocation = ''; //Same as above
var url = 'https://spreadsheets.google.com/feeds/list/' + urlLocation + '/od6/private/full?alt=json-in-script&access_token=' + token + '&callback=?';
$.getJSON(url, function(data) {
console.log(data);
});