Google Apps Script AddOn: onChange trigger programatically with misleading permission scope text - google-apps-script

By Google documentation, an onChange trigger for a Spreadsheet addon has to be created programmatically via ScriptApp.newTrigger(...)....
newtrigger requires the scope :
https://www.googleapis.com/auth/script.scriptapp
When the user is asked for granting permissions, the text presented is:
Allow this application to run when you are not present
I understand this function can create time based trigger, but for a simple onChange in a Spreadsheet, I would understand if the user declines it due to that text.
Is there a way to have an onChange trigger without this scope for changes (not onEdit) in spreadsheets?

Answer:
The only way to create a trigger on behalf of a user programmatically is using ScriptApp.newTrigger(), and this method requires the https://www.googleapis.com/auth/script.scriptapp scope. No other scope will allow this to run.
You can however set up an onChange() trigger for the Spreadsheet, which will run regardless of who makes the change to the sheet.
More Information:
When you set up a trigger, you are allowing an app/script/function to run, on behalf of you, without you manually running the function. Asking a user to create a trigger will require them to authorise that they consent to the script doing something on their behalf.
You can however, set up the trigger yourself. As long as the function that will run on the trigger doesn't violate any of the installable trigger restrictions then this will run on the Sheet as long as it has been authorised to run by you.
Remember though, that if you set up the trigger yourself, the script will always execute as you, not as the user. So if the function, for example, sends an email, then the email will be sent on behalf of you - as you were the one that authorised the script.
Setting up an Installable Trigger manually:
If setting up the trigger to run as you is an acceptable solution, you can do so by following these steps:
Save the script with the save icon, press the run button (►), and confirm the authentication of running the script.
From here, following the Edit > Current project's triggers menu item, you will have a new page open in the G Suite Developer Hub. Click the + Add Trigger button in the bottom right and set up the trigger settings as follows:
Choose which function to run: <your-function-name>
Choose which deployment should run: Head
Select event source: From Spreadsheet
Select event type: On change
And press save.
This will now run on all changes made to the sheet - regardless of who made the change. Just remember that as far as execution goes, you're the script runner.
References:
Class ScriptApp | Apps Script - .newTrigger(functionName) method
Installable Triggers | Apps Script - Restrictions
Event Objects | Apps Script - Google Sheets events: Change

Related

onSpecialEdit() for Google App Script on a sheet with many users executes for all, each time

This can't be right. What am I missing?
I have a Google Sheet with scripts. The scripts are using certain ui functions that require authentication and special permissions coming from an onEdit() event. Therefore, I had to create onSpecialEdit() and the custom trigger to run that function with event source of the spreadsheet and event type onEdit(). This all works fine. Script does what it should.
From what I've seen, each person who needs to have this ability and access the special edit will need their own trigger. That is created for them automatically. Triggers all work for each person.
Now, what I see is that when I do an action that triggers onEdit(), it appears to run the onSpecialEdit() 20 times...once for each person who has that trigger....I'm assuming.
This can't be right. What am I missing? Is there another way to create triggers where they are only executed by the person triggering? Is this approach wrong?
Issue:
You want to have an onEdit trigger that has access to services that require authorization.
These services should have access private of the user who has caused the trigger to run by editing the spreadsheet.
Since the services require authorization, it cannot be a simple but an installed trigger (Restrictions).
Since installed triggers act on behalf of the user who installed them, they can only access data of the user who installed them.
Therefore, each user whose data should be accessed needs to install the trigger, so multiple triggers should be installed.
A trigger runs whenever any user edits the spreadsheet, it doesn't have to be the user who installed the trigger. Because of this, all of the installed onEdit triggers will run whenever any user edits the spreadsheet.
Solution:
Use Session.getActiveUser() and Session.getEffectiveUser() to check whether the user editing the spreadsheet (getActiveUser) is the same as the one who installed the spreadsheet (getEffectiveUser). If it's not, stop the function execution. This way, only one of the multiple onEdit triggers will make the corresponding actions.
Code sample:
function onEditTrigger(e) {
const active = Session.getActiveUser().getEmail();
const effective = Session.getEffectiveUser().getEmail();
if (active === effective) {
// TRIGGER ACTIONS
}
}

Why Apps Script execution failed only when it is triggered by onEdit event on Google Spreadsheet?

