installing a trigger by script keeps adding triggers to template sheet - google-apps-script

I have ran into a problem with a template I have been working on. I have a template file in a folder on a shared drive that has a script containing a create function and close function, triggered by this installed on edit ran from a drop down.
function onMyEdit(e) {
if(sh.getName()=="Sheet2" && e.range.columnStart==6 && e.range.rowStart==3 && e.value) {
switch(e.value) {
case 'Create':
Create();
break;
case 'Close':
Close();
}
e.range.setValue('');
}
}
The create function created a renamed copy of the template in a folder on the shared drive, adds a calendar event, and writes certain data to another spreadsheet in the drive acting as a data base. I manually installed a trigger for the onMyEdit function in the template file, but when I create the copy the trigger does not work so the close function will not run from the newly created sheet. I tried programming the create function to install a trigger on the new sheet. Useing this
var WOssID = WOss.getId(); //get the ID of the newly created work order spreadsheet
Logger.log(WOssID)
ScriptApp.newTrigger('onMyEdit')
.forSpreadsheet(WOssID)
.onEdit()
.create()
where WOss is the spreadsheet that I just created by copying the template, and I check the logger and the ID does match the new sheet. This does fix it so I can run the close function from the newly created sheet, but it seems to add a trigger to the template sheet not the new sheet, and it keeps adding a trigger every time I run the create function from the template file until it limits out at 20 and then it all breaks. I must be misunderstanding something because I don't understand why it is adding triggers to the template sheet when I have the ID for the newly created sheet being called. Also hoping to get this all to run on an Ipad so believe that means I can't use added on menus, onopen calls, or clickable buttons. Any help is appreciated.
UPDATE
So I now have a simple onEdit trigger that creates a installed onMyEdit trigger to my work order sheet on first edit and doesn't create multiple triggers in my template file. However I need to figure out a way to authorize it in the newly created sheet, without going into the script editor. If I open the newly created sheet and make an edit the new installed trigger shows up in the project triggers, but it won't run until I try to run it from the script editor first at which time it asks for authorization. Is there a way to tie the authorization to a checkbox that when clicked makes the popup appear to authorize the installed trigger so it can run?
function onEdit(){
var WOss = SpreadsheetApp.getActiveSpreadsheet();
var WOssID = WOss.getId();
var allTriggers =
ScriptApp.getUserTriggers(WOss)//ScriptApp.getProjectTriggers();
Logger.log(allTriggers.length)
if(allTriggers.length == '0' ){
ScriptApp.newTrigger('onMyEdit').forSpreadsheet(WOssID).onEdit().create()
}
else(allTriggers.length != '0')
{}
}

