Exception: Cannot call SpreadsheetApp.getUi() from this context. (line 1) - google-apps-script

I have a project with two scripts, that is bound to a Google spreadsheet.
The first script opens the UI
var ui = SpreadsheetApp.getUi();
var userProperties = PropertiesService.getUserProperties();
const TOKEN_OLD = 'api.token_old';
function onOpen(){
ui.createMenu('CREDENZIALI')
.addItem('Imposta TOKEN', 'setToken')
.addToUi();
}
function setToken(){
var scriptValue = ui.prompt('Per favore inserisci il token.' , ui.ButtonSet.OK);
userProperties.setProperty(TOKEN_OLD, scriptValue.getResponseText());
}
And the second one does the business logic and implements a custom function that is meant to be used into the spreadsheet.
function getFromApi(book_id,dato) {...}
Obviously, this API is behind authentication and I don't want to hardcode a token into the script, so I've created the first UI in order to let the end-user manage it at runtime.
The UI works flawlessly but when I try to invoke the custom function, I get this error:
I am the owner of the files. What am I missing ?

Try to add SpreadsheetApp.getUi(); inside onOpen() and setToken() separately.
The following script works as standalone:
var userProperties = PropertiesService.getUserProperties();
const TOKEN_OLD = 'api.token_old';
function onOpen(){
SpreadsheetApp.getUi()
.createMenu('CREDENZIALI')
.addItem('Imposta TOKEN', 'setToken')
.addToUi();
}
function setToken(){
var ui = SpreadsheetApp.getUi();
var scriptValue = ui.prompt('Per favore inserisci il token.' , ui.ButtonSet.OK);
userProperties.setProperty(TOKEN_OLD, scriptValue.getResponseText());
}
However, you can not use a custom formula to call this function. See related articles:
Custom Functions in Google Sheets
Custom function throws a "You do not have the permission required to setValue" error

Related

GoogleJsonResponseException: API call with error: Login Required

I made a script using the advanced service API Google Drive. It's working fine when we stay into the app script GUI :
/** #OnlyCurrentDoc */
function GetListOfDrivesName() {
const ALL_AVALIBLE_DRIVE = Drive.Drives.list();
return ALL_AVALIBLE_DRIVE.items.map(driveData => driveData = driveData.name)
}
However, when it's called from cell into Google Sheet we got an error
Error message is like below : GoogleJsonResponseException: API call to drive.drives.list failed with error: Login Required.
I guess it's some authentification & authorisation to ask before using the sheet.
Unfortuantly I have no idea how to request that ! I read doc but it's sound like it will be asked promptly.
In addition, trigger have been added for this function but it neither worked
Any idea ? Thanks in advance !
Hi #Yuri Khristich you were right, using ui is a good work around , code turn up to work corectly
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
ui.createMenu('[FM Script for G Drive]')
.addItem('test','showAvaillableDrive')
.addToUi();
}
function showAvaillableDrive(){
var ui = SpreadsheetApp.getUi();
var resultat = ui.prompt(GetListOfDrivesName())
}
function GetListOfDrivesName() {
const ALL_AVALIBLE_DRIVE = Drive.Drives.list();
return ALL_AVALIBLE_DRIVE.items.map(driveData => driveData = driveData.name)
}
As suggested by the comments you can build a menu over the Sheet and run it to write down Drive files that I own.
I have this custom Sheet where you can run the "Drive" function over "Adv Menu"
I automatically get the information of the Sheet and get a list of my Drive files:
function onOpen () {
var ui= SpreadsheetApp.getUi();
ui.createMenu('Adv Menu').addItem('Drive', 'getMyFilesFromDrive').addToUi();
}
function getMyFilesFromDrive() {
var myFiles = DriveApp.searchFiles('"me" in owners');
var sheet = SpreadsheetApp.getActive().getSheetByName("Files");
sheet.clear();
var rows = [];
rows.push(["ID", "Name", "Url"]);
while(myFiles.hasNext()) {
var file = myFiles.next();
if(file != null) {
rows.push([file.getId(), file.getName(), file.getUrl()]);
}
}
sheet.getRange(1,1,rows.length,3).setValues(rows);
}
It would also write it directly to my Sheet. Feel free to review it and use it.
You can also Draw a button (Inserting a Google Drawing) over the Sheet and assign a script:
Reference:
https://developers.google.com/apps-script/guides/menus

