Find the ID of the attached spreadsheet from doGet function - google-apps-script

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.

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

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...

Pass a variable between a script bound to a Google Form and a script bound to its destination sheet

I am having issues getting information from a Google Form into a Google Sheet. I am looking to get the edit url onFormSubmit and then set it to the end of the record in a column where the responses are stored.
Research:
I asked this question, which started as a script bound to the sheet but trying to access the form. It then became a script bound to the form, trying to access the sheet.
I found this question which looks to be related to my question (with a slightly different use case). Similarly to mine, I think it will have issues getting spreadsheet methods while on the form.
Since both required methods that are only available to either the script or the form I keep hitting a wall. Now I am thinking that I may need a hybrid solution that requires some of the code to be bound to the sheet, and some to be bound to the form, with a variable passed between the two scripts that are both executing onFormSubmit.
This is what I think I should keep bound to the form
function onFormSubmit(e)
{
Logger.clear; //if I can use log to pass variable I want to clear out at the beginning of each submission
var form = FormApp.getActiveForm();
var activeFormUrl = form.getEditUrl();//This is the variable I need to pass to the sheet
Logger.log(activeFormUrl); //only to confirm what we are getting unless I can somehow access the log after the fact using sheet script
}//This is the end of onFormSubmit function bound to the Form
This is what I think I should keep bound to the sheet
function onFormSubmit(e)
{
var ss = SpreadsheetApp.getActiveSheet();
var createDateColumn = ss.getMaxColumns(); //CreateDateColumn is currently in AX (Column 50) which is the last/max column position
var urlColumn = createDateColumn-1; //urlColumn is currently in AX (Column 50) Calculating using it's relative position to createDateColumn Position
if (ss.getActiveRange(urlColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite editResponseURL
{
var editResponseURL = setGoogleFormEditUrl(ss, createDateColumn, activeFormUrl);
var createEditResponseUrl = ss.getActiveRange(urlColumn);
createEditResponseUrl.setValue(activeFormUrl);
}
else
{
if (ss.getActiveRange(urlColumn).getValue() != activeFormUrl)
{
Logger.log("Something went wrong - URL doesn't match" + activeFormUrl);
Logger.log(ss.getActiveRange(urlColumn).getValue());
var checkLog2 = Logger.getLog();
}
else {}//do nothing
}
}//This is the end of the onFormSubmit function bound to the Sheet
What I need to know is how to take activeFormUrl from the form script and send it to the sheet script. Can I use the log?
I'm not sure if this would work for you, but you can make an HTTPS GET or POST request to an Apps Script project with UrlFetchApp.fetch(url). So, from the Form project, you can make an HTTPS POST request to a published Web App. The published Web App can actually be published from the project bound to the spreadsheet, if you want to do that.
The way that an Apps Script project detects an HTTPS GET or POST request being sent to it, is with either a doGet() or doPost() function.
var webAppUrl = "https://script.google.com/macros/s/123_My_FileID/exec";
var payload = {
"url":"activeFormUrl"
};
var options = {"method":"post","payload":payload};
UrlFetchApp.fetch(webAppUrl, options);
The above code makes a POST request to another Apps Script project, and sends the payload to the file.
function doPost(e) {
var theUrl = e.parameter.url;
};
I'm assuming that you are trying to have a spreadsheet that is getting data from multiple Forms?
I had to separate the form and the spreadsheet operations as getting the formEditURL using the FormApp method would not work if I was using other SpreadsheetApp methods in the same function and the FormApp method only worked if it was in the onFormSubmit function.
Here is the code snippet which I used successfully
function onFormSubmit(e)
{
var rng = e.range; //Collects active range for event
var ss = SpreadsheetApp.getActiveSpreadsheet();//collects active spreadsheet object
var fUrl = ss.getFormUrl();//gets form url linked with active spreadsheet
var f = FormApp.openByUrl(fUrl);//opens form using form url
var rs = f.getResponses(); //gets responses from active form
var r = rs[rs.length - 1]; // Get last response made on active form
var c = getCellRngByCol(rng, 'formEditURL'); //locates the cell which the form url will be stored by searching header name
c.setValue(r.getEditResponseUrl());// sets form url value into correct cell for active form response
var callSpreadsheetFunctions = spreadsheetFunctions(rng, ss); //method calls other spreadsheet functions. This had to be modularized as you can't get form url if the other functions are occuring in the same function
}//This is the end of the onFormSubmit function
function spreadsheetFunctions (rng, ss)
{
var rowIndex = rng.getRowIndex();//gets row index for current response. This is used by tracking number
var createDateCell = getCellRngByCol(rng, 'CreateDate'); //locates which cell the createdate will be stored in by searching header name
var timestampCell = getCellRngByCol(rng, 'Timestamp'); //locates which cell the autogenerated timestamp is located in by searching header name
var trackingNumberCell = getCellRngByCol(rng, 'Tracking ID#');//locates which cell the tracking ID# will be stored in by searching by header name
var createDate = setCreateDate(rng, createDateCell, timestampCell); //method sets create date. NOTE: Function not included in code snippet but left here to demonstrate type of information used
var trackingNumber = setTrackingNumber(rng, rowIndex, trackingNumberCell, createDateCell); //method sets tracking number. NOTE: Function not included in code snippet but left here to demonstrate type of information used
return;
} //This is the end of the callSpreadsheetFunctions function
function getCellRngByCol(rng, col)//finds the cell associated with the active range and column
{
var aRng = SpreadsheetApp.getActiveSheet().getDataRange();//gets the spreadsheet data range
var hRng = aRng.offset(0, 0, 1, aRng.getNumColumns()).getValues();//finds the header row range by offsetting
var colIndex = hRng[0].indexOf(col);// declares the column index in the header row
return SpreadsheetApp.getActiveSheet().getRange(rng.getRow(), colIndex + 1); //returns the cell range at the position of the active row and column name passed into this method
}//This is the end of the getCellRngByCol function

getFormUrl() in google spreadsheet return url only of last linked form?

I have two forms linked with one spreadsheet, one sheet for each form.
When I run such script
function myFunction() {
var ss;
ss = SpreadsheetApp.getActive();
Logger.log(ss.getFormUrl());
}
I will see only one url (as life shows url for the second form). Is there any way to get urls for all linked forms?
I know that it's an old question, but I'll still add an answer to whom it may concern.
Generally speaking, Form is connected to Sheet, not to Spreadsheet itself. So, one way to get all of the Spreadsheet forms is to go through the Sheets of the Spreadsheets, and get their Form URL, like that:
function getFormsOfSpreadsheet(spreadsheetId) {
const ss = SpreadsheetApp.openById(spreadsheetId);
const sheets = ss.getSheets();
const formsUrls = [];
for (const sheet of sheets) {
const formUrl = sheet.getFormUrl();
// getFormUrl() returns null if no form connected
if (formUrl) {
formsUrls.push(formUrl);
}
}
return formsUrls;
}
Docs
The other possible way is to get all of the Forms in the Drive or in the Folder, iterate through them to get their destination and compare it to your Spreadsheet ID, like in this answer
This functionality is currently missing in the API. There is an existing request on the issue tracker for this here. Star that issue to follow it for the resolution.

Access to spreadsheet in deployed web app

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);
}