How can I invoke my standalone script in my spreadsheet? - google-apps-script

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.)

Related

How to allow to run google script on a protected sheet (in a spreadsheet) [Can't use trigger and web app]

I'm coding a script for a spreadsheet. This script creates a menu.
Then by choosing an option in this menu a function (which uses API) will run in order to filter some columns and hide others.
The problem is:
This sheet is protected (because shared with coworkers) but I want to allow people to run the script, which is impossible without the permission.
I already looked at different solutions:
Using a trigger: Doesn't work because a trigger can't correctly call a function which uses API (yes, my functions use API).
Web App: When the script is run from the spreadsheet, the script is run as the current user, not the script editor. (the web app is efficient if the user uses the HTML page.)
Remove protection -> run the function -> Re-add protection: Can't modify the protection without permission, which is logical.
Add the current user in the list editor -> run the function -> Remove the current user from the list editor: Can't modify the editor list without permission, which is logical.
How can I solve this problem?
This is a pain that I have not seen a good workaround. Google should know that in a collaborative environment that the owner would create script and want users to be able to run those scripts while at the same time not messing with formulas or cells that you desire to protect. The only way I have found to solve this on the sheet itself is to Unprotect and then Protect the cell or range you are making modifications to during the script run. Do be mindful that if the script fails in the middle (after you have unprotected it) the cell remain unprotected. Might want to run within a "try" script.
Are you the spreadsheet owner?
If no, you are not authorized to bound an apps script on it.
But you can make a copy of the spreadsheet by duplicate it and save in your drive.
If the spreadsheet is created by you yourself, maybe you created it by another username. If it does, log in by that user and change the sharing option to allow the specific user can edit it.

How to change a "bound" Script to a "Standalone" script?

I have a Google Sheets script that's bound to a specific doc. Basically it takes the results of certain cells, then it used these values in doc.
I have multiple Sheets I'd like to "attach" to this script. All the other Sheets have the exact same tabs and cell names, etc.
Is there a way to turn that bound script to a standalone script, then attach them to the other Sheets?
You cannot unbind a bound script:
The file a bound script is attached to is referred to as a "container". Bound scripts generally behave like standalone scripts except that they do not appear in Google Drive, they cannot be detached from the file they are bound to, and they gain a few special privileges over the parent file.
To use the functionality you've created, you'll want to export the bound script as a library and import it in the other spreadsheets' script editors. You may also have to do some tweaks to get any UI alterations
Note that an installed library will not automatically update the used version, so if you make changes to your library source and save a new version, you'll have to go through all the spreadsheets that reference it and update the library version.
The alternative to a library is to go through the process to publish your script as an add-on. This will let you redeploy your changes without needing to go through every single spreadsheet.

Authorization on spreadsheet script for own use

I have a system (I'm the only user of this system, and that's not expected to change, ever) in which spreadsheets are copied from a template file. This template has a script, which populates a few menus to perform some operations. So each new copy of the template spreadsheet has its own copy of the script. The problem with this is that every time a new spreadsheet is used, the user (me) has to authorize the execution of the script. This didn't use to be that bad, but the authorization process has recently become way more annoying (see for example https://developers.google.com/apps-script/images/unverified-app-ui.gif). Given that I'm the only user of these spreadsheets, I think this process is unnecessary and wish to get around it.
I have tried to get around this by extracting the code into a standalone script file and publishing as an add on (https://developers.google.com/apps-script/quickstart/docs) but actually publishing it requires me to pay 5 dollars, and I think this is ridiculous given that I am the only user.
Any other ideas?
Thanks in advance.
AFAIK, that's the intended behavior. Check guide to the authorization lifecycle for add-ons.
Add-on automatically runs its onOpen(e) function to add menu items when a document opens — but to protect users' data, Apps Script restricts what the onOpen(e) function can do.
Note that only published add-ons can be in AuthMode.NONE.
The concept of authorization modes applies to all Apps Script executions.

Using Google Apps Script Libraries

I have read all Google documentation on managing and creating libraries, yet I still do not know if they are an appropriate option for the problem I am trying to solve.
I know how to save a version of a standalone script. I know how to add the library to a spreadsheet via the script editor. But I don't understand, very simply, how to trigger the library script within the new spreadsheet.
I have a spreadsheet that serves as an often-copied template within my organization. The template contains a script that (onOpen) accesses data on a separate spreadsheet (a master database) and sets those values on a tab called "admin." The desired result is to have a copy of the master database living within the template sheet (and every subsequent copy of the template sheet). At this point, there are thousands of copies of the template sheet, each running that same script.
Whenever I have to change the script, I have to change it within thousands of sheets. Can I use a library instead? I'd like to be able to create a new version of the script in the library and have all sheets connected to that library experience the change. I understand that the library needs to be in development mode (within each sheet) to do this. I also understand that in order to make this switch, I will probably still have to go into each sheet to add the library. I'm just hoping it will be the last time I have to do such a tedious task.
Any advice or links to solid info is appreciated.
besides making an add-on (already covered in another answer) I will answer your libraries question. They will work for you. What you are missing is the "connect" part.
For this you want to trigger the library code from say, onOpen. The onOpen in the library is not enough and not detected by apps script. Instead each of your spreadsheet's script needs an onOpen(e) which just calls yourlibrary.onOpen(e).
since those "hook" calls rarely change, specially once you stabilize your library api, and using it in "development" mode will let you modify just the library.
whenever one of those hooks needs to change (say a callback from an html GUI needs a new parameter) you need to update all the spreadsheets. to avoid this, make all your callbacks receive a single json object instead of multiple parameters.
Sorry if I am repeating other answers, but I would like to sum up and add something:
You can access your library functions as follows:
From the app using the library you go to the Resources/Libraries. You can see the library name under "Identifier". On the same line where you can select Development mode.
Library name found in resources
Now in your library you have for example a function
function onOpen(e)
{
Browser.msgBox("HELLO!");
}
In the spreadsheet app you wish to access it you use the library name found in the resources, for example "testlibrary"
function onOpen(e)
{
testlibrary.onOpen(e);
}
Now if you have development mode on, the modifications to functions in the library update automatically to your application (spreadsheet) as long as the user using the application has edit access in your library script.
If anyone using your spreadsheet has a restricted access to your library script (meaning only view access) or development selection is off in the application, you have to go to the application's script, Resources/Libraries and select the most recent version of your library to be used in the app everytime you update the library and save a new version of it.
Still, especially if you are using mostly only the onOpen function , I would recommend using the library rather than copy-pasting the function to the script of each spreadsheet, as it is easier to track which scripts are up to date and it is easier to avoid errors and differences between the scripts.
Even in the more restricted case, if you update function in library - as long as you are already calling it in the app - all you have to do is select the new version of the library used.
I hope I had anything to give in this conversation and my language was appropriate, this was my first answer..
A good question Melly. I've been through a bunch of the documentation and some tutorials on this subject but haven't tried adding any libraries yet. My understanding is that once you are connected to a library all you have to do is call the applicable functions. That once a library is connected the functions in the library become an extension of all the other Apps Script classes available in the script editor.

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.