I am trying to email on edit, but it is not working due to the fail code listed in the body of this - email

This should be an easy one to fix and it'll probably be a duh moment, so here it is. I have this google script written to send an email if a certain cell is a certain value. It works just fine when "run". I want it to run onEdit, but I get this notification when it fails:
TypeError: Cannot call method "getActiveSheet" of undefined. (line 3, file "Original Copy to make it send if b29 is less than 100")
Original Code:
function email(e)
{
var sheet = e.SpreadsheetApp.getActiveSheet();
if (sheet.getName() == "Sheet1") {
var activeCell = sheet.getRange("E1");
if (activeCell.getA1Notation() == "E1") {
if (activeCell.getValue() <100)
{
MailApp.sendEmail("#gmail.com", "subject", "message")
}
}}}
Looking forward to that easy answer that I can't seem to find! Thanks in Advance!
EPR

Just remove the e.
var sheet = SpreadsheetApp.getActiveSheet();

This seems to work the best.
I have a trigger set up using the google script trigger "button" to push this everytime the sheet edits.
function emailonEdit(e)
{
var sheet = SpreadsheetApp.getActiveSheet();
if (sheet.getName() == "Sheet1") {
var activeCell = sheet.getRange("E1");
if (activeCell.getA1Notation() == "E1") {
if (activeCell.getValue() <100)
{
MailApp.sendEmail("#gmail.com", "freezer", "temp check for new script")
}
}}}

Related

OnEdit only triggering when manually editing a cell

I'm now able to only have an email sent when a cell in one sheet has something added to it and not when that thing is deleted. However, it only triggers an email when I manually add a value and not when a value is added via the appsheet it's linked to. I know it's possible because when I was playing around with it yesterday I could get emails to trigger from my phone via the appsheet, but now it only works if I type something random in a cell and press enter. I just can't see what might be wrong with this. Can anyone please help? Thank you!
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet4=ss.getSheetByName('Copy');
var emailAddress = sheet4.getRange(2,12).getValue();
var subject = sheet4.getRange(2,13).getValue();
var message = sheet4.getRangeList(['G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8'])
.getRanges()
.map(range => range.getDisplayValue())
.join('\n');
MailApp.sendEmail(emailAddress, subject, message);
}
function onEdit(e) {
if (e.source.getActiveSheet().getName() === `Trigger`) {
if (e.range.rowStart >= 1 && e.range.columnStart >= 1) {
if (`value` in e) sendEmail()
}
}
}
With AppSheet 'edits' you will have to use onChange().
Try:
function onChange(e) {
if (e.source.getActiveSheet().getName() === `Trigger`) {
if (e.source.getActiveRange().getRow() >= 1 && e.source.getActiveRange().getColumn() >= 1) {
if (e.source.getActiveRange().getValue() !== ``) sendEmail()
}
}
}
Note: This is specified to only work on an individual cell edit.
Let me know if this works for your AppSheet!
See:
Event Objects | onChange

Email notification when changes are made to specific data in Google sheets

