There is a Google sheet with a bound Apps script. One of the functions (written by me some time ago) that applies custom formatting etc. is triggered by an installed onEdit trigger (installed by the client). This worked perfectly until now.
The spreadsheet owner (one of my clients) recently complained that when he enters any text into a cell then the cell next to it will automatically get overwritten with "FALSE", and when he edits that "FALSE" then the first cell gets overwritten with "FALSE", which makes no sense at all.
I have checked the original script, which still looks fine, it can not cause this automatic mess creation. To make sure I am not missing something, I have checked, there is only one installed trigger, and there is no other simple onEdit function in the bound script.
I have also temporarily changed the name of the function started by the installed onEdit trigger to disable the trigger. Despite the fact that now editing could not run any function in the bound script, the automatic mess was still active, and something has automatically overwritten some cells when the user is editing.
This makes me suspect that an external unbound script (could be also a script bound to another spreadsheet with a timer trigger) might be the culprit and doing all the mess.
My question is whether or not there is a way to identify any external scripts (not bound to this spreadsheet) that change the sheets?
In this case I can not provide any code or show you the spreadsheet/script because it is confidential, belongs to the client. I can not show you any code that I have tried either. Only the above explanation how I attempted to locate the source of the problem.
Additionally let me add, that in this case more than one people are messing with the sheets and scripts in the background that I don't know who they are. Therefore, there is a chance that some rogue parties are trying to deliberately sabotage my work (if for nothing else, then as a prank).
UPDATE_1
I have made a copy of the spreadsheet, then verified the installed triggers page and there are no triggers installed.
Next, to make absolutely sure that none of my scripts should cause the problem, I have deleted all my scripts. Only an empty project remained, still having the same name as originally. Saved the empty project. Reloaded the spreadsheet and tested again.
The problem still exists: when I edit the misbehaving cells, the cell next to it automatically changes to "FALSE".
I have also made sure that no ARRAYFORMULA is present in those columns.
The right answer to the topic question was first given here by #TheAddonDepot thanks, but for some reason he deleted it. If he re-posts the same answer here, I will accept it as the best answer and give full credit for it. Then few minutes later Rafa Guillermo repeated it as seen above.
The answer is that it is not possible to identify external apps that modify the spreadsheet, but the only chance to find a trace is to look into the version history to see which editors have modified the sheets. I take this as the correct answer as long as someone does not offer a better answer.
In mean time (as explained in the update of the original post) I have stripped down all scripts from the spreadsheet, but the problem was still present. With this setup the only remaining suspects were any addons.
It turned out that there were these two addons visible in the Add-ons menu but they were not installed:
Sheets2GCal
Magic Cell Notifications
Since I could not remove an addon that was not installed, I had to install Sheets2GCal and then removed it right away. This fixed the problem, the automatic mess has stopped.
But, to make sure the other one should not cause any problems in the future, I have installed and uninstalled that one as well.
Thanks again to the contributors.
Related
I’ve created a Google Sheet – kind of like an app – using script. It’s a number of blank cells where once added some information, it creates a string with that information in the right order. I created this to help some colleagues. We are using free accounts.
I face 2 problems:
The app is supposed to be used by only one person at a time, but I’m sure at some point two or more people will want to use it at the same time. Is there a way to allow this without they interfering with each other? I’ve read you can share a link that creates copies of your doc, but that wouldn’t work in this case because I intend to keep updating it regularly.
The changes people make to the sheet will stay there. Right now I have a onOpen function that just rewrites everything, but if someone accesses the document while someone is using it they’ll rewrite everything and ruin their work.
Any ideas on how to solve these two problems? Thank you so much in advance!
I think that you have to find another way to implement your "app" because Google Sheets not a good tool for limiting that only one user edit a spreadsheet a time, but if for any reason you decide to keep with it,...
... you need to implement a workflow like the following
Save the active user email on PropertiesService.getUserProperties().
This should be done by each user by running a script themselves. The most user-friendly will be by using a macro, custom menu or button (a drawing with an assigned Google Apps Script function). You might use an open installable trigger to validate that the active user have already done this.
You might have to make the function that clears the data a "smart" function.
You might have to consider additional cases, like if the owner of the spreadsheet opens it when there is an editor working on the spreadsheet.
Use installable triggers to manage the sharing settings.
Use an open trigger to remove all the editors except the active user
Use a time-driven trigger to add again the editors. To make this work effectively should define how the time-driven trigger will know that the last editor have finished their session, i.e. you might use DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()).getLastUpdated()
The above should help you with both problems, as long you as the owner do not open the spreadsheet as is used by someone else.
Other alternatives that might work better is to create an add-on or a web application.
Related
Determine current user in Apps Script
Last modified date in Google App Script GAS sheets
I have been playing around with Google Script Editor and I've gotten heaps of use out of it.
The next task that I am looking at is automatic formatting and text insertion/replacement when copying a Template.
An example use case is as follows: Within my organisation I have submitted a Doc to the template gallery. When creating a copy of the template I want it to automatically insert today's date and the current time (rounded to the nearest hour).
This is a question about the Triggers. The text replacement bit is easy and done. Not to mention this is just one of the basic use cases, I'll be attempting many more similar behaviours with things like timesheets and the like.
The problem that I am running into is that I can't seem to get the triggers to work as I'd like them to.
2 of the Triggers that I thought I could try and use: onOpen(e) and onInstall(e).
onOpen(e), though it works, it works "too well". That is, it also replaces the text on the original template as well, proving a nuisance when updating info in these templates.
onInstall(e), I thought this would work as creating a copy of the Doc also "installs" the script as well. However this function doesn't seem to run at all.
Any ideas about getting a trigger to happen once and only once when a Doc is created from a template?
Cheers,
Bricktron
First of all, Trigger onInstall(e) works only for Add-ons.
Now coming to onOpen(e), in my opinion you can use Google Apps Script Property's Services to store one flag which helps your code identifying whether this file has been opened or not.
So for very first time onOpen(e) runs, assign property eg: propertyService.setProperty("opened","TRUE") and next time you can check by accessing the property whether it has been already "opened" or not.
Example:
var openedFlag=propertyService.getProperty("opened");
if(openedFlag=="TRUE"){
//Document has been modified
//Do not run the modifiable code again
}else {
//First time
//Edit the file
//Set the propertyService to "TRUE"
}
First time I'm working with google app scripts. I inherited a google sheet (template) from a colleague (who no longer works with us) that runs some scripts on change. The script from what I read should be a bound script.
When I open Script editor though, I see 2 projects. One is the copy of an earlier version of the project. Each of them has an onOpen() function. My question is how does the sheet know which one to invoke?
To replicate the behaviour, I created a sheet of my own and then using the script editor, created two projects each with the onOpen function. The function adds some menus on opening the sheet.
I see that both functions are fired, because the menu names are different. However, I don't see the same behaviour in the original template in question which seems to invoke only one of the two scripts.
I checked to see if there are any triggers etc. but none seem to exist. I find no documentation around this either.
They are both bounded to the sheet, as your tests showed both onOpen(e) are being invoked. What you could do to know which of the script was edited last or some details is to check the revision history of each script (in each script file-> see revision history).
I figure the last developer didn't comment the code so he must've disaled one of them by hand
It turns out as I mentioned that both onOpen functions were indeed being fired. Both scripts and tons of code in it but was exactly identical and the end effect was the same on the sheet. Pretty weird.
I fixed it by making a copy of the sheet which let me delete one of the projects and keep just one. I then deleted the original sheet and made my copy the official one (naming it the same).
I'd have preferred if apps script chose to make it unambiguous by mandating explicit triggers to a function on a particular sheet.
I have a script which adds new rows and changes the sheet in some ways.
I'd like to lock this sheet so it cannot be edited manually, but allow the script to be run.
Is there a way to do this?
Yes, it's not the most easier thing to do, but it's possible.
But before we start it's important to understand how Google spreadsheet/sheet protection works.
There's no way to protect a sheet or range from the file owner. So, if you also want to protect a file from yourself (assuming you're the owner). Then the only solution available now (I do hope they change this in the future) is to change the ownership of the file to another account. What most people do is use another Google account that they don't use actively (you can create a bogus easily if you don't already have one).
Then, after you have transferred the file to another account, you can easily lock changes to ranges, sheets or the whole spreadsheet using the regular GUI.
Ok, now to the script. When a script runs, it's always using the authorization of a specific account, and it can do everything that this account can. For example, when someone runs a script directly from the script editor, or clicking on a custom menu or image: it runs under the account of who's clicking. But if you set a installable trigger, then the script always runs under the account of who setup the trigger in the first place (not who's performing the action that actually triggers it). Simple event triggers run as who's triggering the action (you can think anonymously), but they can't do much, it's a security feature (read the link for a better understanding).
Lastly, when one is publishing a script as a web-app, then there's a selection box to choose if the script runs as the developer or the user. Very simple.
Back to the problem. Since we have locked out everybody from the desired sheet/range. For a script to make changes to this locked area, it must run as the file owner!
If the changes you do are automatic, e.g. via a installable trigger, then you're good. Just setup the trigger using the file owner account and that's it. Also, if your users are accessing the script "externally", that is, via a web-app. Then that's easy to, just setup the web-app to run as the developer (the file owner).
The most complicated scenario is if you need to run the script from a custom menu, which will then run under the privileges of who's clicking on it, which themselves can not make changes to the protected area. The solution to this is to deploy the script as web-app running as the developer, and have the function running from the button click to call the deployed url using UrlFetch, possibly passing some parameters to designate what needs to be done. Then, since the webapp runs as the developer (which is the file owner), it can make any changes required, and return (if there's something to return) any value (usually a JSON) to the calling function (which is running as the user and is associated with his session), so you can show him a message on a popup or toaster, etc.
It's kind of tricky but works great, and you can do all that on the same script contained in the spreadsheet. But if you're concerned that your users may access the script editor and change the code (which is possible), than you should separate the web-app part on a different file, that you don't need to share with them.
Yes but only for others not for the script owner. Just use 2 accounts one as the owner and the other one as the reader.
I have a trigger that I can't find! At least, I can't find it easily, and may have to resort to the brute force method.
I've got more than a hundred scripts and spreadsheets-with-scripts, and somewhere in there I have a function called colorRows that is set up as a trigger. I know that, because it shows up in my triggers list:
How can I find the script that contains colorRows?
I've tried using Search in Google Drive, but it appears to be unable to find the content of scripts contained in spreadsheets. (It does index standalone scripts, so that did narrow down my search to just spreadsheets.)
I can open every spreadsheet, then open the script, and search - brute force. No thank you, that's still some 75 sheets.
Script Services getProjectTriggers() is project-based, as the name implies, and only works within the project it's running in.
The title of Spreadsheet source of a programmed trigger sounded good, but is quite different.
I don't want to stop the trigger (that's easy), I want to find the code. So How to Stop Orphaned Triggers is irrelevant.
There is a record of this complaint, Issue 146. It was raised in July 2010, and closed as Won't Fix in Feb 2011.