Here's a function I use to create triggers:
function createOnFormSubmitTriggerForSpreadsheet() {
const ssid=SpreadsheetApp.getActive().getId();
const resp=SpreadsheetApp.getUi().prompt("Create On Form Submit Trigger", "Enter Function Name", SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
if(resp.getSelectedButton()==SpreadsheetApp.getUi().Button.OK) {
let funcname=resp.getResponseText();
The next line prevents me from creating more than one trigger for the same function
if(!isTrigger(funcname)) {//I use this to keep from creating more than one trigger
I recommend that you do the same
ScriptApp.newTrigger(funcname).forSpreadsheet(ssid).onFormSubmit().create();
}
}
}
function isTrigger(funcName){
var r=false;
if(funcName){
var allTriggers=ScriptApp.getProjectTriggers();
for (let i=0;i<allTriggers.length;i++){
if(funcName==allTriggers[i].getHandlerFunction()){
r=true;
break;
}
}
}
return r;
}
And don't mean to be cruel but you code is a mess and you dont' need to describe your whole project we just want to see the minimum code that generates the problem. You said that you didn't know what's important and what's not. Well when you spend the time to create a minimum reproducible example you will have learned a lot more about your code and I often find that I end up solving the problem on my own.

Related

Have an onEdit() function run whenever a cell value is changed in Google Sheets

I have a Sheet that has the following:
ID ROW STATUS ......
3588053 4 NEW
The Data for ID and ROW are coming from an External Spread Sheet called KDCAlerts
The STATUS filled is changed with an onEdit command (or in this case onMyEdit) that executes when there are changes on KDCLog
The ID and ROW changes each time the External spreadsheet changes (ex: adding / deleting rows, etc.)
What I need to do is to fix this so that when ID or ROW changes, the onMyEdit function is ran.
How can I do this? Below is the code currently being used now.
Any help, hints or advice would be greatly appreciated.
TIA
function creatTrigger() {
if(ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() == "onMyEdit").length == 0) {
ScriptApp.newTrigger("onMyEdit").forSpreadsheet(SpreadsheetApp.getActive()).onEdit().create();
}
}
function onMyEdit(e) {
var sh = e.range.getSheet();
if (sh.getName() == "KDCLog" ) {
var extSS = SpreadsheetApp.openById("1b5qiNxxxxxxxxxRuLf-8dTBgRU9cHLBbd2A");
var extSH = extSS.getSheetByName("KDCAlerts");
} else { return; }
....
....
UPDATE:
#doubleunary - thanks for the response.
It is unclear how the values in the ID and ROW columns get written to the spreadsheet,
There are 2 Files: KDCLog and KDCAlerts.
KDCLog!ID is populated as follows:
=IF ( ISERROR( INDEX(SORTN(FILTER({KDCAlerts!E:E,KDCAlerts!H:H, KDCAlerts!A:A}, KDCAlerts!D:D=E7),1,,2,FALSE),,3) ), -1, INDEX(SORTN(FILTER({KDCAlerts!E:E,KDCAlerts!H:H, KDCAlerts!A:A}, KDCAlerts!D:D=E7),1,,2,FALSE),,3) )
So, it is "pulling" from KDCAlerts using a function (being executed in the KDCLog worksheet).
Whenever the KDCAlerts change, the Value in KDCLog!ID also changes (without manual intervention - the KDCAlerts changes with the adding of rows).
If you are using another script to write to the spreadsheet, it is
likely that no events get sent.
For the STATUS column, it is populated with an onEdit funciton (seen above)
trigger events that can be monitored with an installable on change
trigger.
Is there a sample on how this could work
You cannot use on edit trigger here, because it fires only when the spreadsheet is manually edited by a user, regardless of whether you are using a simple trigger or an installable trigger.
It is unclear how the values in the ID and ROW columns get written to the spreadsheet, which is what actually determines whether you can catch those updates. If you are using another script to write to the spreadsheet, it is likely that no events get sent.
If you are using another integration tool, it may trigger events that can be monitored with an installable on change trigger.

Automatically execute Google App Script on Creation of Google Doc or shortly thereafter

We previously had success (from help we've received here!) adding an onOpen menu item that would trigger text formatting. The back story here is that we have google doc merge templates that each have this bound app script. Once the template is generated using data from another source, the user can click the menu item to then format the text. However, as we've grown this has become cumbersome with the number of documents that are generated daily. It requires a user to authorize the script and click the buttons to ultimately format the merged text. This also causes a stopping point in business process because the user running the script is not the ultimate user of the document.
I've pasted the working code below, but I was hopeful that someone might have some ideas on how to change this script so that it ran automatically, without user intervention, after the document is created. The document creation and the data merge happen simultaneously or nearly simultaneously.
function onOpen() {
DocumentApp.getUi().createMenu('Butler')
.addItem('Format Headings', 'FormatHeadings')
.addToUi();
}
function FormatHeadings() {
handle_tags(['<req>', '</req>'], "Roboto", 14, "Bold", "#4a5356");
handle_tags(['<cit>', '</cit>'], "Roboto", 12, "Bold", "#4a5356");
handle_tags(['<con>', '</con>'], "Roboto", 12, "Bold", "#B38F00");
}
function handle_tags(tags, family, size, style, color) {
var body = DocumentApp.getActiveDocument().getBody();
var start_tag = tags[0];
var end_tag = tags[1];
var found = body.findText(start_tag);
while (found) {
var elem = found.getElement();
var start = found.getEndOffsetInclusive();
var end = body.findText(end_tag, found).getStartOffset()-1;
elem.setFontFamily(start, end, family);
elem.setFontSize(start, end, size);
elem.setForegroundColor(start, end, color);
switch (style.toLowerCase()) {
case 'bold': elem.setBold(start, end, true); break;
case 'italic': elem.setItalic(start, end, true); break;
case 'underline': elem.setUnderline(start, end, true); break;
}
found = body.findText(start_tag, found);
}
body.replaceText(start_tag, '');
body.replaceText(end_tag, '');
}
Thank you!
Just to set the right expectations, you can make Apps Script code run by triggering an event or manually running a function. We have those 2 options.
Now, you can't automatically run after the creation of a file because a file creation is not a supported event. You could however:
Create a form that requests the fileID of the new file (the file must be ready to be formatted), use the onSubmit event so when the form is submitted an Apps Script function is executed and the file is created. The onSubmit event is an installable trigger, for more details check the documentation at https://developers.google.com/apps-script/guides/triggers/events#google_forms_events.
You are using DocumentApp.getActiveDocument() you can replace that and use DocumentApp.openById() with the ID that was submitted in the form. This will automatically apply that same script to the file ID you submit in the form.

Writing into another Spreadsheet with SetValue fails without errors/exceptions

I'm trying to update a spreadsheet from a script running on another spreadsheet.
Nothing seems to have any effect on the table (SetValue(), SetBackgroundRGB(), etc.).
I've checked the scope, it includes "https://www.googleapis.com/auth/spreadsheets" permission; besides, this same script has no problem writing to another spreadsheet that it creates in runtime.
function updateAnotheSpreadsheet() {
var targetSpreadsheet = SpreadsheetApp.openById('<target spreadsheet id>');
var sheet = targetSpreadsheet.getSheetByName('<sheet name>');
Browser.msgBox(sheet.getRange("A1").getValue()); // Here I see that my getSheetByName worked
sheet.getRange("A1").setValue('Test value'); // But this does nothing
}
There are no errors but also no effect: nothing changes in the target spreadsheet.
Hi I was testing and was able to do what you are trying to do. You can try to declare a variable for the message, here is what I was able to do, I hope this resolves your inquiry.
function updateAnotherSpreadsheet() {
//Opening the second Spreadsheet by ID
var targetSpreadSheet = SpreadsheetApp.openById("<SpreadSheetId>");
var sheet = targetSpreadSheet.getSheetByName("<SheetName>");
//Getting the range to show on msgBox.
var msg = sheet.getRange("A1:A1").getValue();
//Displaying old data on A1:A1 from the secondary Spreadsheet.
Browser.msgBox("Old data on secondary spreadsheet: " + msg);
//Getting the range and setting new values
sheet.getRange("A1:A1").setValue('Test value').setBackground("teal").setFontColor("white");
}
I would suggest to check for more information here https://developers.google.com/apps-script/guides/sheets.
I hope this helps, greetings.
I found the problem.
A function called from OnEdit() can't ask for permissions. I needed to first update that other spreadsheet from any function called by something "active", like a button; then, after the script asks for permission once, it can do its thing from OnEdit() the next time it runs.

Time-driven triggers scope in Google Script

UPD:
I have a published application as Spreadsheets addon and a few spreadsheets, that use this addon and execute time driven trigger:
ScriptApp.newTrigger("myFunction")
.timeBased()
.everyHours(1)
.create();
All works fine, but I'm a little confused, in what scope does this trigger work?
Since this is a published addon, the script is one and the time driven function is also one, right? As Diego said: "It works with the user scopes of the user who initiated the trigger". Okay, but what about spreadsheets?
I need to write (if trigger sees only this spreadsheet):
function myFunction(){
var ss = SpreadsheetApp.getActive();
}
Or (if it runs without binding to a specific spreadsheet):
function myFunction(){
var ss = SpreadsheetApp.openById('<some id>');
}
In other words, when I set the trigger, do I set it only for the current document or for all?
Thanks!
It works with the user scopes of the user who initiated the trigger. Here's how you can check:
Create a User property value i and set it to zero
In a new function, get the user property i, write it to a sheet, increment i and save it back to the property.
Create a trigger so that function runs every minute.
View the spreadsheet as a different user.
As the different user, repeat steps 1 & 2 to see that now i has started again from zero, but the 1-minute trigger is still iterating the original i value (because the different user can't access the first user's properties).
Here's the code:
function getAndPrintUserProperty() {
var userProperties = PropertiesService.getUserProperties();
var i = userProperties.getProperty("i");
i++;
SpreadsheetApp.getActive().getSheetByName("Sheet1").appendRow([i]);
userProperties.setProperty("i", i);
}
function addUserProperty() {
PropertiesService.getUserProperties().setProperty("i", 0);
}

Allow other users to execute google spreadsheet scripts

I have made a simple Google Spreadsheet with some scripting behind to update colours depending on status, set update and creation dates for rows and a few other controls.
It all works when I am editing it with my own user, but now that set spreadsheet is set to public (access via link), anyone else accessing the spreadsheet gets an error you do not have permissions to do this action.
I am using onEdit() triggers and apparently guest users do not have permission to execute them. Do I have to configure something in order for it to work?
thanks
Not a real solution but somme observations:
I made a little script and tested it on the old ad the new spreadsheet type:
function imwatchingus(event) {
Logger.log(JSON.stringify(event));
try{
if(event.value=="faux"){
SpreadsheetApp.getActiveRange().setBackground("red");
}
else if(event.value=="vrai"){
SpreadsheetApp.getActiveRange().setBackground("green");
}
else{
SpreadsheetApp.getActiveRange().setBackground("blue");
}
}
catch(e){}
}
function spyonme(){
var trigs = ScriptApp.getProjectTriggers();
var func = [];
for (var i in trigs){
func.push(trigs[i].getHandlerFunction());
}
if(func.indexOf("imwatchingus")>-1){
return("already enrolled");
}
else{
ScriptApp.newTrigger("imwatchingus").forSpreadsheet(SpreadsheetApp.getActive().getId()).onEdit().create();
return("enrolling you");
}
}
the function imwatchingus is the one that will do the job (it colors the background of the cell in red / green / blue depending on his text).
to have this function working I added a trigger to it. You can do that with the menu or launching the function spyonme.
On the New spreadsheet there is an issue. Only the first cell (A1) will be modified, but you can use this script if you are anonymous.
On the old spreadsheet, the color is applyed on the changed cell, but it's not working in anonymous mode.