How do you progress through the Google add-on authorization lifecycle? - google-apps-script

I feel like a bit of an idiot asking this question, but how do you actually move up from AuthMode.NONE to AuthMode.LIMITED to AuthMode.FULL?
I have https://www.googleapis.com/auth/spreadsheets in required scopes and this code:
function onOpen(e) {
if (e.authMode === ScriptApp.AuthMode.NONE) {
ui
.createAddonMenu()
.addItem('Authorise', 'authorise')
.addToUi();
} else {
// This never runs
}
}
function authorise() {
ui.alert(ScriptApp.AuthMode);
}
When testing the addon in AuthMode.NONE, the menu item appears. Clicking it brings up an authorisation request, but then AuthMode remains NONE. What is the correct structure to get to LIMITED? And beyond that, how do you get to FULL?
I've read the authorization lifecycle docs a hundred times, but it fails to explain this very simple point fully.
Thank you!

Related

GAS TypeError: SpreadsheetApp.getUI is not a functionDetails

I'm trying to learn some basic google scripting.
I tried reading through this several times, thought about it overnight and searched all over SO and a google for answers and feel like I'm missing something stupid.
When I click the menu item I've added to my gsheet to kick of the sidepage I get this error:
TypeError: SpreadsheetApp.getUI is not a function Details(/link)
If I click that details link I get the same message in a message box:
TypeError: SpreadsheetApp.getUI is not a function
Here's my code:
//#OnlyCurrentDoc
function onOpen() {
SpreadsheetApp
.getUi()
.createMenu("Admin")
.addItem("Admin page", "showAdminSidebar")
.addToUi();
}
function showAdminSidebar(){
var widget = HtmlService.createHtmlOutput("<h1>Sidebar<h1>");
SpreadsheetApp.getUI().showSidebar(widget);
}
I know I must be missing something simple, so thanks for any advice you can give me.
As pointed by #TheMaster above, there is a typo on the method showAdminSidebar().
...SpreadsheetApp.getUI().showSidebar(widget);... where getUI() should be getUi().
See this documentation of methods the class SpreadsheetApp has.
Just tested here and it works just fine after fixing it.

How to launch a second HTML form using Google Scripts / Google Web app

I am trying to build web app based on Google Sheets.
But for the interaction with user I need more then one HTML page.
The first one is easy to get:
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
But based on the response from the first page, I need to show the second page for detailed information.
For example: the first page is for log-in; the second page is for ordering.
How to run the:
HtmlService.createHtmlOutputFromFile('Index2');
Any help appreciated.
Peter
Add page to query string.
function doGet(e) {
if(e.paramenter.page) {
return HtmlService.createHtmlOutputFromFile(e.parameter.page);
}else{
return HtmlService.createHtmlOutputFromFile('Index');
}
}

Google Apps Script Add On google.run.script runs as developer?

