Inherited Apps Script onOpen function not working on cloned Google Sheet - google-apps-script

I am using the Drive API, authenticated via a service account, to copy a "master" Google Sheet that has an Apps Script attached. The Apps Script is simple and creates a custom menu in the Google Sheet. However, when the Google Sheet is copied from Drive and then accessed via the Google Sheets API, the attached apps script doesn't execute the onOpen function. I'm guessing there is some strange ownership/permissions issue that is preventing the onOpen from "activating" when it's copied with the service account and then given domain-wide "write" permissions.

On open events, simple and installable, are triggered only when a user opens a spreadsheet not by API actions.
Reference
https://developers.google.com/apps-script/guides/triggers/

Related

When creating copy of a spreadsheet that has bundled script using drive api, script behind doesn't work [duplicate]

I have a GSheet that is owned by a service account, and I would like to add an “on edit” trigger to receive real-time edit notifications.
So far I've tried/looked at:
Simply adding the onEdit function to the bound script, however Google doesn’t allow Apps Script to be run on the script (issue is discussed more here):
Google Apps Script: The script cannot be run because it is owned by a service account. Please copy or transfer the project to a valid account before running.
Drive API push notifications - but that doesn’t give the per cell detail required, and regardless neither does it give a response faster enough; can be up to 3 mins according this SO post.
Changing the ownership of the GSheet - however other requirements require the GSheet to remain belonging to the service account.
Using the Script API - but it isn’t allowed to create triggers, and anyway service accounts can’t use the Script API.
Any other ideas, or is the fact Google simply doesn’t allow Apps Script to run under the service account a show stopper?”
I believe your goal is as follows.
You have a Google Spreadsheet created by the service account.
You want to use the OnEdit trigger on this Spreadsheet.
Updated on January 25, 2023:
I confirmed that in the current stage, your goal can be directly achieved using the installable OnEdit trigger. The sample flow is as follows.
Flow
1. Create a new Google Spreadsheet by service account.
Please create a new Google Spreadsheet by the service account. And, please copy the Spreadsheet ID. In this case, the owner of the Spreadsheet is the service account.
And, please share this Spreadsheet with your Google account as a writer. This is an important point.
2. Create a standalone script by your account.
Please create a new standalone script with your account. In this case, the owner of the standalone script is your account.
And, please copy and paste the following script to the script editor. And, please set the Spreadsheet ID of Spreadsheet of the service account to spreadsheetId and save the script.
function installTrigger() {
const spreadsheetId = "###"; // Please set the spreadsheet ID of your Spreadsheet.
const ss = SpreadsheetApp.openById(spreadsheetId);
ScriptApp.newTrigger("installedOnEdit").forSpreadsheet(ss).onEdit().create();
}
// In this pattern, please set your script in this function.
function installedOnEdit(e) {
e.range.setValue(JSON.stringify(e));
}
3. Install the OnEdit trigger.
In order to install the OnEdit trigger, please run installTrigger(). By this, the OnEdit trigger is installed to the function installedOnEdit. By this, when a cell of the above Spreadsheet is edited, installedOnEdit is run.
4. Testing.
Please edit the cell in the Spreadsheet of the service account. By this, the script of your standalone script is run, and you can see the event object in the edited cell.
References:
Installable Triggers
Old post:
Issue and workaround:
In the current stage, unfortunately, when the owner of Google Spreadsheet is the service account. The Google Apps Script cannot be run. By this, even when onEdit function is put to the script editor, this script cannot be run. It seems that this is the current specification of the Google side. Unfortunately, in the current stage, your goal cannot be directly achieved. I think that this is the current answer to your question.
But, I thought that a workaround might be able to be proposed. Fortunately, the built-in functions of the Spreadsheet can be also used in the Spreadsheet created by the service account. I thought that this might be able to be used as a workaround.
When I saw your question, I remembered the following my posts.
Automatic Recalculation of Custom Function on Spreadsheet Part 1
Letting Users Running Google Apps Script on Google Spreadsheet without both Authorizing Scopes and Showing Script
I thought that when these posts are used, a workaround might be able to be proposed. In this answer, I would like to propose a workaround for achieving your goal. So, please think of this as the pseudo OnEdit trigger.
The flow of this workaround is as follows.
Prepare Web Apps.
This Web Apps is deployed at the Google Apps Script project created by an account that the script can be run. Please be careful about this.
When the pseudo OnEdit trigger is used in "Sheet1", put IMPORTXML to another sheet (For example, it's "Sheet2".). IMPORTXML requests to the Web Apps.
This Spreadsheet is the Spreadsheet created by the service account.
When the cells in "Sheet1" are edited, the formula in "Sheet2" is run.
By this flow, when the cells in "Sheet1" are edited, the Google Apps Script of Web Apps can be run. This is the pseudo OnEdit trigger as this workaround.
Usage:
1. Create Google Apps Script for Web Apps.
In order to use Web Apps, please create Google Apps Script project by the account that the script can be run. In this case, as a sample situation, please create Google Apps Script project by your account instead of the service account.
2. Sample script:
Please copy and paste the following script to the script editor of the created Google Apps Script project.
function doGet(e) {
// do something
// Please set the script you want to use.
return ContentService.createTextOutput(`<result>Edited at ${new Date().toISOString()}</result>`).setMimeType(ContentService.MimeType.XML);
}
3. Deploy Web Apps.
The detailed information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the importance of this workaround.
Please select "Anyone" for "Who has access".
In your situation, I thought that this setting might be suitable.
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
3. Testing.
Please prepare 2 sheets in Google Spreadsheet created by the service account. Those are "Sheet1" and "Sheet2".
Please put the following formula to "A1" of "Sheet2". In this case, please use your Web Apps URL. And, please confirm that the value is returned from the deployed Web Apps. If this cannot be done, please check the above flow again.
=IMPORTXML("https://script.google.com/macros/s/###/exec?values="&TEXTJOIN(",",TRUE,Sheet1!A:Z),"/result")
This formula is a sample formula. So, please modify the range and formula for your actual situation.
Please edit the cell on "Sheet1". When you use the above formula, please edit the cells in "A:Z". By this, the cell "A1" of "Sheet2" is refreshed, and by this refresh, Google Apps Script of Web Apps is run. This is the pseudo OnEdit trigger as this workaround.
Note:
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
In this workaround, I think that you can retrieve the edited cells by checking the difference between before and after the edited sheet. But, this is a simple script for explaining this workaround. So, if you use this workaround, please modify the script for your actual situation.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
IMPORTXML
Referenced my posts.
Automatic Recalculation of Custom Function on Spreadsheet Part 1
Letting Users Running Google Apps Script on Google Spreadsheet without both Authorizing Scopes and Showing Script

Creating an onEdit trigger on a sheet owned by a service account

I have a GSheet that is owned by a service account, and I would like to add an “on edit” trigger to receive real-time edit notifications.
So far I've tried/looked at:
Simply adding the onEdit function to the bound script, however Google doesn’t allow Apps Script to be run on the script (issue is discussed more here):
Google Apps Script: The script cannot be run because it is owned by a service account. Please copy or transfer the project to a valid account before running.
Drive API push notifications - but that doesn’t give the per cell detail required, and regardless neither does it give a response faster enough; can be up to 3 mins according this SO post.
Changing the ownership of the GSheet - however other requirements require the GSheet to remain belonging to the service account.
Using the Script API - but it isn’t allowed to create triggers, and anyway service accounts can’t use the Script API.
Any other ideas, or is the fact Google simply doesn’t allow Apps Script to run under the service account a show stopper?”
I believe your goal is as follows.
You have a Google Spreadsheet created by the service account.
You want to use the OnEdit trigger on this Spreadsheet.
Updated on January 25, 2023:
I confirmed that in the current stage, your goal can be directly achieved using the installable OnEdit trigger. The sample flow is as follows.
Flow
1. Create a new Google Spreadsheet by service account.
Please create a new Google Spreadsheet by the service account. And, please copy the Spreadsheet ID. In this case, the owner of the Spreadsheet is the service account.
And, please share this Spreadsheet with your Google account as a writer. This is an important point.
2. Create a standalone script by your account.
Please create a new standalone script with your account. In this case, the owner of the standalone script is your account.
And, please copy and paste the following script to the script editor. And, please set the Spreadsheet ID of Spreadsheet of the service account to spreadsheetId and save the script.
function installTrigger() {
const spreadsheetId = "###"; // Please set the spreadsheet ID of your Spreadsheet.
const ss = SpreadsheetApp.openById(spreadsheetId);
ScriptApp.newTrigger("installedOnEdit").forSpreadsheet(ss).onEdit().create();
}
// In this pattern, please set your script in this function.
function installedOnEdit(e) {
e.range.setValue(JSON.stringify(e));
}
3. Install the OnEdit trigger.
In order to install the OnEdit trigger, please run installTrigger(). By this, the OnEdit trigger is installed to the function installedOnEdit. By this, when a cell of the above Spreadsheet is edited, installedOnEdit is run.
4. Testing.
Please edit the cell in the Spreadsheet of the service account. By this, the script of your standalone script is run, and you can see the event object in the edited cell.
References:
Installable Triggers
Old post:
Issue and workaround:
In the current stage, unfortunately, when the owner of Google Spreadsheet is the service account. The Google Apps Script cannot be run. By this, even when onEdit function is put to the script editor, this script cannot be run. It seems that this is the current specification of the Google side. Unfortunately, in the current stage, your goal cannot be directly achieved. I think that this is the current answer to your question.
But, I thought that a workaround might be able to be proposed. Fortunately, the built-in functions of the Spreadsheet can be also used in the Spreadsheet created by the service account. I thought that this might be able to be used as a workaround.
When I saw your question, I remembered the following my posts.
Automatic Recalculation of Custom Function on Spreadsheet Part 1
Letting Users Running Google Apps Script on Google Spreadsheet without both Authorizing Scopes and Showing Script
I thought that when these posts are used, a workaround might be able to be proposed. In this answer, I would like to propose a workaround for achieving your goal. So, please think of this as the pseudo OnEdit trigger.
The flow of this workaround is as follows.
Prepare Web Apps.
This Web Apps is deployed at the Google Apps Script project created by an account that the script can be run. Please be careful about this.
When the pseudo OnEdit trigger is used in "Sheet1", put IMPORTXML to another sheet (For example, it's "Sheet2".). IMPORTXML requests to the Web Apps.
This Spreadsheet is the Spreadsheet created by the service account.
When the cells in "Sheet1" are edited, the formula in "Sheet2" is run.
By this flow, when the cells in "Sheet1" are edited, the Google Apps Script of Web Apps can be run. This is the pseudo OnEdit trigger as this workaround.
Usage:
1. Create Google Apps Script for Web Apps.
In order to use Web Apps, please create Google Apps Script project by the account that the script can be run. In this case, as a sample situation, please create Google Apps Script project by your account instead of the service account.
2. Sample script:
Please copy and paste the following script to the script editor of the created Google Apps Script project.
function doGet(e) {
// do something
// Please set the script you want to use.
return ContentService.createTextOutput(`<result>Edited at ${new Date().toISOString()}</result>`).setMimeType(ContentService.MimeType.XML);
}
3. Deploy Web Apps.
The detailed information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the importance of this workaround.
Please select "Anyone" for "Who has access".
In your situation, I thought that this setting might be suitable.
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
3. Testing.
Please prepare 2 sheets in Google Spreadsheet created by the service account. Those are "Sheet1" and "Sheet2".
Please put the following formula to "A1" of "Sheet2". In this case, please use your Web Apps URL. And, please confirm that the value is returned from the deployed Web Apps. If this cannot be done, please check the above flow again.
=IMPORTXML("https://script.google.com/macros/s/###/exec?values="&TEXTJOIN(",",TRUE,Sheet1!A:Z),"/result")
This formula is a sample formula. So, please modify the range and formula for your actual situation.
Please edit the cell on "Sheet1". When you use the above formula, please edit the cells in "A:Z". By this, the cell "A1" of "Sheet2" is refreshed, and by this refresh, Google Apps Script of Web Apps is run. This is the pseudo OnEdit trigger as this workaround.
Note:
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
In this workaround, I think that you can retrieve the edited cells by checking the difference between before and after the edited sheet. But, this is a simple script for explaining this workaround. So, if you use this workaround, please modify the script for your actual situation.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
IMPORTXML
Referenced my posts.
Automatic Recalculation of Custom Function on Spreadsheet Part 1
Letting Users Running Google Apps Script on Google Spreadsheet without both Authorizing Scopes and Showing Script

Standalone Script for Google Sheets

I am looking to locally use a Google Apps Script with one of my Google Sheets (Attached to a form) that will be accessible to all emails that have permission to access the sheet, but I don't plan on publishing this script to the public. Do I still need to publish in order to use this script, or will I be fine just adding an onSubmit function for my Google Form as a trigger for my script to run in my Google Sheet? And can I use installable triggers if I plan on operating this script as a standalone only attached to this one Google Sheet?
Do I still need to publish in order to use this script
No: If you own the spreadsheet (or have editor permissions on it), your script can remain private. A script does not need to be bound to a Spreadsheet except if it will use:
Spreadsheet UI (getUi(), custom menus, dialogs and sidebars)
"Active" items (getActiveSpreadsheet(), getActiveSheet(), getActiveRange(), getActiveCell())
...will I be fine just adding an onSubmit function for my Google form as a trigger for my script to run in my Google Sheet?
Yes: Your standalone script can have installable triggers associated with Google Apps items such as spreadsheets, documents and forms.
And can I use installable triggers if I plan on operating this script as a standalone only attached to this one Google Sheet?
Yes: There's nothing special about the 1:1 relationship; the same trigger could be installed for multiple spreadsheets.

onOpen trigger for view-only users

The built-in onOpen trigger in Google Apps Script only runs when a user with permission to edit opens a spreadsheet or document. I am developing a spreadsheet where only developers have edit privileges and all other users must have view-only privileges.
How do I create an onOpen trigger that fires when view-only users open my spreadsheet as well as those with edit privileges?
You can't, because it's not supported. Google Apps Script, embedded in either Google Sheets, Docs, Forms, and as an add-on, runs only for editors.
The alternative is to Publish as a webapp, so the viewers open that instead of the sheet directly, or open the webapp (on another tab, not inside the sheet) from a link in a sheet cell.
From the "Restrictions" section of trigger documentation on triggers - both simple and installable:
They do not run if a file is opened in read-only (view or comment) mode.**

Google Doc Script onEdit

I want to build a Google Doc Extension.
Google Spreadsheets has an onEdit trigger, Google Documents don't seem to: https://developers.google.com/apps-script/understanding_triggers
Is there a way for me to handle user input (typing) cleanly?
Not in Apps Script. If you write an app using the Drive API, you can use the Realtime API https://developers.google.com/drive/realtime/