I am working on Google Apps Script. I have apps script that I have wrote that is supposed to be executed by onEdit event on specific sheet.
onEdit.gs
function onEdit(e) {
var syokuninIchiran = new SyokuninIchiran();
syokuninIchiran.syncTable();
}
When the above is executed by edit on the sheet, this error occurs:
You do not have permission to call openById. Required privilege: https://www.googleapis.com/auth/spreadsheets
But, it works with no problem when the script is triggered from Google Apps Script development window. I call the same code by making a test function as follows:
function test(){
var syokuninIchiran = new SyokuninIchiran();
syokuninIchiran.syncTable();
}
It might be problem for this issue? The spreadsheet's owner is not mine. My account has right to edit as the image below.
Try1:
I tried to add the scope manually into the manifest file(appsscript.json). And I approved the authorization. But, nothing changed. It still have the same error message.
{
"oauthScopes": [
"https://www.googleapis.com/auth/sqlservice",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.external_request"
]
}
Answer:
Simple Triggers can not open files aside from the file it is bound to. For this, you need to use an installable trigger.
More Information:
As per the documentation on Simple Trigger restrictions:
Because simple triggers fire automatically, without asking the user for authorization, they are subject to several restrictions:
They can modify the file they are bound to, but cannot access other files because that would require authorization.
So an installable trigger must be used.
Setting up the Installable Trigger:
Follow the Edit > Current project's triggers menu item, which will open a new page in the G Suite Developer Hub. Click the + Add Trigger button in the bottom right and set up the trigger settings as follows:
Choose which function to run: onEdit
Choose which deployment should run: Head
Select event source: From Spreadsheet
Select type of time based trigger: On Edit
And press save. This will increase the range of functionality of your edit triggers.
References:
Simple Triggers | Apps Script | Google Developers
Installable Triggers | Apps Script | Google Developers

How to run a Google Apps Script in a read-only spreadsheet?

