Include Parent Folder Name in Spreadsheet onOpen - google-apps-script

I would like a spreadsheet to automatically include the name of its parent folder in cell A1 of the active sheet onOpen. I have a script that successfully achieves this if I run it manually, yet I am not able to accomplish this onOpen. As recommended below, I have tried using an installable onOpen trigger but that is not a solution as I want to make copies of the spreadsheet and have the folder name included onOpen. The trigger will not copy with the spreadsheet so I am back to square one. The issue is with permissions, I believe? Any workaround?

You can use something like this to create an installable onOpen trigger if one is not already in the project.
function createOpenTrigger(funcname) {
if(funcname) {
if(!isTrigger(funcname)) {
ScriptApp.newTrigger(functionName).forSpreadsheet(SpreadsheetApp.getActive()).onOpen().create();
}
}
}
function isTrigger(funcName){
var r=false;
if(funcName){
var allTriggers=ScriptApp.getProjectTriggers();
for(var i=0;i<allTriggers.length;i++){
if(funcName==allTriggers[i].getHandlerFunction()){
r=true;
break;
}
}
}
return r;
}
Of course, the user will have to approve it first.

As recommended below, I have tried using an installable onOpen trigger but that is not a solution as I want to make copies of the spreadsheet and have the folder name included onOpen. The trigger will not copy with the spreadsheet so I am back to square one. The issue is with permissions, I believe? Any workaround?
No, the issue isn't with permissions, the issue is related to other concepts. I'll try to make a summary including the minimal of them that I think are required for this question to help you learn the basic terms so you could scan this site and others to find the stuff you will need or that will help you to make more specific questions:
There are two kinds of script projects, bounded and standalone.
Scripts that will make certain actions should be authorized before they execute. Please keep reading.
There are two kinds of triggers, simple an installable
Installable triggers could be created manually or by a script. Creating a a trigger by a script requires authorization.
Installable triggers only could execute functions that the user that is creating the trigger either manually or through a script had been authorized. If you create an installable trigger but later makes changes to the script, perhaps this will make that your script should be authorized again.
Installable triggers could be created for spreadsheets by using bounded and standalone project scripts. This kind of triggers require authorization to access non public spreadsheets
To get the parent folder of a spreadsheet your script should use the Drive Service or the Drive Advanced Service. This requires authorization.

Related

How do you run function in Google Workspace Addon when Google Spreadsheet is opened?