Displaying a prompt and getting user input in Google Apps script bound to a Google Sheet

I have an Google sheet with some apps script code in it and I am trying to make it interactive by displaying a prompt and getting some user input. I have added a simple function to test that this works:
function displayPrompt() {
var ui = SpreadsheetApp.getUi();
var result = ui.prompt("Please enter a name for your report:");
Logger.log(result.getResponseText());
};
When I run it, I get the error:
"Exception: Cannot call SpreadsheetApp.getUi() from this context."
What am I doing wrong please?
Thanks Annette
If I run it using a trigger it works as soon as I open the file
function onOpen() {
var ui = SpreadsheetApp.getUi();
var result = ui.prompt("Please enter a name for your report:");
Logger.log(result.getResponseText());
}

How to save variable once every time sheet is open in google apps script

I'm running a script using Google Apps Script and the Spreadsheet Service. I created a custom menu using the onOpen() function, and added a button which triggers another function, obtainPriority().
Inside obtain priority I'm doing something time consuming which I realized needs to be done only once for every time someone opens a document. So, I've tried the following code:
var priorLists = {};
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Name1')
.addItem('Name2', 'obtainPriority')
.addToUi();
priorLists = doSomethingHeavy();
}
and then, in obtain priority:
function obtenerPrioridad() {
/*
Some code goes here
*/
var result = randomFunction(priorLists)
/*
Some code goes here
*/
}
However, randomFunction seems to receive an empty Object. Is this correct? What is the correct way to proceed to define an object only onced and then reuse it?
As mentioned by #cooper, you can use PropertiesService to save your object. However, please do keep in mind that the value associated with the given key in the current Properties store should be in type string.
Sample Code:
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Name1')
.addItem('Name2', 'obtainPriority')
.addToUi();
var priorLists = doSomethingHeavy();
Logger.log(priorLists);
//Save the priorLists in the properties service
var scriptProperties = PropertiesService.getScriptProperties();
var strVal = JSON.stringify(priorLists);
scriptProperties.setProperty("list", strVal);
}
function doSomethingHeavy(){
//sample object
var result = {type:"Fiat", model:"100", color:"white"};
return result;
}
function obtenerPrioridad() {
var scriptProperties = PropertiesService.getScriptProperties();
var priorLists = JSON.parse(scriptProperties.getProperty('list'));
Logger.log(priorLists);
Logger.log(typeof priorLists);
}
What it does?
Get the script properties using PropertiesService.getScriptProperties()
Convert priorLists object into a string using JSON.stringify(obj).
Save the converted string object to the script properties with key = 'list' using setProperty(key, value)
Get the saved string object in the script properties with key = 'list' using getProperty(key). Then convert the string back to an object using JSON.parse(str)
Output:
6:21:03 AM Notice Execution started
6:21:04 AM Info {type=Fiat, model=100, color=white}
6:21:04 AM Info object
6:21:04 AM Notice Execution completed

Google Sheets & Docs Script fails to create add-on menu when add-on is installed from Chrome store

