Access to spreadsheet in deployed web app - google-apps-script

I am trying to access a Google spreadsheet via a Google script, that is published as a web app.
How I created the script:
from the spreadsheet, Tools/Script editor..., Spreadsheet project
and it asked for access to the spreadsheets in GDrive, so overall I assume it is attached to the spreadsheet.
The script:
function doGet() {
var ss = SpreadsheetApp.getActive();
// alternative, doesn't work either
// var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/abcdef/edit");
var sheets = ss.getSheets();
var text = ... loop over sheets and do some stuff to get the data ...
return ContentService.createTextOutput(text);
}
The error message when calling the web app:
TypeError: Cannot call method "getSheets" of null.
The function works (minus the ContentService.createTextOutput of course) when run in the editor.

You can't use the getActive() method unless the script is bound to a spreadsheet (ie script was created within the spreadsheet).
In your example with openByUrl() I would check your URL and the permissions to that sheet. Here is working example of what you were trying to do.
function doGet(){
var ss= SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1XLOVvEo2nzLARwRhsiWUrFMtF0LnofC1PQoWIeLmwgQ/edit#gid=0');
return ContentService.createTextOutput(ss.getSheets()[0].getName()).setMimeType(ContentService.MimeType.TEXT);
}

Related

Running Script in Other Spreadsheet from Current Spreadsheet

I have two different Spreadsheets with their own script projects. I have created a library from SpreadsheetA and linked the same to SpreadsheetB. Now when I call the function of SpreadsheetA from the script of SpreadsheetB it runs the same in the current active sheet of SpreadsheetB while I want to run the function on selected sheet of SpreadsheetA.
function createDO() {
var ss = SpreadsheetApp.getActive(); //SpreadsheetB
var cell = ss.getActiveCell().getRowIndex();
var spreadSheetAURL = "URL"; //SpreadsheetA
var spreadSheetsInA = SpreadsheetApp.openByUrl(spreadSheetAURL).getSheetByName("name");
spreadSheetsInA.activate()
Sheetname.Project(); //script in SpreadsheetA
};
I need to run the Sheetname.Protect() function for SpreadsheetA from the current SpreadsheetB.
Possible Solution:
Firstly, it is worth noting that libraries do not work like this. As Oleg Valter has stated, libraries are by nature utility scripts which are not bound to any one specific script/class/project.
As a workaround, however, you can set up a web app which can be called from one project to another, running code inside a doGet() function on the respective Spreadsheet.
A structure would look something like this:
In Spreadsheet A:
function doGet(e) {
var sheetName = e.parameter.sheetName
var sheet = SpreadsheetApp.getActiveSpreadheet.getSheetByName(sheetName);
sheet.activate();
// I am not how this works, but I have extrapolated from your question
Sheetname.Project();
}
Then deploy this as a Web App from the Publish > Deploy as web app... menu item. The settings should be Execute the app as: me and Who has access to the app: Anyone (even anonymous).
Then, getting the Web App URL from the dialog, in Spreadsheet B:
function createDO() {
var ss = SpreadsheetApp.getActive(); //SpreadsheetB
var cell = ss.getActiveCell().getRowIndex();
var webAppUrl = "URL"; //SpreadsheetA
UrlFetchApp.fetch(webAppUrl + "?sheetName=name");
};
The ?sheetName=name parameter is a URL query string which would be passed to the web app's doGet(e)'s event object. This is what is accessed with e.parameter.sheetName.
The string on the right-hand-side of the = in the query string needs to be the sheet name you were trying to activate originally.
References:
Web Apps | Apps Script | Google Developers
Query string - Wikipedia
Simple Triggers - doGet(e) and doPost(e) | Apps Script | Google Developers
Event Objects | Apps Script | Google Developers

renaming sheets with "full sheet" charts on them causes spreadsheet to reload

Google sheets allows charts to be moved to their own sheet. Google scripts appears to be unable to rename these sheets though, without crashing the spreadsheet and forcing it to reload.
To see what I mean try this:
1) create new spreadsheet and put some data in it.
2) create any type of chart w/ the data.
3) edit the chart and select "Move To Own Sheet..."
4) create a script w/ the following code:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
sheets[1].setName('bob');
}
5) run the script and the spreadsheet crashes with the message: "Unable to Load File"
6) reload the spreadsheet and notice that the sheet did get renamed.
Why does the spreadsheet crash? Is it possible to rename the sheet w/o crashing?
Unfortunately, I couldn't find the clear reason of the crash. But I think that there is a workaround for avoiding the crash. So how about this workaround? In this workaround, it uses Sheets API. In order to use Sheets API, please enable Sheets API at Advanced Google Services and API console. About the how to enable them, please check here.
The sample script using Sheets API is as follows.
Sample script :
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var id = ss.getId();
var sheetId = ss.getSheets()[1].getSheetId();
var resource = {"requests":[{"updateSheetProperties":{"properties":{"sheetId":sheetId,"title":"bob"},"fields":"title"}}]};
Sheets.Spreadsheets.batchUpdate(resource, id);
}
If this was not what you want, I'm sorry.