I have a general script that will send email notification of changes made to a live roster in Google sheets and have set up a trigger for the event to occur 'On change'. All data in the sheet is retrieved via IMPORTRANGE. This script is performing as expected, however I wish to make a couple of changes that am not sure how to go about. Here is a sample sheet: link to sheet
Wish to only send email notification when changes concern the name 'Craig'. For example my name is either added or taken off a rostered day. The script currently sends emails for all changes made across the sheet.
ID the row that this change occurs so as to reference the date and venue in the email body.
Thanks for any help and suggestions.
Full script:
function onEdit() {
var getProps = PropertiesService.getUserProperties();
var lenProp = getProps.getProperty('Len');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Roster');
var data = sheet.getRange('E4:ACB562').getValues().toString().length;
if (data != lenProp) {
getProps.setProperty('Len', data );
MailApp.sendEmail('email#gmail.com', 'Changes have been made to your live roster', 'Previous value: ' + lenProp + ' New value: ' + data);
}
}
New revised script:
function installedOnEdit(e) {
var range = e.range;
var value = range.getValue();
if (value == "Craig" || (e.oldValue == "Craig" && value == "")) {
var rowNumber = range.getRow(); // You can retrieve the row number of the editor cell.
console.log(rowNumber)
var [colA, colB] = e.source.getActiveSheet().getRange(rowNumber, 1, 1, 2).getValues()[0];
var getProps = PropertiesService.getUserProperties();
var lenProp = getProps.getProperty('Len');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Roster_LIVE');
var data = sheet.getRange('E4:ACB562').getValues().toString().length;
if (data != lenProp) {
getProps.setProperty('Len', data);
MailApp.sendEmail('email#gmail.com', 'Changes have been made to your roster!', 'Your live roster has been edited. These changes concern ' + colB + ' on ' + colA);
}
}
}
I believe your goal as follows.
You want to send an email using OnEdit trigger when the value of Craig is put to a cell.
You want to retrieve the edited row number.
You want to run the script when the value of Craig is removed.
Modification points:
From I have a general script that will send email notification of changes made to a live roster in Google sheets and have set up a trigger for the event to occur 'On change'., I understood that you are using the installable OnChange trigger. If it's so, in your situation, I would like to recommend to use the installable OnEdit trigger.
And, when I saw your script, the function name is onEdit. If the installable OnEdit trigger installs to onEdit, when a cell is edited, onEdit is run 2 times by the simple trigger and the installable trigger with the asynchronous process. Ref So, please rename the function name from onEdit to other and reinstall the installable OnEdit trigger to the renamed function name.
In order to retrieve the row number of the edited cell, you can use the event object.
When above points are reflected to your script, it becomes as follows.
Modified script:
After the script was modified, please install the OnEdit trigger to the function of installedOnEdit. In this script, when you put the value of Craig to a cell, the script below the if statement is run.
From:
function onEdit() {
var getProps = PropertiesService.getUserProperties();
var lenProp = getProps.getProperty('Len');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Roster');
var data = sheet.getRange('E4:ACB562').getValues().toString().length;
if (data != lenProp) {
getProps.setProperty('Len', data );
MailApp.sendEmail('email#gmail.com', 'Changes have been made to your live roster', 'Previous value: ' + lenProp + ' New value: ' + data);
}
}
To:
function installedOnEdit(e) {
var range = e.range;
var value = range.getValue();
if (value == "Craig" || (e.oldValue == "Craig" && value == "")) {
var rowNumber = range.getRow(); // You can retrieve the row number of the editor cell.
console.log(rowNumber)
var getProps = PropertiesService.getUserProperties();
var lenProp = getProps.getProperty('Len');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Roster');
var data = sheet.getRange('E4:ACB562').getValues().toString().length;
if (data != lenProp) {
getProps.setProperty('Len', data);
MailApp.sendEmail('email#gmail.com', 'Changes have been made to your live roster', 'Previous value: ' + lenProp + ' New value: ' + data);
}
}
}
In this modified script, when the value of Craig is put to a cell, the script below the if statement is run. If you want to also check the column and row, please tell me.
References:
Installable Triggers
Event Objects
This sheet does not get edited, but rather changes occur through IMPORTRANGE.
No events get triggered when a formula result changes, so I do not think that you can get an automatic notification when values get updated in the imported data.
To make it work, you will have to use a trigger in the source spreadsheet rather than the target spreadsheet.

How to run script if a google sheets cell contains email?