I'm new to the world of Google Apps Script, and I found myself faced with a problem. I've created a script that runs as an onOpen() trigger, but if the spreadsheet file is read-only, this script does not run. I read that, being read-only, it is not able to run it. Is there a way to remedy this problem?
I gave the permissions in edit, blocking all the cells from scripts. I wish people can not see the script, as this allows them to re-run the script and take "power" on the sheet. If I create an API can I keep the sheet read-only?
Any suggestions?
All scripts must run under someone's authority. Under whose authority, a script is run determines whose data is accessible to the script and whether such authority can run the script.
Authorization Concepts:
Scripts which are run from the script editor run under the authorization of user at the keyboard¹. Custom functions runs anonymously. Installable triggers runs under the user created the trigger. WebApps run as per the options selected during deployment.
Simple triggers fire automatically and anonymously under these restrictions²:
The script must be bound to a Google Sheets, Slides, Docs, or Forms file, or else be an add-on that extends one of those applications.They do not run if a file is opened in read-only (view or comment) mode.They cannot access services that require authorization.
Installable triggers must be set up and run under the user who set up the trigger³.
They do not run if a file is opened in read-only (view or comment) mode.Installable triggers always run under the account of the person who created them.A given account cannot see triggers installed from a second account, even though the first account can still activate those triggers.
You can restrict access to spreadsheet, sheets or ranges⁴,⁵.
Deductions:
Mr.A(Owner) has a spreadsheet. The spreadsheet has two sheets 1.Main Sheet and 2. Secret Sheet(Sheet is protected and hidden to be edited only by Mr.A). It also has the following scripts: 1. A simple trigger script(onEdit) to set timestamp as a note in every cell that is edited. 2. A installed trigger (AonEdit()) to send email from MrA's email on every edit. 3. A function(summary()) to create a summary of Main Sheet and send email from user's account to himself(to run manually from script editor). 4. A function to protect the secret sheet(protect()) 5. A simple onOpen() trigger logging Someone opened your sheet. And He gives edit access to Mr.B(a editor). What can Mr.B do?
Mr.B edits cell A1 in Main Sheet:
Simple trigger onEdit is fired anonymously and a timestamp is set on A1 as a note.
Installable trigger AonEdit is fired under Mr.A's authority and a email is sent from Mr.A's gmail.This is not known to Mr.B,though he can see the script itself.
Mr.B,being a cunning person as he is, unhides the secret sheet successfully and attempts to modify it:
Mr.B could not modify the secret sheet,even though he can fully unhide/view it
Mr.B finds the script editor and tries to run the function summary(). This function needs gmail permission. Mr.B is greeted with authorization[¹] for Mr.B's gmail account,so that the function may run. Mr.B grants authorization and the summary is sent from Mr.B's gmail account.
Mr.B cunningly modifies the protect() function to grant himself edit access to the secret sheet. The user at the keyboard is Mr.B. Mr.B's authority is not enough. He's greeted with the following error:
You are trying to edit/remove a protected cell or object. Please contact the spreadsheet owner to remove protection if you need to edit.
Mr.A (Owner) knows about this unauthorized access attempt by Mr.B, He restricts edit access to All sheets. Now, Mr.B even though has permission to edit the spreadsheet, He cannot edit any sheet in the spreadsheet. Whenever he opens the sheet, the simple onOpen() is triggered logging Someone opened your sheet. Mr.B however can run the function summary() even now(If he has the script editor link) to get the summary of the sheet.
Solutions:
as this allows them to re-run the script and take "power" on the sheet.
A user with write permission can enter the script and modify the permissions.
As explained above, That wont be possible. The sheet/range edit permission is maintained even at the script level. However, A potential loop-hole is the AonEdit() function. If Mr.B were to know that Mr.A had set up a installable trigger for AonEdit() function, He can modify the AonEdit() function to say protection.remove(),which will run under Mr.A's authority(Installable triggers run under the person who created it) and thus the protection is removed.
I wish people can not see the script.
You can use a standalone script⁶. You can also use installable triggers[³] with standalone scripts subject to the restrictions of those triggers(such as It'll only run under your authority). Since the script is not bound to the spreadsheet, Editors on the spreadsheet do not have edit/view permission on the script. Alternatively, You can publish a addon⁷/webapp⁸.

onEdit() function does not triggered when change was made by automatic script

my onEdit() function calling to other function when there is a change in the sheet.
if the user makes the change all works fine.
but if the change was made by google-form (the form fill some cells with the answers it gets) onEdit() does not trigger.
do I miss something?
Yes, you forgot to read the documentation for the simple triggers:
onOpen(e) runs when a user opens a spreadsheet, document, or form that he or she has permission to edit.
onEdit(e) runs when a user changes a value in a spreadsheet.
onInstall(e) runs when a user installs an add-on.
doGet(e) runs when a user visits a web app or a program sends an HTTP GET request to a web app.
doPost(e) runs when a program sends an HTTP POST request to a web app.
and installed triggers:
Even though installable triggers offer more flexibility than simple triggers, they are still subject to several restrictions:
They do not run if a file is opened in read-only (view or comment) mode.
Script executions and API requests do not cause triggers to run. For example, calling FormResponse.submit() to submit a new form response does not cause the form's submit trigger to run.
Installable triggers always run under the account of the person who created them. For example, if you create an installable open trigger, it will run when your colleague opens the document (if your colleague has edit access), but it will run as your account. This means that if you create a trigger to send an email when a document is opened, the email will always be sent from your account, not necessarily the account that opened the document. However, you could create an installable trigger for each account, which would result in one email sent from each account.
A given account cannot see triggers installed from a second account, even though the first account can still activate those triggers.
Consider what would happen if your onEdit(e) was activated by programmatic changes, such as if your onEdit function alters the spreadsheet values...
In your situation, where you want form submission to activate your on edit function, You will need to install a form submission trigger (there is no simple trigger for form submissions).
An example function to receive your form submission trigger:
function giveMeAnInstalledFormSubmitTrigger(formSubmitEventObject) {
if(!formSubmitEventObject) throw new Error("You called this from the Script Editor");
var newEventObject = /* do something with the formSubmitEventObject */;
// Call the on edit function explicitly
onEdit(newEventObject);
}
You can read more about the event objects that triggered functions receive in the Apps Script documentation: https://developers.google.com/apps-script/guides/triggers/events
onEdit() will not be triggered by another script editing a sheet or by a form submission.
You can use onFormSubmit(e) instead depending on what your function does it would be a good idea to use the e parameter of the trigger.
https://developers.google.com/apps-script/guides/triggers/events#form-submit
What i have done is that i created an extra sheet with a cell that is the same where the primarecell is(the cell that changes). Leave it for now. I open the scriptapp and write down the code(the code that should happen if the cell changes). Then i wrte down "if" and take the two value i both cells and make an trigger that goes on every minute. With that said, if The cells are the same the "if" is looping on "true". But if something change on the primary cell, it will be "false" and you use "else". Under"else" you should have what ever function you want when soemthing change in that cell. + You must have a funtion where you insert the new value form the primarycell to the "check cell" if u want this too function as loop. Otherwise the "if" code will loop "false" every minute becuse the both cells are not the same.
My english and explaining is not that good:(

How to run a spreadsheet bound script as the creator (not viewer/editor)

I made some script with my spreadsheet, which uses some Trigger and SendEmail functionalities. I have menu items in spreadsheet to control these triggers and sendemails.
Now when I share this spreadsheet with someone other, when he tried to access the trigger or sendemail functions from menu, the script asks for authorization as that user. IF authorized it will function as that user. e.g. send email as that user or make a new trigger as that user. This makes things double and useless
I want that any user accessing the script can use those functionalities, but won't require authorization to run as that user. The script should run as the creator of the sheet, so that no double triggering occurs. How should I do it?
You could create a web-app. Web-apps have the ability to run either as the user himself, or as the developer/publisher.
Under Execute the app as, select whose authorization the app should
run with: your account (the developer's) or the account of the user
who visits the app (see permissions).
https://developers.google.com/apps-script/guides/web
This web-app wouldn't have a sheet linked to it, but if you only use 1 sheet you can have the web-app access the sheet through the ID of the sheet. You could use your existing menu-items to trigger the webapp
Would this be a possible solution for your problem?