My script onOpen is running only half of the script. When I try to run the scrip manually it works, but when I refresh the spreadsheet, it only changes the number to 5 in cell A10.
function onOpen() {
const ss = SpreadsheetApp.getActive();
const rok = ss.getSheetByName('ROK');
const id = ss.getSheetByName('ID');
const kontrola = rok.getRange('I4').getValue();
ss.getSheetByName('DATA').getRange('A10').setValue(5)
if (kontrola == 1) {
//iGV synergie copy
const term = rok.getRange('G3').getValue();
const name = ('iGV'+term);
const Synergie = id.getRange('C5').getValue();
const iGVSarchiveid = id.getRange('C3').getValue();
const iGVSarchive = DriveApp.getFolderById(iGVSarchiveid);
const iGVS = DriveApp.getFolderById(Synergie).getFilesByName('iGVxF&L');
const copyiGVS = iGVS.next().makeCopy(name);
const urlcopyiGVS = copyiGVS.getUrl();
DriveApp.getFilesByName(name).next().moveTo(iGVSarchive);
ss.getSheetByName('DATA').getRange('J3').setValue(urlcopyiGVS)
}
}
Issue:
The issue is that you are using a simple onOpen trigger which according to the official documentation has the following restriction which is relevant to your case:
Simple triggers cannot access services that require authorization. For
example, a simple trigger cannot send an email because the Gmail
service requires authorization, but a simple trigger can translate a
phrase with the Language service, which is anonymous.
Essentially the simple trigger can not access DriveApp because it requires authorization to do so.
Solution:
Create an installable onOpen trigger instead. Rename your function and use any name except for onOpen and then go to your current project's triggers and create an installable trigger for your function (in my example myFunction):
function myFunction() {
// code goes here
}
Related
I have created a simple OnOpen add-on in Google sheets that installs a trigger to send email based on Edit in the sheet. When I installed it, I authorized it from my account. The script is as follows:
function onOpen(e) {
var ui = SpreadsheetApp.getUi().createMenu("Install Trigger").addItem("Run", "initialize").addToUi();
}
const initialize = () => {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ScriptApp.newTrigger('sendEmail')
.forSpreadsheet(ss)
.onEdit()
.create();
};
The script runs fine and sends emails from my email address which I authorized. Now the issue is when I shared this sheet with another editor, the person does not need to install this trigger and he can send email from my email account without any authorization. Is there a way that we can make every editor in this sheet install their own trigger to send email from their email account instead of using mine?
You could use the method getUserTriggers(spreadsheet) that retrieves all installable triggers owned by the user executing the script:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var triggers = ScriptApp.getUserTriggers(ss);
// Log the event type for the first trigger in the array.
Logger.log(triggers[0].getEventType());
If this comes with a trigger, you know that the user has already installed the trigger.
If this comes empty, means that the user has no triggers in it. Then you could use the method deleteTrigger(trigger) to delete the existing triggers (created by others) in the script and then programatically create a new one:
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
how to get current list of sheet names automatically refresh by google app script when making new sheets or changing sheet name or duplicaing sheets or deleting sheets from google spread sheet
:::::: I need list of sheets name ::::::::::::
There are many sheets
New sheet will be added by other user
Name of new sheet will be changed by other user
some sheets will be deleted by other user
I need existing sheets name list not past
::::::::::::::::::::::::::::::::::::::::
and the list of sheet name should display on second sheet that code expression is sheet[1]
below code are working well. but it's not refresh by adding sheets or deleting sheets
function sheetnames()
{
return SpreadsheetApp.getActiveSpreadsheet().getSheets().map(function(x) {return x.getName();});
}
I believe your situation and goal as follows.
You are using the function of sheetnames() as the custom function on Google Spreadsheet.
You have already confirmed that your function of sheetnames() works.
You want to refresh the custom function when the sheet is deleted, inserted, copied and the sheet name is changed.
In order to achieve above, I would like to propose the following method.
Usage:
1. Prepare script.
In this case, the sample script for refreshing the custom function of sheetnames() in the Spreadsheet is run by the OnChange event trigger. For this, please copy and paste the following sample script to the container-bound script of Spreadsheet, and save the script.
function onChange(e) {
var lock = LockService.getDocumentLock();
if (lock.tryLock(10000)) {
try {
const prop = PropertiesService.getScriptProperties();
if ((e.changeType === "OTHER" || e.changeType === "REMOVE_GRID" || e.changeType === "INSERT_GRID") && !prop.getProperty("run")) {
const formula = "=sheetnames"; // <--- Please set the function name of the custom function.
const ss = e.source;
const tempFormula = "=sampleFormula";
ss.createTextFinder("^\\" + formula).matchFormulaText(true).useRegularExpression(true).replaceAllWith(tempFormula);
ss.createTextFinder("^\\" + tempFormula).matchFormulaText(true).useRegularExpression(true).replaceAllWith(formula);
prop.setProperty("run", "done");
} else {
prop.deleteProperty("run");
}
} catch(e) {
throw new Error(e);
} finally {
lock.releaseLock();
}
}
}
In order to avoid the duplicate run of the script, LockService is used.
In order to avoid the infinite loop of the trigger, PropertiesService is used.
2. Install OnChange event trigger.
In order to execute the function of onChange, please install the OnChange event trigger to the function onChange. You can see the method for installing this at this official document.
3. Testing
In order to test above script, after you installed the function onChange as the installable OnChange event trigger, for example, please insert new sheet. By this, you can confirm the custom function sheetnames() is refreshed.
References:
Installable Triggers
Class TextFinder
Lock Service
Class PropertiesService
I am trying to get a Google Apps Script to trigger on edit of a particular cell. Right now I have achieved this using a run-time trigger option built in Google Sheets. However, I lose the run-time trigger when I make a copy.
Hence, I have managed to find a programmable trigger from various sources and patched something together, but it is not working. I have no programming knowledge, so I am unable to understand where I am going wrong.
My objective is to run the script when user edits the Named Range "monthfilter".
With script trigger creation
function HideColumns() {
//open the current spreadsheet and get the value of the named range and trigger project
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("monthfilter").forSpreadsheet(ss).onedit()
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
My previous function:
function HideColumns() {
//open the current spreadsheet and get the value of the named range
var ss = SpreadsheetApp.getActive();
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
//show or hide February
if (namevalue == "February") {
var sheet = ss.getSheetByName("MIS");
sheet.hideColumns(30);
/** other stuff */
Not sure what is the 'runtime trigger' you mean, there are three kinds of trigger:
Simple trigger
Installable trigger
Time driven trigger
onEdit(e) is just a Simple trigger.
Let's take a look to the following situation:
yourMainSpreadsheet - (You are the owner)
|
binding script functions: - 1. function makeCopy( ) {...}
2. function monthFilter(e) {...}
3. function onEdit(e) { monthFilter(e); }
|
| After copying, new spreadsheet remains all functions and simple trigger.
V
newSpreadsheet- (You are the owner, maybe share with other editors.)
|
binding script functions: - 1. function makeCopy() {...}
2. function monthFilter(e) {...}
3. function onEdit(e) { monthFilter(e); }
No matter what kind of trigger you use or just manually run makeCopy() in yourMainSpreadsheet, onEdit(e) would be copied to new spreadsheet too, and it works when you or other editors make edits, you don't need to do anything:
function makeCopy(){
var mainSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSpreadsheet = mainSpreadsheet.copy('newSheet');
}
function monthFilter(e) {
var thisSheet = e.source.getActiveSheet();
var name = thisSheet.getRangeByName("rangeMonthfilter");
var nameValue = name.getValue();
//...
}
function onEdit(e) {
monthFilter(e);
}
What you have done is set a installable trigger to new spreadsheet.
Highlight of documentation about difference between them.
Installable triggers, however, offer more flexibility than simple
triggers: they can call services that require authorization ...
For example:
function monthFilter(e) {
var thisSheet = e.source.getActiveSheet();
var name = thisSheet.getRangeByName("rangeMonthfilter");
var nameValue = name.getValue();
// If you have operation like:
var onlyYouHavePermission = SpreadsheetApp.openById('xxxxxxxxx');
/* This is not allowed if you just use Simpler trigger onEdit(e) to
drive monthFilter(). */
}
For this, now is the time to use installable trigger:
function makeCopy(){
var mainSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSpreadsheet = mainSpreadsheet.copy('newSheet');
ScriptApp.newTrigger('monthFilter')
.forSpreadsheet(newSpreadsheet)
.onEdit()
.create();
/* The host of this trigger is you, it's like you
deploy a trigger to the newSpreadsheet, and make other editors
available to do something on "onlyYouHavePermission" through it.*/
}
Update
If all you want to do is just in HideColumns() you provide, you just need to add a new function which is a simpler trigger in script editor:
function onEdit(e) {
var ss = SpreadsheetApp.getActive();
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
if (namevalue == "February") {
/* If you copy the spreadsheet, of course including the sheets
inside it, and you share the permission of the new spreadsheet to
other editors, because they own the permission, authorization
is not required here.
*/
var sheet = ss.getSheetByName("MIS");
sheet.hideColumns(30);
}
}
I have written a custom function that calls the code below to create a temporary sheet. When I ran it in Google Editor I was asked for authorisation and granted access.
My custom function runs well in Google Editor but doesnt work in the spreadsheet and I get the error "You do not have the permission to call insertSheet".
I created the spreadsheet and custom function so I own (have access) to everything.
var tempSheetName = "temp";
var sp = SpreadsheetApp.getActiveSpreadsheet();
var activeCell = sp.getActiveCell();
sp.insertSheet(tempSheetName);
From Google Apps Guide : Link
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.
You may have your reasons, why you want to accomplish it in this way. But I'm really not sure if a custom function in a cell is the right place to create a new tab. You usually do calculations within a cell not document manipulations.
What, if you'd create a menu entry for the function like this and call it from there?
function onOpen() {
// instantiate UI object
var ui = SpreadsheetApp.getUi();
// create the menu
ui.createMenu("Functions")
.addItem("Create Sheet", "createSheet")
.addToUi();
}
function createSheet() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet(),
sheetName = 'mySheet',
sheet = ss.getSheetByName(sheetName);
// delete sheet if it already exists
if (sheet) ss.deleteSheet(sheet);
// insert sheet
ss.insertSheet(sheetName, 0);
}
Do you still get an error? Just curious...
I'm trying to write a script that display row value in a SpreadSheet file using UiApp.
And this script will need to update the data whenever there is a change to the SpreadSheet. This would normally be simple with just Trigger onChange.
However, the SpreadSheet itself is also updated by another function, and it seem that onChange is not triggered when the changes made are caused by another AppScript.
Is there an alternative way to achieve what I described?
Code below, thanks
var ss = SpreadsheetApp.openById("tqwJk280I1O_yW5oNX9nLQA");
function doGet() {
var app = UiApp.createApplication();
var masPanel = app.createFlowPanel().setId("panel");
var number = parseInt(ss.getRange("A1").getValue());
masPanel.add(app.createLabel(number));
masPanel.add(app.createFlowPanel().add(app.createFormPanel()
.add(app.createFlowPanel()
.add(app.createSubmitButton("Plus!")))));
app.add(masPanel);
return app;
}
function doPost()
{
var num = parseInt(ss.getRange("A1").getValue()) + 1;
ss.getRange("A1").setValue(num);
}
function onChange()
{
ss.getRange("A2").setValue("hahaha");
var app = UiApp.createApplication();
app.remove(app.getElementById("panel"))
var masPanel = app.createFlowPanel().setId("panel");
var number = parseInt(ss.getRange("A1").getValue());
masPanel.add(app.createLabel(number));
masPanel.add(app.createFlowPanel().add(app.createFormPanel()
.add(app.createFlowPanel()
.add(app.createSubmitButton("Plus!")))));
app.add(masPanel);
return app;
}
The onChange() function will not have reference to the UI. For example, if there are multiple people who have the same UI open, which one should be updated ?
So, the solution to your problem is for your UI code to poll the spreadsheet regularly and update the UI if the spreadsheet has changed. To do so, you can use the undocumented addTimer() method.
See this SO question & answer for a nice example