How do you make a google script run on whichever google sheet is open?

I made an app to insert data into a google spreadsheet. Usually you set the url of the google spreadsheet that the script is going to perform the actions specified in it.
function doGet(e){
var ss = SpreadsheetApp.openByUrl("your google spread sheet url");
var sheet = ss.getSheetByName("Sheet1");
Instead of setting a permanent url, i wanted the google script to perform the actions on a google spreadsheet that is open at that moment. This way if i create a new google spreadsheet, i don't have to change the url in the script
to add data since the app script will run on whichever spreadsheet is open.
Initially i tried this but it didn't work
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
When i used getActiveSpreadSheet and deployed it as a web app(testing the code) it gave me this error:
TypeError: Cannot call method "getSheetByName" of null. (line 4, file "Code")
Edited.
I tried this but wont work.
function doGet(e){
var newUrl = e.parameter.newurl
var ss = SpreadsheetApp.openByUrl([newurl]);
var sheet = ss.getSheetByName("Sheet1");

Call to insertSheet works in Google Script Editor and was authorised but dosent work when calling from the spreadsheet

I have written a custom function that calls the code below to create a temporary sheet. When I ran it in Google Editor I was asked for authorisation and granted access.
My custom function runs well in Google Editor but doesnt work in the spreadsheet and I get the error "You do not have the permission to call insertSheet".
I created the spreadsheet and custom function so I own (have access) to everything.
var tempSheetName = "temp";
var sp = SpreadsheetApp.getActiveSpreadsheet();
var activeCell = sp.getActiveCell();
sp.insertSheet(tempSheetName);
From Google Apps Guide : Link
If your custom function throws the error message You do not have
permission to call X service., the service requires user authorization
and thus cannot be used in a custom function.
You may have your reasons, why you want to accomplish it in this way. But I'm really not sure if a custom function in a cell is the right place to create a new tab. You usually do calculations within a cell not document manipulations.
What, if you'd create a menu entry for the function like this and call it from there?
function onOpen() {
// instantiate UI object
var ui = SpreadsheetApp.getUi();
// create the menu
ui.createMenu("Functions")
.addItem("Create Sheet", "createSheet")
.addToUi();
}
function createSheet() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet(),
sheetName = 'mySheet',
sheet = ss.getSheetByName(sheetName);
// delete sheet if it already exists
if (sheet) ss.deleteSheet(sheet);
// insert sheet
ss.insertSheet(sheetName, 0);
}
Do you still get an error? Just curious...

Find the ID of the attached spreadsheet from doGet function

I am looking to have 'openById' find the ID of the spreadsheet it is attached to automatically, if this is possible?
Currently I am pulling cells from a spreadsheet into an HTML template which populates the design with the cell data.
If a user 'makes a copy' of the spreadsheet, the ID (which I have entered manually) is still that of the original spreadsheet I am using, not the new spreadsheet ID of the one they are using.
Would it be possible to get the ID of the spreadsheet that the script is attached with dynamically?
// code.gs
function doGet() {
var template = HtmlService.createTemplateFromFile('index.html')
template.sheet = SpreadsheetApp.openById('THE SPREADSHEET ID');
// returning .evaluate() (an HtmlOutput object) will allow you to display this in a web app.
// to get the string HTML content, use .getContent() on the HtmlOutput
return template
.evaluate();
}
The method openById requires an Id, it does not return it. One can get an id with getId called on a spreadsheet object. However, doGet and doPost functions don't have a concept of active spreadsheet; the method SpreadsheetApp.getActiveSpreadsheet() returns null when called from them. It seems that Web Apps are never considered bound to a spreadsheet, as documentation hints at when listing triggers.
So, there is no direct way to achieve what you want. But there is a workaround: instruct the user to execute a function capturing Id and storing it in ScriptProperties (they'll need to authorize this, so onOpen won't do). Example:
function recordId() {
var ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
PropertiesService.getScriptProperties().setProperty('id', ssId);
}
function doGet(e) {
var id = PropertiesService.getScriptProperties().getProperty('id');
var ss = SpreadsheetApp.openById(id); // have access to spreadsheet
return ContentService.createTextOutput(id); // confirms that id is known to the script
}
You can make the process easier by using onOpen to create a menu item that will launch recordId.