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]);
}
Related
I'm very new to script writing, but I'm trying to set up a script to send an email once a certain cell (A4) is populated within a certain sheet.
The trouble I'm having is getting the script to trigger - I understand that running it when not in the spreadsheet generates an error, I get no error message, but no email appears to be sent using this code:
function onEdit(e) {
var name = SpreadsheetApp.getActiveSpreadsheet().getName();
var cell = e.range.getA1Notation();
var spreadSheet = e.source;
var sheet = spreadSheet.getSheetByName('Scanned');
var range = getRange('A4');
var column = e.range.getColumn();
var row = e.range.getRow();
if(sheet == 'Scanned' && column == 1 && row == 4)
MailApp.sendEmail('joey#gmail.com', 'Tipping Notification: '+(name), 'Notification email body '+(name));
}
An onEdit trigger always requires a 'manual' edit of the spreadsheet. If the spreadsheet(cell) is populated by a formula, it will not fire the onEdit trigger.
Also, in order to send emails, you'll have to use an installable onEdit trigger. Simple onEdit triggers cannot access services that require authorization.
There are some restrictions for using simple trigger, One is simple trigger cannot access services that require authorization. For example, a simple trigger cannot send an email because the Gmail service requires authorization.
In order to use services that requires authorization, you need to create an Installable trigger. See this link on how to create Installable Trigger.
Also, getSheetByName() returns a Sheet Object and not the name of the edited Sheet. This causes your code to skip sending email.
Try this:
Code:
function onEditA4(e) {
var editedSheet = e.range.getSheet().getName();
var editedCell = e.range.getA1Notation();
var name = e.source.getName();
if(editedSheet == 'Scanned' && editedCell == "A4")
MailApp.sendEmail('insert email here', 'Tipping Notification: '+(name), 'Notification email body '+(name));
}
Example Trigger:
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
}
I need a solution to enable all editors to leave their name stamps after they make changes in a sheet. I was able to come up with script code which does work ONLY FOR OWNER.
I tried to authorize the script by running the script manually from editors accounts - the app has the authorization but even though it doesn't work for Editors.
Norbert Wagner: "However authorizing from the script editor did not work for me. But as soon as I added a menu in the document and executed the function from there, I got an authorization request in the document. From then on also the onEdit function works, for all users. I did this as the documents owner though. " -
maybe this is the solution? But how can I run the onEdit from the
document? Simple edit doesn't work, but how about this menu? Is there
a way to execute ALL SCRIPTS AND FORCE AUTHORIZATION from document level?
function onEdit(e) {
// Your sheet params
var sheetName = "Arkusz1";
var dateModifiedColumnIndex = 3;
var dateModifiedColumnLetter = 'C';
var range = e.range; // range just edited
var sheet = range.getSheet();
if (sheet.getName() !== sheetName) {
return;
}
// If the column isn't our modified date column
if (range.getColumn() != dateModifiedColumnIndex) {
var row = range.getRow();
var user = Session.getActiveUser().getEmail();
user = String(user);
user = user.replace('#gmail.com', '');
var dateModifiedRange = sheet.getRange(dateModifiedColumnLetter + row.toString());
dateModifiedRange.setValue(user);
};
};
This function works only when using owner account.
Even after Editor has authorised script manually from script editor - the onEdit user stamp function doesn't trigger.
Below is another function which I used for testing. When I run it manually from editors account IT WORKS - it saves editor's name in A1.
function USERNAME(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0]; // this will assign the first sheet
var cell = sheet.getRange('A1');
var d = new Date();
d.setHours(d.getHours() +1);
var user = Session.getActiveUser().getEmail();
user = String(user);
user = user.replace('#gmail.com', '');
cell.setValue(user);
}
In fact you have to use installable trigger.
Rename your function onEdit(e) with another name for example customFunction(e)
Then you setup an installable trigger using this function :
function createTrigger(){
ScriptApp.newTrigger('customFunction')
.forSpreadsheet(SpreadsheetApp.openById('YOU_SHEET_ID'))
.onEdit()
.create();
}
Or you can setup the trigger manually by going to 'Edit >> Current project's triggers >> Click on + button (bottom right)'
By this way trigger will run each time there is an Edit no matter the user.
Stéphane
ERROR : You do not have permission to call ScriptApp.newTrigger. Required permissions: https://www.googleapis.com/auth/script.scriptapp.
Having permission problem to create time based trigger
function onEdit(e){
var sheetName = e.range.getSheet().getName()
if(sheetName == "Config")
{
if(e.range.getRow()==2 && e.range.getColumn()==1){
createSpreadsheetOpenTrigger()
}
}
}
function createSpreadsheetOpenTrigger() {
Logger.log("hello")
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ss)
.onOpen()
.create();
}
For Simple Triggers, just running the script within the App Script Web IDE is enough and should prompt you with an authentication popup: Simple Auth Steps.
However, to programmatically create new Triggers you need to make sure the onEdit Trigger is Installable! This gives you increased permissions to do what you need.
We need to open up the dev console to set up an installable trigger.
We need to create a new trigger tied to our function. For installable triggers, it is best not to use the default simple trigger onEdit() function name.
Done! We should be able to run our functions based on triggers with increased permission scopes.
I have a function that is intended to detect changes in one spreadsheet and copy them into another spreadsheet in the same exact cell location if the change is made in sheet1. I first tried to do this using the installed onEdit(e) function provided by sheetsAPI, but kept running into the error that I'm not authorized to open a new spreadsheet from a built-in function. I tried setting up my own trigger, but it won't activate even though I assigned an OnEdit trigger to the function.
function ChangeDetect(e){
var range = e.range
var esheet = SpreadsheetApp.getActiveSheet()
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheets()[1];
// literally, if the edit was made in the first sheet of the spreadsheet, do the following:
if (esheet.getIndex() == ss.getSheets()[0].getIndex()){
var numrows = range.getNumRows();
var numcols = range.getNumColumns();
for (var i = 1; i <= numrows; i++) {
for (var j = 1; j <= numcols; j++) {
var currentValue = range.getCell(i,j).getValue();
var cellRow = range.getCell(i,j).getRow();
var cellCol = range.getCell(i,j).getColumn();
var ss3 = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1K2ifzjPTATH77tV4xInh0Ga2SuPsLdgNRSbekjDx-w8/edit#gid=0')
var sheet3 = ss3.getSheets()[0];
sheet3.getRange(cellRow,cellCol).setValue(currentValue)
}
}
}
}
I think you may be experiencing the limitation/restrictions of installable triggers:
Restrictions
Because simple triggers fire automatically, without asking the user for authorization, they are subject to several restrictions:
The script must be bound to a Google Sheets, Docs, or Forms file.
They do not run if a file is opened in read-only (view or comment) mode.
They 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.
They can modify the file they are bound to, but cannot access other files because that would require authorization.
They may or may not be able to determine the identity of the current user, depending on a complex set of security restrictions.
They cannot run for longer than 30 seconds.
In certain circumstances, add-ons for Google Sheets, Docs, and Forms run - their onOpen(e) and onEdit(e) simple triggers in a no-authorization mode that presents some additional complications. For more information, see the guide to the add-on authorization lifecycle.
These restrictions do not apply to doGet() or doPost().