This is the function I have written, which sends an email to notify someone whenever a specified cell/row/column is edited, and I have it set to trigger onEdit. It works as is.
/* This function send an email when a specified range is edited
* The spreadsheets triggers must be set to onEdit for the function
*/
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Get Active cell
var mycell = ss.getActiveSelection();
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
//Define Notification Details
var recipients = "me#email.com";
var subject = "Update to "+ss.getName();
var body = ss.getName() + "has been updated. Visit " + ss.getUrl() + " to view the changes.";
//Check to see if column is A or B to trigger
if (cellcol == 1, 2)
{
//check for row to trigger
if (cellrow == 1)
{
//Send the Email
MailApp.sendEmail(recipients, subject, body);
}
//End sendNotification
}
}
My issue is that I need this to work only when a column or row on a certain sheet(page) of the spreadsheet is edited, rather than column X in any of the sheets within the spreadsheet.
Any ideas? I'm hoping it's something simple that I missed, but I have been unable to find a solution.
You could interrupt the function after assigning the sheet variable:
if (sheet.getSheetName() != 'SheetIWant') return;
You need to look at the parameters that onEdit provides. It will tell you where the change happened.
Related
I am trying to figure out which trigger to use to send an email (from a template) per row:
function sendConfirmationEmail(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = e.range;
var row = range.getRow();
var dataRange = sheet.getRange(row, 1, 1, 5).getValues();
// Test
console.log('Data: ' + e + 'range ' + dataRange);
}
A/ I am trying to add a column F with sendEmail and somehow trigger the function sendConfirmationEmail(e) in the script
B/ Trying to figure out if I can use the onhover email (column E) and when clicking Send email my emailTemplate.doc will be populated and replace the {{placeholder}} default message.
Any tips, suggestions?
To trigger a function on editing a cell content - use the onEdit trigger
This trigger contains the event object e.range that allows you to access the cell that has been edited.
Given that sending an email is an action that requires authorization, you need to use an installable instead of simple onEdit trigger
This you can do by calling your function something different from the key word onEdit (this is already the case in your function) and bind a trigger to it as explained here
Sample function to bind on installable onEdit trigger:
function sendConfirmationEmail(e) {
var range = e.range;
var sheet = range.getSheet();
var row = range.getRow();
var column = range.getColumn();
// for column F
if(column ==6 && e.value == "sendEmail"){
var dataRange = sheet.getRange(row, 1, 1, 5).getValues().flat();
var email = dataRange[4];
GmailApp.sendEmail(email, "subject", "body");
}
}
I have a spreadsheet on Google spreadsheets that has "D2" through all of "D" (because it will expand as used and when I add more) with a drop down box for priority status. The drop down has "Low, Medium, High". I want to get a script to send me an email when that priority gets edited to "High"
This is the script I did last night at 2AM half asleep.
{
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("TC52 Bugs or Issues and Improvements.");
var valueToCheck = sheet.getRange(D).getValue();
var rangeEdit = e.range.getA1Notation();
if(rangeEdit == "D")
{
if(valueToCheck > High)
{
MailApp.sendEmail("austin.hendrix#decathlon.com", "High Priority please check now.", "Check spreadsheet" + valueToCheck+ ".");
}
}
}````
This article has a tutorial.
On that note, something along the lines of (Heavily edited by MetaMan)
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange(1,4,sh.getLastRow()) or getRange('D1:D'+sh.getLastRow()));
var data = dataRange.getValues();
for (var i in data) {
var row = data[i];
var emailAddress = "youremail#email.com"; // First column
var message = "High priority set with data in A column "+sheet.getRange(("A"+i+1)).getValues()[0]; // Second column
var subject = 'High priority set in D'+i;
if(row[0]=="High"){
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
The function will send you an email when column D is edited to "High".
You will need to fill in 'Your Spreadsheet name' the recipient email address and what ever subject you wish. Also you can edit the body as well. The current body just tells you which row issue the email. Alternately, if you wish to provide more information I'll do it for you.
function onMyEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Your Sheet Name' && e.range.columnStart == 4 && e.range.rowStart >1 && e.value == 'High') {
GmailApp.sendEmail('recipient','subject',`row ${e.range.rowStart} has been set to high`)
}
}
Since sending an email requires permission you will have to create an installable trigger for this function.
You can do it manually or use a function like the one below which will prevent you from creating more that one trigger.
function createTrigger() {
const ts = ScriptApp.getProjectTriggers().map(t=>t.getHandlerFunction());
if(!~ts.indexOf('onMyEdit')) {
ScriptApp.newTrigger('onMyEdit').forSpreadsheet(SpreadsheetApp.getActive().getId()).onEdit().create();
}
}
ScriptApp
Please Note: that you cannot run onEdit triggered functions from the Script Editor or even from a menu. They require the onEdit trigger which populates the single parameter e with the event object. You can run them from another function as long as it provides the event object.
I have a installable time driven trigger with 30 minutes interval in Google Sheet.
The Trigger will send alert if there is any pending task.
The trigger works fine when the Google Sheet is open.
But the problem is when the Google Sheet is not open the Trigger is fetching the Old Data and that is causing False Alert.
The pending task data is calculation using IMPORTRANGE function from another sheet.
The another sheet is linked with Google Form where the sheets get updates by user's output.
So if anyone can help the App Script Code which will refresh all the data of the sheet event the sheet is not opened and then the trigger will do the job.
Note: The Source file and the Destination files are set for Update automatically on change and every minute.
I have posted this to Google App Script Bug Report Tracker and they have suggested to ask help here. You can check that using the Link: https://issuetracker.google.com/issues/168644365
My Code:
function AutoTelegramReminder() {
var SpreadsheetId = 'My_SpreadSheetId';
var SheetName = 'Pending Details';
var ss = SpreadsheetApp.openById(SpreadsheetId)
var sheet = ss.getSheetByName(SheetName);
//Select the column we will check for the first blank cell
var columnToCheck = sheet.getRange("D:D").getValues();
// Get the last row based on the data range of a single column.
var lastRow = getLastRowSpecial(columnToCheck);
if (lastRow>1){
var SlipNo = sheet.getRange(2, 4, lastRow-1).getValues();
//getRange(row, column, numRows)
//Send alert to Telegram
var botSecret = "My_Bot_Secret"; //KCSL BOT ID
var chatId = "My_Chat_Id"; //M.B.D Group ID
var Url = "https://api.telegram.org/bot";
var Link = "My_Google_Sheet_Link_For_Users";
var whatsapLink = "My_Whatsapp_Link";
var body = "MY_TEXT" + SlipNo + "MY_TEXT " + whatsapLink + "MY_TEXT";
var response = UrlFetchApp.fetch( Url + botSecret + "/sendMessage?text=" + encodeURIComponent(body) + "&chat_id=" + chatId + "&parse_mode=HTML&disable_web_page_preview=TRUE");
}
}
//Function To Get Last Row
function getLastRowSpecial(range){
var rowNum = 0;
var blank = false;
for(var row = 0; row < range.length; row++){
if(range[row][0] === "" && !blank){
rowNum = row;
blank = true;
}else if(range[row][0] !== ""){
blank = false;
};
};
return rowNum;
};
If you want to get the data refreshed then in place of =importrange try using the script to call for sheets by using SpreadsheetApp.openById('Sheet Id') and then fetch the range using getRange().getValues().
Say I have a spreadsheet in google sheets with three columns EMAIL, TOPIC and TIME where I´d like to send an email to each of the EMAILs in the spreadsheet containing a link to a google form that has a question asking the preferred TIME for the given TOPIC in the spreadsheet. Is it doable to create such individualized google forms based on sheets?
The first thing that should be done is creating the forms and sending the emails. To do that, I wrote a function that loops through all the rows in your sheet (called "Sheet1", change it according to your preferences), creates a form for each row and sends it to the emails found in column A (in the sheet I've been working on, data starts at row 2 and columns are: A - email / B - topic / C - time):
function sendMails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var values = ss.getSheetByName("Sheet1").getDataRange().getValues();
for(var i = 1; i < values.length; i++) {
var email = values[i][0];
var topic = values[i][1]
var formName = email + " - " + topic;
var form = FormApp.create(formName);
var url = form.getPublishedUrl();
form.setTitle(formName);
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId()); // This line bounds the created form to your spreadsheet so that responses will be written in here, in an automatically created sheet
// You can set another destination spreadsheet if you don't want all these sheets created in your spreadsheet
var question = form.addDateTimeItem();
question.setTitle("Please provide a preferred time for your assigned lecture on " + topic);
var userEmail = form.addCheckboxItem();
userEmail.setChoices([userEmail.createChoice(email)]);
var topicName = form.addCheckboxItem();
topicName.setChoices([topicName.createChoice(topic)]);
var checkBoxValidation = FormApp.createCheckboxValidation()
.requireSelectExactly(1)
.build();
userEmail.setValidation(checkBoxValidation);
topicName.setValidation(checkBoxValidation);
MailApp.sendEmail(email, topic, url);
}
}
Next, you need to install an onFormSubmit trigger in your spreadsheet. You can do this to run a function that will write the preferred TIME the user chose in the form every time a form is submitted. To create the trigger, run this function in your script, only once:
function createOnFormSubmitTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('writeTime')
.forSpreadsheet(ss)
.onFormSubmit()
.create();
}
Finally, below is the function that the trigger trigs when a form is submitted. It looks for the row where topic and email match the ones coming from the form, and sets the time:
function writeTime(e) {
var response = e.values;
var time = response[1];
var email = response[2];
var topic = response[3];
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var values = sheet.getDataRange().getValues();
for(var i = 1; i < values.length; i++) {
if(values[i][0] == email && values[i][1] == topic) {
sheet.getRange(i + 1, 3).setValue(time);
}
}
}
I hope this is useful to you.
SpreadsheetApp.getActiveSheet() will always return the first sheet rather than the active sheet which is most likely a bug in new sheets.
function testActiveSheet(){
var ssCentral = SpreadsheetApp.openById("xxxxxxxxxxx");
var ssCentralName = ssCentral.getName();
SpreadsheetApp.setActiveSpreadsheet(ssCentral);
Logger.log('the ssCentralName is ' + ssCentralName);
var currentActiveSheet = ssCentral.getActiveSheet();
var currentActiveSheetName = currentActiveSheet.getName();
Logger.log('the currentActiveSheetName is ' + currentActiveSheetName);
}
despite the active sheet is not the first one, it still returns the first one.
If the script is stand-alone and not bound to a specific spreadsheet, an active Sheet would not exist. This is not a bug but expected behaviour. To expand an active sheet is what a user is looking at, not what the script is currently working in; or as said in the official documentation, "the active sheet in a spreadsheet is the sheet that is being displayed in the spreadsheet UI."
If the script is not bound then there is no sheet being displayed to any user and therefore the function just returns the first sheet! Hope that makes sense, let me know if you have any follow-up questions.
See here for details.
in response to #Chris
this is the SCRIPT B
function callActivateNewMember(){
LibraryName.activateNewMember()
}
this is the standalone SCRIPT A
function activateNewMember(){
// DECLARE THE CURRENT ACTIVE WORKSHEET as ssCentral
var ssCentral = SpreadsheetApp.openById("xxxxxxxxxxxxxx");
// check if TeamList sheet is active - if not land on Teamlist and ask to select a
new member by his/her first name
// goto and activate TeamList
var teamListSheet = ssCentral.getSheetByName('TEAM LIST');
var teamListSheetIndex = teamListSheet.getIndex()-1;
Logger.log('the teamListSheetIndex is ' + teamListSheetIndex);
var currentSheet = SpreadsheetApp.getActiveSheet().getName();
Logger.log('the currentSheet is ' + currentSheet);
if(currentSheet !='TEAM LIST'){
ssCentral.setActiveSheet(ssCentral.getSheets(
[teamListSheetIndex]).getRange('B5').activate();
Logger.log('WRONG TABLE');
return;
}
var teamListSheetLastRow = teamListSheet.getLastRow();
var nberRows = teamListSheetLastRow-7
// CHECK IF THE CELL IS IN THE FIRST COL AND IS NOT EMPTY
// ------------------get the current row of the member selected
var MemberRow = ssCentral.getCurrentCell().getRow();
var MemberCol = ssCentral.getCurrentCell().getColumn();
if(MemberRow <8 ||MemberCol>1 ){
Logger.log('RIGHT TABLE - WRONG CELL SELECTED');
return;
}
var memberCellValue = ssCentral.getCurrentCell().getValue();
if(memberCellValue=='' ){
Logger.log('CELL SELECTED EMPTY');
return;
}
// VALIDATION : IS THIS MEMBER WAS ALREADY ACTIVATED
//-----------get the column with “Reporting Activated” as header
var repActiveCol = teamListSheet.getRange("D7").getColumn();
//-----------check if the cell of “Reporting Activated” is Y
var checkY = teamListSheet.getRange(MemberRow, repActiveCol).getValue();
//-----------If checkY = “Y” then Alert OK
if (checkY == "Y") {
Logger.log('MEMBER WAS ALREADY ACTIVATED');
return;
}
// rest of the function
}
after considering the answers from Chris I decided to change the strategy
I now call the function in the standalone SCRIPT along with 2 parameters I catch from the SCRIPT bound to the Spreadsheet: ActiveSheet + ActiveCell.
then I return an error message based on the situation and display alert based on the situation.
it works now but I am not totally satisfied as I wanted to show the most limited code in the SCRIPT bound to the Spreadsheet.
thanks again to Chris for taking the time to explain to me the issue I faced. if you pass by Bali - Indonesia just let me know I'd be glad to have a coffee with you. We have a dev center here.
If your gs script was created from within a Google Sheet you can use
var sheet = SpreadsheetApp.getActiveSheet();
to get the currently selected sheet