Google Sheets "You do not have permission to call appendRow" - javascript

function myFunction() {
var url = 'https://api.github.com/users/chaimf90/repos'
var response = UrlFetchApp.fetch(url);
var json = response.getContentText();
var data = JSON.parse(json)
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(['Repo Name', data[0].name]);
}
When I execute this function from the script editor it runs as expected, but when I try to call this function in the sheet itself by calling =myFunction(), I get an error saying that I do not have permission to call appendRow.
Why can I call this function from the script editor, but not from the sheet itself?

I had the same problem. The solution seems to be to create a custom menu that runs an Apps Script function instead of writing a custom function and invoking it from a cell in the spreadsheet.
A function that is triggered from a menu will ask the user for authorization if necessary and can consequently use all Apps Script services. I learned this here.
Example:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('My Custom Menu')
.addItem('Run My Function', 'myFunction')
.addToUi();
}
function myFunction() {
SpreadsheetApp.getUi()
.alert('Running My Function');
}
After writing and saving this code:
Close and re-open your google sheets document.
After a few seconds, a new menu "My Custom Menu" will appear at the
top, next to File, Edit, View, ..., Help.
Click on "My Custom Menu" and then click on "Run My Function" in order to invoke
the function myFunction.

Related

How to access data from an external spreadsheet with Google Apps Script

I have a spreadsheet with some data tables that I want to use in other spreadsheets script. How can I call this spreadsheet by its ID and access the data?\
From the documentation, I know that custom functions are not allowed to access other spreadsheets, a solution would be to put this function in a menu and run it, since it'd ask for the user's authorization. But this is not an option because I am using the data with the built-in method onEdit().\
Also, I tried accessing the spreadsheet via onOpen() since it's not a custom function but still no success. Any other solution?
My code:
function onOpen(){
ss = SpreadsheetApp.getActiveSpreadsheet();
src = SpreadsheetApp.openById("spreadsheetID");
Error message:
Exception: You do not have permission to call SpreadsheetApp.openById. Required permissions: https://www.googleapis.com/auth/spreadsheets
And appscript.json
{
"timeZone": "Europe/Paris",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets"
]
}
Lastly, I also tried creating an installable trigger with this code:
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ss)
.onOpen()
.create();
}
But the trigger does not seem to be called upon opening the sheet.
As you already realized, custom functions and simple trigger cannot perform requests that require authorization
SpreadsheetApp.openById("spreadsheetID"); is one of those requests
You are on the right track with the installable triggers - they can trigger the execution of requests that require authorization
I think your problem is the correct implementation of installable triggers
function createSpreadsheetOpenTrigger creates an installable onOpen trigger that calls the function myFunction when the sheet is open
This means that you need to create the function myFunction first
Also, you need to run function createSpreadsheetOpenTrigger() once manually - to install the trigger
Sample complete code:
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ss)
.onOpen()
.create();
}
function myFunction(){
ss = SpreadsheetApp.getActiveSpreadsheet();
src = SpreadsheetApp.openById("spreadsheetID");
Logger.log(src.getActiveSheet().getName());
}
Note:
Instead of implementing the function createSpreadsheetOpenTrigger(), you can install the trigger manually by going on Edit->My project's triggers and bind a trigger of the desired type to the desired function, see also Managing triggers manually
First, in the line src = SpreadsheetApp.openById("spreadsheetID"); did you replace the id with the actual spreadsheet ID?
That's the number at the end of the url. I assume you did, but it doesn't show that you did in your example.
Second, just for testing, try to do this action in a regular function (not a trigger) to get things to work. Have you done that? Then you can focus on the spreadsheet access without worrying if the trigger is causing a problem.

Open two urls with one click

I'm new to Google Sheets and Apps Script. I have a sheet with two URLS. In cell F1 is http://testurl1.com and in cell G1 is http://testurl2.com.
I would like to have a button or link or something in cell D1 that when I click it will open both of these links. I can do this manually with Alt-Enter but haven't been able to translate that to code.
I have been able to open both these urls from a menu item, but when I try calling the code from a cell it says
Exception: Cannot call SpreadsheetApp.getUi() from this context.
But the code works from a menu item. Weird. The code I'm currently trying to use is below but I am open to any suggestions!
function callOthers() {
myFunction()
Utilities.sleep(1500);
myFunction2()
}
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getRange("F1").getValue();
var html = "<script>window.open('" + selection + "');google.script.host.close();</script>";
var userInterface = HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModalDialog(userInterface, 'Open Tab');
}
function myFunction2() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection2 = sheet.getRange("G1").getValue();
var html2 = "<script>window.open('" + selection2 + "');google.script.host.close();</script>";
var userInterface2 = HtmlService.createHtmlOutput(html2);
SpreadsheetApp.getUi().showModalDialog(userInterface2, 'Open Tab');
}
Problem
Custom function is blocked from being run when used in a cell
Explanation
There are three main ways of making a bound script function available in the Spreadsheet UI:
As a custom function that can be used like a formula
As a menu item that will run the function on click
As a "button" created via image or drawing that will run the function on click
All three have different execution context and limitations on what they can and cannot access, the most restrictive being the first. Custom functions execution context is bound to the cell it is called in, so you cannot do anything that affects the UI as a whole, which getUi() allows to do.
Additionally, since showModalDialog is a method that requires authorization on behalf of the user, even if the getUi() method was available, you could not show the dialog due to the fact that custom functions never ask users to authorize access to personal data.
Solution
If you want to interact with UI, you should either create a menu or a button as mentioned before.
Please note that users will have to give your script their permission for the following scope:
https://www.googleapis.com/auth/script.container.ui
References
Custom functions guide
showModalDialog method reference
getUi() method reference