I'm looking for help to send an email whenever a new row is added by a google form entry if said entry contains an email in the Email column. I'm new to Javascript, but I've pieced together some code which I plan to run off an onEdit trigger in GSheets.
My problem is that if there is no email address, the code will fail. I need to know how to wrap this in an "if/else" or maybe just a simple error handling bit would be fine, not sure.
If I go with an "if/else", I'll need to check if the email column contains a value. I don't need to check if it is a valid email; the google form already does this on submission.
Here is the code I have right now:
function MessageNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Message Board"));
//
//extracts the values in last row and stores them into a two-dimensional
array called data
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(lastRow,3,1,8);
var data = dataRange.getValues();
//
//pull column elements into a one-dimensional array called rowData
for (i in data) {
var rowData = data[i];
var emailAddress = rowData[2];
var poster = rowData[7];
var subject = rowData[3];
var recipName = rowData[6];
var comment = rowData[4];
var replyLink = rowData[5];
//
//
var message = 'Dear ' + recipName + ',\n\n'+poster+' has posted the
following comment directed to you: '+'\n'+comment+'\n\n'+'To reply to this
comment click: '+replyLink;
var subject = subject;
MailApp.sendEmail(emailAddress, subject, message);
}
}
thanks in advance for any help you can give me.
Thank you tehhowch for the help. I'm new at this so I'll have to continue researching the link you referred to regarding iteration best practice. However I was able to get this working with a simple 'if' wrapper, which turned out to be simpler than I thought.
I did find out that form submission does not recognize an active sheet, so manually testing my code worked, while form submission did not trigger it.
After some looking, I replaced:
var ss = SpreadsheetApp.getActiveSpreadsheet();
with this:
var ssID = '//insert spreadsheet id here';
var ss = SpreadsheetApp.openById(ssID);
This still did not work, so I had to kickstart it by deleting the trigger and putting it back in (found this info: On form submit trigger not working)
This may not be the most efficient code, but here is what I have now, and it does work:
function MessageNotification() {
var ssID = '//insert spreadsheet id here';
var ss = SpreadsheetApp.openById(ssID);
ss.setActiveSheet(ss.getSheetByName("Message Board"));
//extracts the values in last row and stores them into a two-dimensional
array called data
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(lastRow,3,1,8);
var data = dataRange.getValues();
//
//pull column elements into a one-dimensional array called rowData
for (i in data) {
var rowData = data[i];
var emailAddress = rowData[2];
var poster = rowData[7];
var subject = rowData[3];
var recipName = rowData[6];
var comment = rowData[4];
var replyLink = rowData[5];
//
//
var message = 'Dear ' + recipName + ',\n\n'+poster+' has posted the
following comment directed to you: '+'\n'+comment+'\n\n'+'To reply to this
comment click: '+replyLink;
var subject = subject;
if(emailAddress)
{
MailApp.sendEmail(emailAddress, subject, message);}
}
}
As mentioned in the question comments, you want to use the event object available to the on form submit trigger. This can be accessed from a container-bound script on either the form or its responses spreadsheet, simply by adding a parameter to the function that receives the trigger.
This object is of the form:
e: {
authMode: <enum>,
namedValues: {
'q1title': [ <q1string> ],
'q2title': [ <q2string> ],
...
},
range: <Range>,
triggerUid: <string>,
values: [<q1string>, <q2string>, ...]
}
Using this object means that accessing of the Spreadsheet, for the purposes of emailing someone based on contents of the form, is unnecessary.
function MessageNotification(e) {
if(!e) return; // No form event object was provided.
var responses = e.namedValues;
var emailQTitle = /* the title of the question that asks for the email */;
// Check that 1) this question exists in the response object, and also
// 2) it has an answer with a value that 3) is "truthy".
// https://developer.mozilla.org/en-US/docs/Glossary/Truthy
if(responses[emailQTitle] // 1
&& responses[emailQTitle].length // 2
&& responses[emailQTitle][0]) // 3
{
var emailAddress = responses[emailQTitle][0];
/* access the responses variable in a similar manner
for the other variables needed to construct the email */
MailApp.sendEmail(emailAddress, ... );
} else {
/* There was no response to the email question. */
// You can use View->Stackdriver Logging to inspect the form response, for
// example, to make sure that it had the format or values you expected.
console.log({form_object: e, responses: responses, emailTitle: emailQTitle});
}
}

Google form that turns on and off each day automatically