I have some trouble understanding this. In Editor Addons it is easy to run a function for example to create custom menu items by using onOpen trigger. But I cannot figure out how I would achieve the same result with Google Workspace addons.
There are no simple triggers available so I am left with 2 options which do not satisfy my needs:
I use a manifest homepageTrigger
"sheets": {
"homepageTrigger": {
"runFunction": "initializeMenu"
}
}
The problem with this is that this function doesn't run until you click the Addon icon in the panel on the right.
I create an installable trigger
ScriptApp.newTrigger("initializeMenu")
.forSpreadsheet("XYZ")
.onOpen()
.create()
The problem with this one is that I need to provide a specific Spreadsheet ID. But I want my addon to work on EVERY Google Spreadsheet after it is installed.
Am I missing something here?
After checking out the documentation, I believe this cannot be done the way you want with a Google Workspace Add-on.
As you mentioned in your post, you can use onOpen() triggers with editor add-ons to add the menu on all spreadsheets, however, Workspace add-ons have a different set of triggers. Among the list, the most relevant to us would be these:
homepageTriggers are a global trigger type that runs when the user clicks the add-on icon.
onFileScopeGranted triggers are specific to the editor apps (which includes Sheets), and they fire when the add-on gains access to the current file's scope, but it also requires user input to run.
Those are the only triggers available in Sheets if you check out the manifest file definition. Essentially the add-on cannot act by itself in the background with just manifest triggers, it needs to use installable triggers to do this, and to generate installable triggers you need the user to at least open the card once.
It is possible to create installable triggers, but the problem is that you still need one of the regular add-on triggers to fire to create these installable triggers. For example, you can create an onOpen trigger when the user opens the add-on card within a Sheet by defining it within the homepageTrigger function. Something like dobleunary's answer works for this:
Manifest:
"sheets": {
"homepageTrigger": {
"runFunction": "onEditorsHomepage"
},
Sheets.gs
function onEditorsHomepage() {
//probably some logic to check for duplicates
ScriptApp.newTrigger("initializeMenu")
.forSpreadsheet(SpreadsheetApp.getActive())
.onOpen()
.create()
}
In this situation the user clicks the add-on menu, the trigger is created for the sheet, and on subsequent runs the trigger creates the menu without needing to open the add-on again. This works and I was able to create triggers for over 30 different sheets so the 20 triggers/user/script quota may apply in a different way or maybe it counts each sheet as a different script file. This may warrant some testing to figure out the real limit since the docs do not specify.
I would recommend to just build an editor add-on instead and opt for simple triggers. You probably don't want to burden users with triggers for each of their files and even Google's comparison between add-on types emphasizes editor add-ons as the main tools to create custom menus. If you really must build a Google Workspace add-on, then you may have to just live with needing to have the users use the add-on card rather than a menu at the top.
Sources:
Add-on types
Building editor add-ons
Apps Script quotas
Google Workspace add-ons triggers
Editor add-on triggers
Overall add-on manifest
General and Editors manifest definitions.
I need to provide a specific Spreadsheet ID
Instead of using a hard-coded spreadsheet ID, use the ID of the active spreadsheet, or a direct reference to the active spreadsheet object, like this:
function installOnOpenTrigger() {
const functionNameToTrigger = 'initializeMenu';
const ss = SpreadsheetApp.getActive();
if (ss) {
try {
ScriptApp.newTrigger(functionNameToTrigger)
.forSpreadsheet(ss)
.onOpen()
.create();
} catch (error) {
;
}
}
}
SpreadsheetApp.getActive() will return the current spreadsheet whenever there is an active spreadsheet.
From the documentation:
Each add-on can only have one trigger of each type, per user, per document. For instance, in a given spreadsheet, a given user can only have one edit trigger, although the user could also have a form-submit trigger or a time-driven trigger in the same spreadsheet. A different user with access to the same spreadsheet could have their own separate set of triggers.
Add-ons can only create triggers for the file in which the add-on is used. That is, an add-on that is used in Google Doc A cannot create a trigger to monitor when Google Doc B is opened.

Google Apps Script: How are edit events from a bounded script in one sheet treated on another?

I'll try to be as specific as possible. This questions relates to Google Apps Script, bounded to two sheets.
I have a function that runs on edit through an installable (not simple) trigger. (Installable because it needs to call e.g. GmailApp). Call it Sheet A.
On another sheet, which we'll call Sheet B, is a script which causes an edit to be made in a cell on Sheet A.
When an edit is made manually on Sheet A, the installable on-edit script runs fine.
When the edit is made via the script on Sheet B, the installable on-edit script does not run. In other words, there doesn't seem to be any event associated with edits made by a script.
I am wondering if there is any way to trigger the on-edit script on Sheet A to run when changes are being made through another, external, bounded script.
I've looked through GAS documentation and searched as best I can here, but I cannot find an answer. I've tried to use a simple trigger, but it also does not seem to "pick up" the edit event when performed by another script.
Any help would be greatly appreciated.
Triggers only run on "human" changes. You need to rethink your flow.
If you have a function funA() changing something programmatically in a cell and you need to trigger another function funB(), just call funB() directly from funA() without counting on a trigger.
onEdit is triggered only if user updates a cell, not when the cell is updated programmatically.
You should expose onEdit from sheet A (or extract the method that actually does send the emails) and publish it as API executable
Then you can call that method from sheet B, after the edit is made programmatically.

How to copy a Google sheet and keep it's project triggers and scripts?

I currently have a Google Sheet that I'm using as a master template. That is, I'm making a copy of this template for every request. I want to add a Google App Script (which onEdit POSTs to my server when the sheeting is completed) to my master template that will be duplicated and run for every copy of this template.
I've tried doing this from an admin account, however, the scripts don't seem to 'stick' with any of the templates. Is this possible?
Installable triggers can be attached to any spreadsheet (respecting sharing permissions) from any project. You can add a new trigger to your master sheets project for each copy that is made. In the second example here they suggest using SpreadsheetApp.openById() you could also use SpreadsheetApp.openByURL() or the Spreadsheet returned by Spreadsheet#copy() depending on how you are duplicating your spreadsheet.
Personally, what I do in 2022 is including something like this on the beginning:
function scriptSetup() {
createOnEditFunction();
}
function createOnEditFunction() {
const ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('onEdit')
.forSpreadsheet(ss)
.onOpen()
.create();
}
function onEdit(e) {
//your stuff here
}
This way, the scriptSetup() is located on the first line so when copying a spreadsheet I would ask them within Sheets to click on Extensions > Apps Script > Run.
As per Jared Pinkham's link referencing Developer documentation, there are a few things Apps Script can use as triggers other than onEdit.
There are several installable triggers for Google Workspace
applications:
An installable open trigger runs when a user opens a spreadsheet,
document, or form that they have permission to edit.
An installable edit trigger runs when a user modifies a value in a
spreadsheet.
An installable change trigger runs when a user modifies the structure
of a spreadsheet itself—for example, by adding a new sheet or removing
a column.
An installable form submit trigger runs when a user responds to a
form. There are two versions of the form-submit trigger, one for
Google Forms itself and one for Sheets if the form submits to a
spreadsheet.
An installable calendar event trigger runs when a user's calendar
events are updated—created, edited, or deleted.
You can refer to the following link for spreadsheet triggers.

Can a standalone script create a trigger for another script which is bound to a spreadsheet?

I am trying to create an installable onEdit trigger for a spreadsheet bound script. I would like to do this programmatically with a separate, standalone script. It looks like this should be possible according to documentation:
Note that, unlike for a simple onOpen() trigger, the script for the installable trigger does not need to be bound to the spreadsheet. To create this trigger from a standalone script, simply replace SpreadsheetApp.getActive() with a call to SpreadsheetApp.openById(id).
https://developers.google.com/apps-script/guides/triggers/installable#managing_triggers_programmatically
However, when I run the code below, the trigger is added to the standalone script project instead of the target, spreadsheet-bound script.
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.openById('1vcAgQ6vPZiILFX0fB_jojyrSdGKr7goD_iCQcFsImEM');
ScriptApp.newTrigger('update')
.forSpreadsheet(ss)
.onEdit()
.create();
}
What am I missing?
Well, this may be possible, but not like that. I think you misunderstood the documentation a bit. Putting in other words, what it says is: to create a installable trigger, a script does not need to be bounded to the target spreadsheet. But the trigger is for the running script itself, as always. There's no installing a trigger for another script.
Scripts can only set triggers for themselves, and there's no API to set a trigger for another script.
You could have the bounded script published as a web-app, then the remote standalone script could call its URL, basically "telling" the bounded script that it's time to set its installable trigger.

