Bound app scripts and multiple projects - google-apps-script

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.

Related

How to avoid saving a google sheet and have multiple people editing at the same time?

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

Identify External Apps Script That Modifies Google Sheet

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.

How can I invoke my standalone script in my spreadsheet?

I have created a standalone Apps Script from Google Drive, but when I try to access it from a Google Spreadsheet, I don't see a way to access the script (even though when I created the script, I created it as a spreadsheet script).
In the spreadsheet, the "Tools->Script manager..." menu item doesn't show my script, nor does the "Tools->Script editor...". The latter has a section for 'recent projects', but it doesn't list my newly created script, nor its associated project.
If I create a new script from inside the spreadsheet (i.e .Tools->Script editor...) and cut and paste the code from the standalone script, it works fine. However, this can only be used in the single spreadsheet -- to use it from another one, I have to cut and paste / go through auth again.
Is there a way to do this without publishing the script to the gallery, which I assume makes it public (I don't have a problem making it public, but it seems like there must be a better way)? The script is here -- it is world readable with the link. It adds a menu to a google spreadsheet that lets you run a query in BigQuery and dumps the results into your spreadsheet.
The behavior you describe is exactly how it is intended to work... the script you wrote can only work if it is bounded to a spreadsheet.
The fact that you wrote it in an independent script makes it indeed a standalone app but it can only be executed from within the script editor and in this context an onOpen function like the one you wrote doesn't make sense (it runs on opening a document/spreadsheet, not the script).
For now a script can only be bounded to a spreadsheet by copy/pasting the code, at least a script that you want to use as you do (with a menu and direct function calls and activeSheet calls), maybe one day Google will expand the concept of libraries to make your script function available from different document directly (read also the doc about libraries).
There are actually 2 types of standalone Apps Scripts, the one you tried that runs only from within the editor and webapps that are deployed and run in a browser window by themselves. The latter are always build around a doGet function that represents an entry point for the application. You should read the documentation on the subject to get all the details.
To summarize : if you want to access your spreadsheet using getActiveSpreadsheet() and similar methods then you have to write (or paste) the script in the script editor bounded to the spreadsheet.
If you want to have a standalone script you can also interact with spreadsheets (and other document as well) but you have to access them by their ID and the spreadsheet will only be viewable from another browser window, without link between them, and there will be no "active spreadsheet" nor "active sheet"...
A webapp can of course also do that with the advantage of a user interface that you can adapt to your needs but this is becoming a bit too far from the scope of your question.
Hoping I've made it (a bit) more clear, at least I tried to make it simple ;-)
I worked this out with Jordan, following the instructions here:
https://developers.google.com/apps-script/guide_libraries
First, he had to share his standalone script with me, so I had read access to it (probably setting the script as public read would work too).
Then he told me his project key.
I took his project key and inserted it in "Find a Library", in the "Manage Libraries" menu.
For this to work, Jordan had to export "a version" of his library - so third parties can rely on stable code and upgrade at their own rhythm.
As Jordan's script runs automatically on onOpen(), my code had to define a function onOpen() that called his script onOpen(). To do this, the library manager gave me an identifier for the imported library.
Sample code once the library is imported and the library manager gives you an identifier for the library:
function onOpen() {
GivenLibraryIdentifier.onOpen();
}
You can also use installable triggers. If you install an installable trigger (such as onOpen) from your standalone script into the document, it gets similar privileges as if it were a simple trigger.
And better yet, when the installable trigger calls its function from your standalone script, it executes WITHIN your standalone script! So it can call any other function in your standalone script. Its Logger.log() entries can even be viewed from your standalone script, even though it was executed from your sheets document.
So basically, if your script installs an onOpen() trigger tied to one of its functions, it's as good as if your Sheets document had required() your whole standalone script file.
(Assuming the owner of the script has edit privileges to the file.)

Where to save GAS to be accessed via several spreadsheets?

I have written a script inside spreadsheet, that is triggered on spreadsheet-form-submit.
However, since I have several such similar spreadsheet, I would like to place the code at one central location and link/import in each spreadsheet. (That is, I want to maintain one copy of code, where-in if I make improvements, it should auto-magically be reflected in each spreadsheet).
Thanks.
Place your code in a standalone script (create it from the Create>More>Script menu in Drive or just from script.google.com). In each spreadsheet, add a script that just includes your first script as a library and delegates onOpen, etc. to the functions in the library. When you update the standalone script you'll need to go into each spreadsheet and update the version number of the library to be the latest version, but that's all.

Is it possible to have one script for multiple spreadsheets?

I have one master spreadsheet and a number of copies. This master spreadsheet uses some scripting.
Is it possible to link all the copies of this master spreadsheet to the same script as in the master spreadsheet?
Objective:
changes in the scripting in the master spreadsheet are automatically used by the copies
aka: low maintenance
amleczko is right: you should use the new library feature in Google Apps script.
However, as of today, you won't be able to do exactly what you want (using the same script for several spreadsheets). What you can do instead is save a version of your script (Files > Manage Versions...), in order to create a library. Then, import this library in the other spreadsheets (Resources > Manage Libraries...). Switch on the "development mode" so every change made do the library will immediately take affect in the spreadsheets using this library. Otherwise, you will have to save a new version of the library for every change, and manually update the version number of the library in every spreadsheets using it.
The problem is, you need to write a script in every spreadsheets using your library, with skeleton functions like this:
function doSomething(){
myLibrary.doSomething();
}
best way is to publish as add-on, then install the add-on, it will appears in every spreadsheet you open. and you can publish as private, which only seen by yourself.
I think this has changed. According to Issue 40 starting from 22 May 2012 there is such a possibility. Please check:
https://developers.google.com/apps-script/guide_libraries
https://developers.google.com/apps-script/guide_versions
http://googleappsdeveloper.blogspot.it/2012/05/introducing-versions-and-libraries-in.html
It's not possible in this way that you're thinking. At least, not yet (see issue 40).
But, depending on your script usage, you may connect them "the hard way" or even better, use only one script. The script on the master spreadsheet can open the other spreadsheet files and do its job "remotely". It's not required that script to be hosted on a spreadsheet to interact with it (read/write on it). You only need a script hosted on the spreadsheet if you're going to use spreadsheet events triggers i.e. on-open, on-edit and on-form-submit.
Maybe you can develop a nice UI for the script on the master sheet and publish it as service. Then only have a link on the copies to access the same UI on a different browser tab. Adding parameters to the link the script UI can even adapt to the particular spreadsheet that is "triggering" it.
Well, that's all I can imagine now. But, unfortunately, there's some use cases that just don't fit this nice "workarounds". For those, one can only star issue 40 (to kind of vote and keep track of updates) and hope it's developed soon.
The solution I put in place in this context was to have a Google Site, where the Master Script is embedded, and where the Spreadsheet is embedded too
Then, the script, refering to a dedicated spreadsheet, looks for the Google Site Page's name, looks in the Master spreadsheet and get the ID of the spreadsheet which is embedded in the Page.
I have solved this problem when using a script which auto generates spreadsheets.
Typically, I will add a sheet to any spreadsheet with a script called "Info." I'll use that to store information that it important to the script. In my script which auto generates more spreadsheets, I keep track of the ID of the created sheet. This way, I can then quickly call up all of the "linked" sheets, and interact with them with using the same script. It might even be worth writing the script in one sheet, and keeping it totally separate from your Master sheet or it's children.
Take a look at this function, it might give you some ideas.
SpreadsheetApp.openById(id)