I love Google Forms I can play with them for hours. I have spent days trying to solve this one, searching for an answer. It is very much over my head. I have seen similar questions but none that seemed to have helped me get to an answer. We have a café where I work and I created a pre-order form on Google Forms. That was the easy part. The Café can only accept pre-orders up to 10:30am. I want the form to open at 7am and close at 10:30am everyday to stop people pre ordering when the café isn't able to deal with their order. I used the very helpful tutorial from http://labnol.org/?p=20707 to start me off I have added and messed it up and managed to get back to the below which is currently how it looks. It doesn't work and I can't get my head around it. At one point I managed to turn it off but I couldn't turn it back on!! I'm finding it very frustrating and any help in solving this would be amazing. To me it seems very simple as it just needs to turn on and off at a certain time every day. I don't know! Please help me someone?
FORM_OPEN_DATE = "7:00";
FORM_CLOSE_DATE = "10:30";
RESPONSE_COUNT = "";
/* Initialize the form, setup time based triggers */
function Initialize() {
deleteTriggers_();
if ((FORM_OPEN_DATE !== "7:00") &&
((new Date()).getTime("7:00") < parseDate_(FORM_OPEN_DATE).getTime ("7:00"))) {
closeForm("10:30");
ScriptApp.newTrigger("openForm")
.timeBased("7:00")
.at(parseDate_(FORM_OPEN_DATE))
.create(); }
if (FORM_CLOSE_DATE !== "10:30") {
ScriptApp.newTrigger("closeForm")
.timeBased("10:30")
.at(parseDate_(FORM_CLOSE_DATE))
.create(); }
if (RESPONSE_COUNT !== "") {
ScriptApp.newTrigger("checkLimit")
.forForm(FormApp.getActiveForm())
.onFormSubmit()
.create(); } }
/* Delete all existing Script Triggers */
function deleteTriggers_() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
/* Allow Google Form to Accept Responses */
function openForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(true);
informUser_("Your Google Form is now accepting responses");
}
/* Close the Google Form, Stop Accepting Reponses */
function closeForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(false);
deleteTriggers_();
informUser_("Your Google Form is no longer accepting responses");
}
/* If Total # of Form Responses >= Limit, Close Form */
function checkLimit() {
if (FormApp.getActiveForm().getResponses().length >= RESPONSE_COUNT ) {
closeForm();
}
}
/* Parse the Date for creating Time-Based Triggers */
function parseDate_(d) {
return new Date(d.substr(0,4), d.substr(5,2)-1,
d.substr(8,2), d.substr(11,2), d.substr(14,2));
}
I don't think you can use .timebased('7:00'); And it is good to check that you don't have a trigger before you try creating a new one so I like to do this. You can only specify that you want a trigger at a certain hour like say 7. The trigger will be randomly selected somewhere between 7 and 8. So you really can't pick 10:30 either. It has to be either 10 or 11. If you want more precision you may have to trigger your daily triggers early and then count some 5 minute triggers to get you closer to the mark. You'll have to wait to see where the daily triggers are placed in the hour first. Once they're set they don't change.
I've actually played around with the daily timers in a log by creating new ones until I get one that close enough to my desired time and then I turn the others off and keep that one. You have to be patient. As long as you id the trigger by the function name in the log you can change the function and keep the timer going.
Oh and I generally created the log file with drive notepad and then open it up whenever I want to view the log.
function formsOnOff()
{
if(!isTrigger('openForm'))
{
ScriptApp.newTrigger('openForm').timeBased().atHour(7).create()
}
if(!isTrigger('closeForm'))
{
ScriptApp.newTrigger('closeForm').timeBased().atHour(11)
}
}
function isTrigger(funcName)
{
var r=false;
if(funcName)
{
var allTriggers=ScriptApp.getProjectTriggers();
var allHandlers=[];
for(var i=0;i<allTriggers.length;i++)
{
allHandlers.push(allTriggers[i].getHandlerFunction());
}
if(allHandlers.indexOf(funcName)>-1)
{
r=true;
}
}
return r;
}
I sometimes run a log entry on my timers so that I can figure out exactly when they're happening.
function logEntry(entry,file)
{
var file = (typeof(file) != 'undefined')?file:'eventlog.txt';
var entry = (typeof(entry) != 'undefined')?entry:'No entry string provided.';
if(entry)
{
var ts = Utilities.formatDate(new Date(), "GMT-6", "yyyy-MM-dd' 'hh:mm:ss a");
var s = ts + ' - ' + entry + '\n';
myUtilities.saveFile(s, file, true);//this is part of a library that I created. But any save file function will do as long as your appending.
}
}
This is my utilities save file function. You have to provide defaultfilename and datafolderid.
function saveFile(datstr,filename,append)
{
var append = (typeof(append) !== 'undefined')? append : false;
var filename = (typeof(filename) !== 'undefined')? filename : DefaultFileName;
var datstr = (typeof(datstr) !== 'undefined')? datstr : '';
var folderID = (typeof(folderID) !== 'undefined')? folderID : DataFolderID;
var fldr = DriveApp.getFolderById(folderID);
var file = fldr.getFilesByName(filename);
var targetFound = false;
while(file.hasNext())
{
var fi = file.next();
var target = fi.getName();
if(target == filename)
{
if(append)
{
datstr = fi.getBlob().getDataAsString() + datstr;
}
targetFound = true;
fi.setContent(datstr);
}
}
if(!targetFound)
{
var create = fldr.createFile(filename, datstr);
if(create)
{
targetFound = true;
}
}
return targetFound;
}