Installable onEdit trigger in google spreadsheet script when copying a template spreadsheet script

I have a script(A) that copies a template spreadsheet. This template spreadsheet has a script(B) in it. The template spreadsheet and script are successfully programmatically copied and shared with others. The template script needs to use an onEdit() trigger to modify a third spreadsheet (Edits in the copy of the template spreadsheet are going to be synced to the third spreadsheet). A simple trigger does not have permission to access the third spreadsheet. I have tried using installable triggers, but have had no success in getting an installable onEdit trigger to work in the copied spreadsheet.
What I want to achieve is to have an onEdit trigger in the copied spreadsheet that does not require separate authorization to write to a third spreadsheet.
All spreadsheets are owned by a single user.
I can create an installable trigger in script B using the following code in script A.
createSpreadsheetEditTrigger(id_of_shared_spreadsheet)
...
...
function createSpreadsheetEditTrigger(idss) {
var ss = SpreadsheetApp.openById(idss);
ScriptApp.newTrigger('myOnEdit')
.forSpreadsheet(ss)
.onEdit()
.create();
}
The 'myOnEdit' function is in script B and has in it the code that syncs to the third spreadsheet. This is not triggered when the copied spreadsheet is edited.
If I create the installable trigger in script B by calling it from a simple onOpen() function it fails because of permissions.
Following the advice here: Execution failed: You do not have permission to call getProjectTriggers I can create the installable trigger and a menu item to call it, plus a msgbox that prompts the user to click on install and authorize the trigger.
Is it possible to programmatically install an onEdit trigger when copying spreadsheets that has permissions to edit a third spreadsheet without the user having to manually authorize the trigger?
Many thanks in advance
Trevor Storr
I just went through this same sort of deal, I am pretty confident there is not an "easy" way to make this happen without Authorization.
Although you can assign your "createSpreadsheetEditTrigger" script to a button that just installs the trigger and authorizes all at the same time, 3 clicks and 5 seconds, then it will work just as you intended.