Apps script library automatic updation on client side (on doc refresh)? - google-apps-script

I have
an Apps Script Library 'MyLib'.
template Google Spread Sheet ('MyGSSheet'). Through Script Editor I added the library 'MyLib' to 'MyGSSheet'. I set 'Development mode' 'on'.
users get a 'copy' of this template ('MyGSSheet').
How can I have a setup wherein any changes I make to 'MyLib' get picked up across these copies automatically (once the Spreadsheet is reloaded)?
I thought having 'Development mode' 'on' is all that's needed for this continuous update of the code in all the Spreadsheets.
However, I don't see this happening. The copies aren't picking the latest code.
I also granted 'edit' permission to all users within our company domain.

I am not able to comment so I hope I am contributing enough to justify an answer..
So I tried to reproduce this:
I created a standalone App Script 'MyLib' and wrote a single function:
function myFunction()
{
SpreadsheetApp.getUi().alert("TEST");
}
Next I created a spreadsheet and added a script to it via Tools. I'll call it "Spreadsheet Script".
In the Spreadsheet Script I added the MyLib as a library and turned the development mode 'on'.
Also I added two functions to Spreadsheet Script:
function onOpen(e)
{
myFunction();
}
function myFunction()
{
MyLib.myFunction();
}
Ok, now I shared the Spreadsheet and the MyLib - script to my colleague with edit rights. He opened the spreadsheets and got the alert "TEST".
Now when I modified the alert text from the MyLib-script's myFunction to "TEST 2 " and just saved the file (File/Save, did not save a new version), my colleague saw the changes in the spreadsheet. Then, I made him to take a copy of the spreadsheet( To test the effect of the spreadsheet owner change).
I changed the alert the text to "TEST 3". The change was reflected in his copy of the spreadsheet.
Are you able to reproduce this or does this approach fail to update for the other users? I am choosing this kind of simple example as sometimes the reason might reside in the code too...

Related

Hide Apps Script code from editor of a Google Sheet