onOpen is not running to other users except to the owner. How to fix this? Google Script GAS

After refreshing the shared spreadsheet, onOpen is not running to other users who also have access.
But the owner is being able to run the codes and script.
Code.gs
function onOpen(e) {
test();
SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.
.createMenu('Custom Menu')
.addItem('First item', 'menuItem1')
.addToUi();
SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.
.createMenu('Custom Menu').addItem('Test', 'test').addToUi();
};
function test() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt('Spreadsheet Restriction', 'Enter password:', ui.ButtonSet.OK);
// Process the user's response.
var button = result.getSelectedButton();
var text = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
} else if (button == ui.Button.CLOSE) {
// User clicked X in the title bar.
ui.alert('Spreadsheet is protected.');
test();
}
};
function myFunction() {
}
Already shared the access spreadsheet and script to other users but they can't still see it is working.
Appreciate your help guys... Thanks!
The users of your Sheet/script must have edit access on the Sheets document in order for the onOpen() trigger to be executed. From the documentation:
They do not run if a file is opened in read-only (view or comment) mode.
In order to use the prompt() method within the onOpen() function, you must use an installable trigger. Furthermore, the trigger must be set up by the user who is going to use it. The same also applies to other functions of the UI class such as showModalDialog().
This strict rules are most likely enforced in order to protect the end user of potential scams. If you are interested in protecting your Sheets document with password, I suggest you check out other solutions such as this one,
Reference
Simple Triggers restrictions
Installable Triggers restrictions

Google Apps Script send email through spreadsheet not working

I have a problem with some Google Script stuff. Basically, my goal is to have the script check to see if a client's case was resolved and then send an email to them that the issue has been resolved. I've gotten the logic done on when to send an email, but every time I try and implement it into the spreadsheet, I get the error:
Error
You do not have permission to call MailApp.sendEmail. Required permissions: https://www.googleapis.com/auth/script.send_mail (line 8).
I've got a simple function to test the functionality of it, and when run in the script editor it works fine, but not on the spreadsheet. Here is my sample function:
function myFunction(row) {
var sheet = SpreadsheetApp.getActiveSheet();
var rng = sheet.getRange(row, 1, 1, 2);
var ara = rng.getValues();
var email = ara[0][0];
MailApp.sendEmail(email, "TEST", "This is a test of sendEmail().");
return "Email sent.";}
According to the Apps Script Custom Functions documentation:
If your custom function throws the error message You do not have permission to call X service., the service requires user authorization and thus cannot be used in a custom function.
To use a service other than those listed above, create a custom menu that runs an Apps Script function instead of writing a custom function. A function that is triggered from a menu will ask the user for authorization if necessary and can consequently use all Apps Script services.
Method 1
Basically, you can replicate the wanted behavior of the two functions above with this:
function SendEmail() {
var message = "This is your response";
var subject = "You have feed back in the parking lot";
var ss = SpreadsheetApp.getActiveSheet();
var textrange = ss.getRange("F2");
var emailAddress = ss.getRange("B2").getValue();
if (textrange.isBlank() == false)
MailApp.sendEmail(emailAddress, subject, message);
}
And in order to trigger the execution of this function, you can make use of Apps Script triggers and choose one which is the most convenient for your use-case.
Method 2
You can also create a custom menu and with the option of triggering the above function. You only need to add this:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("My Menu")
.addItem("Send Email", "SendEmail")
.addToUi();
}
And this is how it will look like on the Spreadsheet:
Reference
Apps Script Custom Functions;
Apps Script Range Class - isBlank();
Apps Script Custom Menus;
Apps Script Triggers.
I encountered the same problem today "You do not have permission to call MailApp.sendEmail".
I solved this by doing the next steps:
open "Tools" -> "Script editor"
in "Script editor" click on "View" -> "Show manifest file"
open the "appscript.json" file that appeared in the left section of your screen and add "https://www.googleapis.com/auth/script.send_mail" to the oauthScopes, like this:
{
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/script.send_mail"],
}
PS: I assigned the script to an image, which basically acts like a button.

pre-loading custom functions when running sheets daily script

I'm using the 'Cryptofinance' custom function in Google spreadsheets. I have written a custom script which runs daily using the trigger functionality of the app scripts.
function daily() {
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Liquidity");
var value = sh.getRange("B36").getValue();
var lastRow = whichRow();
lastRow += 1;
ss.getSheetByName("Liquidity over time").getRange("B" + lastRow).setValue(value);
ss.getSheetByName("Liquidity over time").getRange("A" + lastRow).setValue(new Date());
}
function whichRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var Avals = ss.getSheetByName("Liquidity over time").getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
return Alast;
}
Basically it should pick up the value from a field and add a row so I can run a chart on this column.
The output when I run it manually from the script editor can look like this;
1337999,52
The output when it gets run by the daily function looks like this:
#NAME?
It didn't help with the .flush() function and I haven't been able to understand the sheets lifecycle in combination with the custom function.
How can I make sure the sheet is pre-loaded before the script runs?
You need to open the Spreadsheet and then set it as active:
var ss = SpreadsheetApp.openById("1234567890");
SpreadsheetApp.setActiveSpreadsheet(ss);
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#setActiveSpreadsheet(Spreadsheet)
Short answer
On time-driven triggers avoid the use of call functions, even custom functions, that use getActiveSpreadsheet and other "get active" methods. Instead use openById or other similar methods.
Explanation
SpreadsheetApp.flush() ensures the pending changes made to the script are applied, so it doesn't make sense to put it as the first action of a function.
On the other hand, on Google Apps Script, the user that has opened the spreadsheet establish which spreadsheet is active, on time-driven triggers getActiveSpreadsheet returns null, in other words, we could say that "active" means "being used at this time by an user".

Categories

Resources