Possible cause is the following:
Usually this is caused by a problem with the Authorization Lifecycle, specifically the opening stage.
The most common culprit is a global variable in the code that tries to access Google services without authorization, like:
var doc = DocumentApp.getActiveDocument();
See the documentation:
Warning: When your onOpen(e) function runs, the entire script is loaded and any global statements are executed. These statements execute under the same authorization mode as onOpen(e) and will fail if the mode prohibits them. This preventsonOpen(e) from running. If your published add-on fails to add its menu items, look in the browser's JavaScript console to see if an error was thrown, then examine your script to see whether the onOpen(e) function or global variables call services that aren't allowed in AuthMode.NONE.
Here is my script:
function onOpen(e) {
SpreadsheetApp.getUi().createAddonMenu()
.addItem('Browse Templates', 'browseTemplates')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function browseTemplates(){
collectBasicData();
// Display a modal dialog box with custom HtmlService content.
var htmlOutput = HtmlService
.createTemplateFromFile("Gallery").evaluate()
.setWidth(700)
.setHeight(510);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Spreadsheet123 - Template Vault');
}
function collectAllData(){
var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(DATA_SHEET);
DATA = sheet.getDataRange().getValues();
return DATA;
}
function collectBasicData(){
var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(PIVOT_SHEET);
var tabSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(TAB_SHEET);
BASIC_DATA = {
"tab_about" : getValue(tabSheet,"B1"),
"tab_help": getValue(tabSheet,"B2"),
"pivot":sheet.getDataRange().getValues()
};
return false;
}
function getValue(sheet,addr){
return sheet.getRange(addr).getValue().toString().replace(/^\s+|\s+$/g, '');
}
function createACopy(id){
var docName = DocsList.getFileById(id).getName();
return DocsList.getFileById(id).makeCopy(docName).getUrl();
}
function insertInCurrent(id){
var destinationSpreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheets = SpreadsheetApp.openById(id).getSheets();
for(var i=0;i<sourceSheets.length;i++){
var sheetName = sourceSheets[i].getName();
var source = SpreadsheetApp.openById(id).getSheetByName(sheetName);
source.copyTo(destinationSpreadSheet).setName(sheetName);
}
}
Can you please help me a little or a lot.
Thanks in advance
OK, so my code was actually correct, but my mistake was that I should have saved any changes made to my code under the new version before publishing it to the store, which I did not and therefore all changes that I made were simply ignored.
function onOpen(e) {
SpreadsheetApp.getUi().createAddonMenu()
.addItem('Browse Templates', 'browseTemplates')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}

Authorization Lifecycle on published add-on is not working

Have a problem with my add-on that should, but not creating a menu items in Add-ons on Google Sheets and Docs on install from the store until the page is refreshed.
According to Google Support I should look in to the following:
"Usually this is caused by a problem with the Authorization Lifecycle, specifically the opening stage.
The most common culprit is a global variable in the code that tries to access Google services without authorization, like:
var doc = DocumentApp.getActiveDocument();
See the documentation:
Warning: When your onOpen(e) function runs, the entire script is loaded and any global statements are executed. These statements execute under the same authorization mode as onOpen(e) and will fail if the mode prohibits them. This preventsonOpen(e) from running. If your published add-on fails to add its menu items, look in the browser's JavaScript console to see if an error was thrown, then examine your script to see whether the onOpen(e) function or global variables call services that aren't allowed in AuthMode.NONE."
I have no idea what and how should I deal with this as my add-on was not created by me. I can do minor things, but this is something that I cannot handle on my own and really need your help please.
Hear is my script:
function onOpen(e) {
SpreadsheetApp.getUi().createAddonMenu()
.addItem('Browse Templates', 'browseTemplates')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function browseTemplates(){
collectBasicData();
// Display a modal dialog box with custom HtmlService content.
var htmlOutput = HtmlService
.createTemplateFromFile("Gallery").evaluate()
.setWidth(700)
.setHeight(510);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Spreadsheet123 - Template Vault');
}
function collectAllData(){
var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(DATA_SHEET);
DATA = sheet.getDataRange().getValues();
return DATA;
}
function collectBasicData(){
var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(PIVOT_SHEET);
var tabSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(TAB_SHEET);
BASIC_DATA = {
"tab_about" : getValue(tabSheet,"B1"),
"tab_help": getValue(tabSheet,"B2"),
"pivot":sheet.getDataRange().getValues()
};
return false;
}
function getValue(sheet,addr){
return sheet.getRange(addr).getValue().toString().replace(/^\s+|\s+$/g, '');
}
function createACopy(id){
var docName = DocsList.getFileById(id).getName();
return DocsList.getFileById(id).makeCopy(docName).getUrl();
}
function insertInCurrent(id){
var destinationSpreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheets = SpreadsheetApp.openById(id).getSheets();
for(var i=0;i<sourceSheets.length;i++){
var sheetName = sourceSheets[i].getName();
var source = SpreadsheetApp.openById(id).getSheetByName(sheetName);
source.copyTo(destinationSpreadSheet).setName(sheetName);
}
}
I have looked in to the documentation on Google, but cannot understand how and where I should use it in my script.
Your help is highly appreciated and thanks in advance