Google Script - Move new submissions to another sheet based on the responses

I'm trying to create a script that will take a new form response and move it to another sheet based on the information submitted. For example, let's say the form has two answer choices A, B. The spreadsheet has three sheets; Form Responses, Sheet A, Sheet B. If someone submits the form and selects A, I need that new row to be moved from "Form Responses" to "Sheet A." I found someone else's script that does exactly this but using the OnEdit function. I cannot figure out how to modify this script to work when a new form response is submitted.
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "Form Responses" && r.getColumn() == 2 && r.getValue() == "A") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Sheet A");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
I used the installable triggers and replaced the OnEdit function with onFormSubmit but that doesn't work. I'd really appreciate it if anyone could help me with this.
Thanks,
To achieve what you want, you will need to:
Create a function write_to_new_sheet that we'll use in a trigger function whenever a new response hits the form. This function will take the form response as an event object e:
function write_to_new_sheet(e){
let responses = e.response.getItemResponses()
let new_row = get_new_response_data_as_row(responses)
let sheet_to_write = SpreadsheetApp.openById('your spreadsheet id').getSheetByName('sheet A') // or 'sheet B', you can set this dynamically by checking the new_row, corresponding to the response as a gsheet row
write_values_in_first_row(sheet_to_write, new_row)
}
this are the auxiliary functions to write_to_new_sheet:
function get_new_response_data_as_row(responses){
let new_row = []
responses.forEach(response => {
new_row.push(response.getResponse())
})
return new_row
}
function write_values_in_first_row(sheet, new_row_values){
let row_to_write_from = 2 // assuming you have a header
let sheet_with_new_row = sheet.insertRowBefore(row_to_write_from)
let number_of_rows = 1
let number_of_columns = new_row_values.length
let range = sheet_with_new_row.getRange(row_to_write_from, 1, number_of_rows, number_of_columns)
let results =range.setValues([new_row_values])
return new_row_values
}
Set up an installable trigger that works whenever you submit a new response to the form:
function setup_write_to_new_sheet_on_form_submit(){
ScriptApp.newTrigger('write_to_new_sheet')
.forForm('your form id goes here')
.onFormSubmit()
.create();
}
Run the above function once, to set up the trigger.
try submitting a new response on the form, and check the changes in the sheets you want it to be written.
Try something a little less broad in your comparing of variables,, For instance the sheet that submissions are sent to is a constant and already address.
function formSubmission() {
var s = SpreadsheetApp.getActiveSheet();
var data = range.getValues(); // range is a constant that always contains the submitted answers
var numCol = range.getLastColumn();
var row = s.getActiveRow;
var targetinfo = s.getRange(row,(Yourcolumn#here).getValue);
if(targetinfo() == "Desired Sheet Name") {
var targetSheet = ss.getSheetByName("Sheet A");
var targetrow = targetSheet.getLastrow()+1);
var Targetcol = numCol();
targetSheet.getRange(targetrow,1,1,Targetcol).setValues(data);
}
}
I didn't test that but hopefully it helps look up Event Objects in the developer guide i just recently found it and it clarifies a lot
the triggers can be set by going to:
then set it:
I have a spreadsheet that collects the form submissions and then has extra sheets that have filtered out these submission based on the answers. All this is just with formulas. Could that do the trick also?