I've only been working in GAS for a little under 6 months. Everything thus far has been bound spreadsheet projects, but have created quite a few. I have a new need to create an add-on sidebar that people can use in any spreadsheet they want (in a domain). I created the code, published it so only the domain can see it, and can install it as another user in the same domain, all of that seems to work fine.
The issue has to do with the sidebar.html, and calling a function in my script using google.script.run to call a function that gets some external data and writes it to the current sheet. All the code to get the external data works just fine, it all goes into a 2 dimensional array. The part where it breaks is at SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(1,1,x,y).setValues(array). I get a "You do not have permissions to access the requested document".
I did some of the following during testing:
1) The script for the add-on started out as a bound script to an existing sheet for development. Thinking that might be the issue, I re-created the project as a stand alone GAS and published it as a web add-on for sheets. That didn't make a difference.
2) The developer can run the add-on just fine, no sidebar error.
3) I created another menu item for the add-on that, rather than opening a sidebar and letting the sidebar call the function via google.script.run, runs the function directly from the menu. Doing it that way, it 'works' for the other domain user (where it fails running it from the sidebar).
4) I 'shared' the underlying Google Sheet I was using to test the add-on with the domain test user with the developer, and the sidebar script will then work. If I use the add-on in another non-shared sheet, I get the permission error.
5) If I use it on a sheet in a big, shared Drive folder we all use and have access to (domain wide), it works fine, just as I would expect from the test in '3' above.
The Big Questions:
So it seems as though 'google.script.run' runs as the developer, and
not as the current user (at the keyboard)? Is that right? Looking in
documents like this, I couldn't find anything that would indicate
that. Is there something I should be adding to make sure this works?
Is it a side effect of the way I'm publishing it, for domain use only?
Update: Without identifying one in particular, I tested out someone's 'Find and Replace' add-on, and sure enough, it's able to write out from the sidebar directly to the sheet without any issues. The only thing I did was authorize the app when I installed it. So it's clearly possible. I'm just trying to figure out what I might be missing from my add-on that will allow me to do the same.
Here is a small, sample I put together just to test the 'write' of an array of 2 columns and 2 rows out to A1:B2 in the currently active sheet. It works from both the menu and the sidebar as the developer. It always works from the menu as a domain user, but will 'only' work from the sidebar if the developer has direct permissions to the file (either via a one-off share or via being created in a previously shared folder the developer has the appropriate permissions to).
// scopes
https://www.googleapis.com/auth/script.container.ui
https://www.googleapis.com/auth/spreadsheets
// code.gs
function onOpen(e) {
SpreadsheetApp.getUi().createAddonMenu()
.addItem('Sidebar Test', 'showSidebar')
.addItem('setValues Test', 'setValuesTest')
.addToUi();
}
function showSidebar() {
var htmlTemplate = HtmlService.createTemplateFromFile('sidebar');
var ui = htmlTemplate.evaluate()
.setTitle('Sidebar Test')
SpreadsheetApp.getUi().showSidebar(ui);
}
function onInstall(e) {
onOpen(e);
}
function setValuesTest() {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var valArr = [];
valArr.push([1,2]);
valArr.push([3,4]);
sheet.getRange(1, 1, 2, 2).setValues(valArr);
} catch (e) {
throw new Error(errorMessage({e: e}));
}
}
function errorMessage(params) {
return params.e.message + '<br/><div class="main">file: '+
params.e.fileName+'<br/>line: '+ params.e.lineNumber + '</div>';
}
function getVersion() {
var scriptProps = PropertiesService.getScriptProperties();
return scriptProps.getProperty('Version');
}
// utility.gs
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
// sidebar.js.html
<script>
$(function() {
$('#run-query').click(runQueryButton);
});
function runQueryButton() {
runQuery(this);
}
function runQuery(element) {
element = (element === 'undefined') ? this : element;
element.disabled = true;
$('#error').remove();
google.script.run
.withSuccessHandler(
function(msg, element) {
element.disabled = false;
})
.withFailureHandler(
function(msg, element) {
showError(msg, $('#main'));
element.disabled = false;
})
.withUserObject(element)
.setValuesTest();
google.script.host.editor.focus();
}
function showError(msg, element) {
var div = $('<div id="error" class="error">' + msg + '</div>');
$(element).after(div);
}
</script>
// sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<?!= include('sidebar.css'); ?>
</head>
<body>
<div class="sidebar branding-below">
<div id="main" >
<button class="blue cursor-pointer" id="run-query" data-toggle="modal">Execute</button>
</div>
</div>
<div class="sidebar bottom">
Version: <?!= getVersion(); ?>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<?!= include('sidebar.js'); ?>
</body>
</html>
// sidebar.css.html
<style>
.error {
padding: .25em .25em;
font-family: Arial,sans-serif;
}
</style>
Update 2
I changed the deployment type to 'Only Trusted Testers', and added my non-Domain self as that tester. I sent my(other)self the link to the app and installed it from there. Answered the authorization pop-up (though, since I'm not in the domain, I had to go the 'unsafe' route, which is fine). Ran the test add-on...and it worked like a champ! Now, both myself (non-domain) and other, developer self (domain) have paid $5. Is that the issue? Is the issue that the test user I created 'in' the domain hasn't paid any money, so it won't work?
That doesn't make much sense to me, as the test add-on installs, and the test user (in the domain) can call the underlying script from a 'menu', and it populates the spreadsheet just fine. It only breaks down for the domain user when they try to run the app from the side bar, which calls the code with a google.script.run call. This is really, really weird, and frustrating.
On another side note, I added a function to show the 'effective user' on the side bar, just in case there was something hinky happening on that front, and that seems all well and good, reporting the name of the test user in the domain and not the name of the developer in the domain.
I guess my next testing will be to:
Flip it back to domain, reinstall for the domain test user, see if something else didn't happen to fix it.
If still broken, flip it back to trusted testers, add the domain test user to that group, pay another $5, and see if they can install and run it in that fashion. If they can, then there is something either flawed with domain publishing of add-ons, or I'm missing some documentation (or something undocumented).
google.script.run will always run as the effective user of the add-on (which may or may not be the developer).
However if the user is not granted permission to edit the spreadsheet you're going to have errors.
You can try setting up a team-drive for your domain where documents can be easily shared with users in the team. Alternatively you can create a shared folder (with edit rights) for all the users in your domain to store documents that should be accessible by all users in said domain.

Button doesn't work on google spreadsheet

I am working to link an image in my Google Sheet document to a specific cell in another tab. I'm doing this by building a simple function that will do this. However, when I assign the function and then click on the image, I then get the error :
Script function "test" could not be found
When I run the function in the script manager interface, it works fine. It's when I try to actually use it in the sheet with the image.
Function Script :
function test()
{
Browser.msgBox("You clicked it!");
}
It turned out that the document owner had left their job and ownership rights had been moved to someone else. Can it matter ?
The error is : Here
Thank you very much,
Make sure the assigned function name does not include the () brackets. 😎
Did a little snippet just to demonstrate on how you might approach this:
I used the sample from HTML Service: Create and Serve HTML on creating a button (in your case it's image) which responds to a click event. I'm using a bound script.
//in bounds script, this integral function triggers as soon as you open the spreadsheet
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Dialog')
.addItem('Click Me', 'test')
.addToUi();
}
//Then I attached your test function
function test()
{
Browser.msgBox("You clicked it!");
}
And sure enough upon clicking the button, the test function triggered:

webkitNotifications - SECURITY_ERR: DOM Exception 18 - script, OK - button

I followed http://www.beakkon.com/tutorial/html5/desktop-notification tutorial for html 5 desktop notifications. The demo on that page work for me. If i copy entire code it works so, but... when i call the method from javascript it don't display niether the notification or permision request. Instead it raises SECURITY_ERR: DOM Exception 18.
It seems the error is raised by the line which creates the notification itself.
Has anybody glue why button works and calling the function directly does not?
My current code:
function RequestPermission(callback)
{
window.webkitNotifications.requestPermission(callback);
}
function notif() {
if (window.webkitNotifications.checkPermission() > 0) {
RequestPermission(notif);
}
notification = window.webkitNotifications.createHTMLNotification('http://localhost:3000/images/rails.png');
notification.show();
}
Does not compute:
notif();
Computes:
<button onclick="notif()">NOTIFY</button>
Google Chrome: 9.0.597.84 (Oficiální sestavení 72991)
WebKit: 534.13
SECURITY_ERR: DOM Exception 18 is valid if the user hasn't allowed your request to have notifications.
The reason why this is happening is simply because requestPermission is asynchronous. Once the user clicks on Allow, for permission to be granted, it will then allow you to use HTML5 notifications feature.
In your case, your not waiting for the user to click on Allow button, it is automatically trying to create the HTML5 notification without evening waiting for their confirmation. If you rearrange your conditionals, it should work.
function RequestPermission(callback) {
window.webkitNotifications.requestPermission(callback);
}
function notif() {
if (window.webkitNotifications.checkPermission() > 0) {
RequestPermission(notif);
} else {
notification = window.webkitNotifications.createHTMLNotification('http://localhost:3000/images/rails.png');
notification.show();
}
}
As you notice above, place the notification creation in the conditional statement, when a callback gets fired it will be guaranteed to have permission.
I believe the createHtmlNotification accepts only one parameter, and that is to be
a url to an HTML document.