I hope I'm not again asking a question that's been answered, kindly bear with me if I am doing so.
I basically have been building a Google Sheet to be used by a number of users. I created a number of Script files and everything is working as expected, however editors of the Sheet will be able to see the source code from Script Editor menu option on Sheets.
There is a lot of sensitive information in the scripts that should be hidden from the editing users, how can I restrict them from editing the scripts and also not viewing sensitive information?
I found this answer which suggested publishing the Apps Script project as an Add-on. Unfortunately I am having a tough time comprehending how to go about it (my manifest hasn't been updated) and there are a few more answers that use different approaches.
Note: If there is an easy example showing how to go about Publishing then kindly share as I am struggling with Google's documentation.
My appsscript.json file:
{
"timeZone": "Africa/Maputo",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
Note2: The scripts' entry point is via an installable trigger On editing the spreadsheet
Note3: When switching to Legacy mode and Publish->Deploy from Manifest as in the examples from Google's documentation, I cannot see an option to install the addon - No entry points is indicated.
What would be the best way to achieve this security feature given I have multiple .gs files in my project and want the editors of the Spreadsheet not to have even viewing access of the source code.
Will greatly appreciate any feedback, Thanks.
The behavior you are experiencing seems to be due to the fact that the script is bound to the sheet in question.
A bound script is nothing but a script which was directly created from the spreadsheet itself rather than separately.
What you can do in this situation
Create a new Apps Script project which is not bound to any spreadsheet. A simple script.new in the browser bar will do the trick.
Copy and paste the entire code to the new script.
Replace any instructions similar to:
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// Or whichever Apps Script service you are using
To:
SpreadsheetApp.openById("SPREADSHEET_ID").getActiveSheet();
// Or whichever Apps Script service you are using
Since the new script won't be attached to the spreadsheet, you will have to specify exactly which spreadsheet you want to open and work on, hence the use of openById method.
Reinstall the trigger/s
You can install the triggers programmatically too! For your use-case, if you want to use an onEdit, just create the following function. This ends up creating an onEdit trigger and it attaches it to the function you want it to act as the trigger.
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.openById('SPREADSHEET_ID');
ScriptApp.newTrigger('TRIGGER_FUNCTION_NAME')
.forSpreadsheet(ss)
.onEdit()
.create();
}
Remove any code from the old script which was attached to the spreadsheet.
VoilĂ !
Reference
Container-bound Scripts.
Each Editor has access to all shared codes.
Check this website
To hide code for editors, you can only obfuscate the code.
You cannot protect the code nor any credentials unless you publish the code as an add-on.

Enable Google Sheet Protection via Google Script

I have been searching for a solution to a Sheet Protection issue, I have a Sheet that includes automated scripts to write data into the Sheet below from some fields at the top. I want the user to be able to provide the data in the fields at the top and then to run a script that adds the data below. The script to add the data works fine for the owner of the sheet if Protection is enabled, but fails for any user that has Edit rights as the Protection cannot be cleared by script for the data to be written to the bottom of the sheet.
https://developers.googleblog.com/2015/02/control-protected-ranges-and-sheets-in.html
I found this code from a Google blog post a few years ago and have tried variations without success....is this type of approach just not possible with Google Sheets and Google Script ?
Unfortunately, that is not currently possible to run a bound script as the owner without using a workaround. Scripts can only be run as the owner when they use Triggers or when you make a standalone Script Web App. You can see this for more information.
This should bypass fooling around with scripting protection ranges!
The Workaround!
There is a way you can get around this by creating a web app so that your bound script talks to the web app which runs on the spreadsheet. See this answer for more information.
So the process looks like this: User clicks a button that runs a function on a bound script. This function makes a web call to a web app that can run a function as the person who created the script.
I would also recommend you pay attention to a comment by Augustine C:
...you may also find it helpful to have a shared secret key saved in your spreadsheet and then verify it using the backend webapp script, or to perhaps verify that the recipient of the email is, in fact, also an editor of your Google Sheet.

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.

Publishing / sharing a Google Sheet via Github

I have a Google Sheet. It has a custom script (read: set of functions) associated with. I'd like to share this sheet template (i.e., tool), not with colleagues (i.e., adding them via email address as is a traditional Google drive share) but with anyone who would like a copy for themselves to use it for themselves (i.e., not my copy, their own copy). Ideally, I'd repo this project / tool on GitHub (or similar) and let them grab it there.
I can "Download As..." the sheet but the script doesn't stay "attached". Are such scripts now what Google considers Add-ons? If so, how to I keep the sheet + script as a "whole".
Also, as a temporary workaround, I tried to copy / paste the script from my working copy to another copy of the sheet (created via Download as and then opened again under a diff Google accnt). However, that didn't go as planned either. There's a function within my scrip that checks to make sure the sheet is on the first tab / sheet (i.e., getActiveSheet().getSheetId() == 0). This works on my dev / working copy. However once I copy / paste the SheetId return a 7 or 8 digit #. Is there a way to keep the SheetId relative to the sheet, and not all sheets (or whatever that Id represents.)
Note: I'm by no means a Google Sheets expert. This was just a side project for myself that I ended up building out to the point of wanting to share it with others. Please presume I know even less than you probably think I know. Thanks :)
To fix your problem with finding the first tab / sheet, use the sheet index instead of the sheet id. So instead of getActiveSheet().getSheetId() == 0, it'll be getActiveSheet().getIndex() == 0 instead.
As for downloading the script - it looks like you've created a container-bound script. There are two types of scripts that you can create, standalone and container-bound scripts. (See Google's explanation here). Standalone scripts are created by going directly to script.google.com, but I'm guessing (please correct me if I've assumed wrong) that you clicked Tools->Script editor, so that the script is locked to that specific spreadsheet. That's fine, but a) it means like you can't download it separately through Google Drive as I originally suggested, and b) when you're download the sheet, it downloads it as an Excel worksheet (which doesn't support Google App Script, so of course the script doesn't come with it).
Here's my suggestion for your use case:
Instead of downloading the spreadsheet, make a new copy of it named yourspreadsheet_public or something along those lines by going to File -> Make a Copy. (You can potentially skip this step if you just want to make your personal spreadsheet available to the world.)
Make the copy available to anyone to view by clicking Share -> Change -> Anyone with the Link (or Public on the Web) -> select "Can View" from the dropdown -> Save.
Now, you can distribute the link to whoever wants it. Anyone who has access to the file is able to make a copy in the same way you did in the first step to their own Google Drive, where they'll be able to edit their own private copy, including your script.
Let me know if that helps!
I don't understand why you want to use an external service to make copies of a spreadsheet. Since Google Scripts only run in Google spreadsheets I don't see any use case where it can be useful... but that's not the point, you do what you want.
That said, the easiest (and probably the only) one would be to share the document with someone and create an onOpen function that suggests to create a copy of it. This script should be executed after the required authorization and the copy will be their own copy, without any link to your G account anymore (which was the goal if I understood you well).
More simple and straightforward than that I can't imagine.
To avoid that their copy keeps the same onOpen behavior just setup a variable stored in userProperties so that when present this part of the onOpen doesn't execute.
This is a workflow I have already used and it works nicely.
edit :
I can suggest a completely different workflow to let other people get a personal copy of your SS.
Here is a test , give it a try and let me know if you're interested.
EDIT2 : since the other answer provides a similar workflow I decided to show the code I use in this answer to make that process more userfriendly.
I use 2 webApps :
one that runs as "me" that have access to my drive and runs without authorization for its user and that does nothing else than show a warning and a link.(accessible to anyone even anonymous)
And a second one that creates the copy and needs authorization to allow the SScopy creation in the user's own drive + a couple of links.(runs as the user accessing the app)
Code below (in 2 distinct projects of course) :
// APP 1 :
function doGet(){
var app = UiApp.createApplication().setTitle('Demo-App');
var link = app.createAnchor('Click this link to create your own copy <br>of my spreadsheet.<br>You will be asked for authorizations<br>tocreate a spreadsheet in your drive',true,'https://script.google.com/macros/s/AKfycbwQ5s_WWrsWXx_umZ1v91XGnm3RaO2Z7UQSXNiWFiaTwGuXIXqq/exec');
app.add(app.createVerticalPanel().setStyleAttribute('padding','50px').add(link));
return app;
}
// APP 2
function doGet(){
var ss = SpreadsheetApp.openById('0AnqSFd3iikE3dGNEUDdoLWhUZl9sZ3Z2Zm5XbjZzTkE');
var copy = ss.copy(ss.getName()); / the SS is shared to "anyone with the link can view"
var app = UiApp.createApplication().setTitle('SSCreate');
var panel = app.createVerticalPanel().setStyleAttribute('padding','50px');
panel.add(app.createHTML('A new spreadsheet has been created in your drive with name '+ss.getName()));
panel.add(app.createAnchor('Open it from <b>here</b>',true,copy.getUrl()));
panel.add(app.createAnchor('or from your own Drive','https://drive.google.com/?authuser=0#all'));
return app.add(panel);
}

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