I'm a teacher and new to AppScript. I have created a spreadsheet to automatically send feedback to students via gmail from a google spreadsheet. It does work and the emails get sent from the spreadsheet but I get the following error 'Invalid Email <>' on the spreadsheet.
If anybody could help it'd be much appreciated.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menu = [{
name: "Send Email",
functionName: "uiSendEmail"
}];
ss.addMenu("Send Email", menu);
}
function uiSendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
range = range.offset(1, 0, range.getNumRows()-1);
range.getValues().forEach( function( recipient, index, data ){
var msgHtml = "Hi " + recipient[0] + "," + "<p>" + "Atttitude to Learning Grade is: " + "<b><u>" + recipient[5] + "</b></u>" + "<p>" + "Feedback: " + recipient[6] + "<p>" + "Improvements to be made: " + recipient[7] + "<p>" + "Overall Grade: " + recipient[8];
var subject = recipient[3] + " - Teacher Feedback";
var msgPlain = msgHtml.replace(/(<([^>]+)>)/ig, ""); // clear html tags for plain mail
GmailApp.sendEmail(recipient[0] + " " + recipient[1] + "<" + recipient[2] + ">", subject, msgPlain, { htmlBody: msgHtml });
});
}
This was from long ago, so I assume you figured something out... (I stumbled upon this trying to solve a related problem).
From your above code and the error, I can see the that the recipient array contains no data.
Related
I have a sheet that I would like to send a specific range (A1:O44) as a pdf document and leave out further information in other cells. Currently, it is all functioning to send these sheets and all is working fine, however, I cannot seem to get the script correct to limit it to a certain range in the sheet.
There are 2 tabs in the sheet and I would like both of those to be sent in one email as part of the same attachment (as is currently set up). The email is generated by clicking the submit button from the custom submit menu on the top:
https://docs.google.com/spreadsheets/d/1EFJKr9281PND_h8TXNeumaCXb8KWLJQw9Itn0i_AZmI/edit#gid=1494149448
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('SUBMIT')
.addItem('SUBMIT', 'menuItem1')
.addToUi();
}
function menuItem1() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
sendSheetToPdfwithA1MailAdress();
}
function sendSheetToPdfwithA1MailAdress(){ // this is the function to call
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("TEAM1"); // it will send sheet 0 which is the first sheet in the spreadsheet.
// if you change the number, change it also in the parameters below
var practiceName = sh.getRange('E2').getValue()
var startDate = sh.getRange('I4:L5').getValue();
var emailBody = sh.getRange('I45').getValue();
var staffName = sh.getRange('c43').getValue()
var shName = Utilities.formatString('%s %s',practiceName, startDate)
var message = "<HTML><BODY>"
+ "<P>Hi,"
+ "<br /><br />"
+ " Please find attached the payroll info for " + practiceName + " for the period " + startDate + "."
+ "<br /><br />"
+ "" + emailBody + "."
+ "<br /><br />"
+ " If you have any question please let me know"
+ "<br /><br />"
+ "Thanks, " + staffName + "."
+ "<br /><br />"
+ "</HTML></BODY>";
sendSpreadsheetToPdf(0, shName, sh.getRange('A46').getValue(),"Payroll Summary", " " + message + " ");
}
function sendSpreadsheetToPdf(sheetNumber, pdfName, email,subject, htmlbody) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("TEAM1");
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = spreadsheet.getId()
var sheetId = sheetNumber ? spreadsheet.getSheets()[sheetNumber].getSheetId() : null;
var url_base = spreadsheet.getUrl().replace(/edit$/,'');
var email2 = sh.getRange('A47').getValue();
var email3 = sh.getRange('A48').getValue();
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
+ (sheetId ? ('&gid=' + sheetId) : ('&id=' + spreadsheetId))
// following parameters are optional...
+ '&size=A4' // paper size
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=true&printtitle=false&pagenumbers=true' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
}
var response = UrlFetchApp.fetch(url_base + url_ext, options);
var blob = response.getBlob().setName(pdfName + '.pdf');
if (email) {
var mailOptions = {
attachments:blob, htmlBody:htmlbody, email2:email2
}
MailApp.sendEmail(
email,
subject+" (" + pdfName +")",
"html content only",
mailOptions);
MailApp.sendEmail(
email2,
subject+" (" + pdfName +")",
"html content only",
mailOptions);
MailApp.sendEmail(
email3,
subject+" (" + pdfName +")",
"html content only",
mailOptions);
}
}
Disclaimer: I don't believe the use of query parameters to control the PDF export is officially supported by Google, but this GitHub Gist from Spencer-Easton has the info you need to accomplish your goal.
The issue is in the query parameters stored in url_ext.
You must include the gid, which you can get from the sheet's URL from the sheet editor (/edit#gid=NUMBERS).
You need to add to url_ext:
gid=NUMBERS (look in URL to get numbers)
ir=false (unknown what it refers to)
ic=false (unknown what it refers to)
r1=0 (starting row, zero index)
c1=0 (starting col, zero index)
r2=43 (end row, note change of index)
c2=14 (end col, note change of index)
I added those and was able to email just the selected portions of the selected sheet.
In google sheets, I have it set it to when an user selects an option from a dropdown, it sends an email (from their email address) to the user who submitted that response on a form. This part works fine. The issue is that when an email is sent from the sheet, it sends an email from every user who authorized the script, not just the user that selected the option from the dropdown. I'm very green at this and not really sure how to fix it to where it only sends an email from the user who selected the option from the dropdown.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "Authorize", functionName: "createSpreadsheetEditTrigger"} ]
ss.addMenu("First Time Setup", menuEntries);
}
//** Creates onEdit trigger using current user as owner.
** If other onEdit triggers exist tied to this document & owner, delete them to avoid
duplicates.**//
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
var session = Session.getActiveUser().getEmail();
var triggers = ScriptApp.getUserTriggers(ss);
triggers.forEach(function(trigger){
if(trigger.getEventType() == ScriptApp.EventType.ON_EDIT){
scriptApp.deleteTrigger(trigger);
}
});
ScriptApp.newTrigger('SendEmail')
.forSpreadsheet(ss)
.onEdit()
.create();
ScriptApp.newTrigger('Denied')
.forSpreadsheet(ss)
.onEdit()
.create();
ScriptApp.newTrigger('Pending')
.forSpreadsheet(ss)
.onEdit()
.create();
}
Utilities.sleep(2000);
function SendEmail(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
Logger.log(Session.getActiveUser().getEmail());
try{
var triggers = ScriptApp.getUserTriggers(ss);
}catch(e){
return;
}
if(e.range.getColumn() == 22 && e.value == "Approved"){
{
var EMAIL_SENT = 'EMAIL_SENT';
var dt = e.range.offset(0,-21).getValue();
var ldc = e.range.offset(0,-19).getValue();
var status = e.range.offset(0,0).getValue();
var notes = e.range.offset(0,-1).getValue();
var cost = e.range.offset(0,-5).getValue();
var start = e.range.offset(0,-4).getValue();
var end = e.range.offset(0,-3).getValue();
var Iemail = e.range.offset(0,-20).getValue();
var Aemail = e.range.offset(0,4).getValue();
var name = e.range.offset(0,-18).getValue();
var issue = e.range.offset(0,-16).getValue();
var emailSent = e.range.offset(0,5).getValue();
var userName = Session.getActiveUser().getUsername();
if (emailSent !== EMAIL_SENT){
var subject = "Exception Bill Pre-Approval Status - Approved";
var body = "Hello," + "\n\r" + "The following request has been Approved:" + "\n\n" + "Submitted on: " + dt
+ "\n" + "Submitted by: " + name + "\n" + "LDC: " + ldc + "\n" + "Service Needed: " + issue + "\n" + "Estimated Cost:" + cost + "\n" + "Start Date: " + start + "\n" + "End Date: " + end + "\n" + "Notes: " + notes + "\n\n" + "Status: " + status + "\n\r" + "\n\r" + "Thanks," + "\n" + userName;
Utilities.sleep(2000);
GmailApp.sendEmail(Iemail, subject, body);
e.range.offset(0,5).setValue(EMAIL_SENT)
SpreadsheetApp.flush();
}
}
}
}
The email is being sent from every user because there is a trigger calling sendEmail created by each of them.
One solution is to add at the beginning of the function an if statement as follows:
if(e.user.getEmail() !== Session.getActiveUser().getEmail()) return;
I was able to build a script to send an email if a new row of data is inputted into google sheets and if Merge Status isn't "pass". However, the script sends out emails with multiple rows of data in one email that isn't "pass". How can I adjust the script to just use one row of data per email? (1 row of data = 1 email) Thanks in advance!
Google Sheets
function sendEmail() {
var ActiveSheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Pass");
var StartRow = 2;
var RowRange = ActiveSheet.getLastRow() - StartRow + 1;
var WholeRange = ActiveSheet.getRange(StartRow,1,RowRange,10);
var AllValues = WholeRange.getValues();
var message = "";
for (var i=0;i<AllValues.length;i++)
{
var CurrentRow = AllValues[i];
var EmailSent = CurrentRow[9];
if (EmailSent!="pass")
{
message +=
"<p><b>Name: </b>" + CurrentRow[2] + "</p>" +
"<p><b>Date: </b>" + CurrentRow[2] + "</p>" +
"<p><b>Referral Link: </b>" + CurrentRow[8] + "</p>" +
"<p><b>Signup Link: </b>" + CurrentRow[8] + "</p><br><br>";
var setRow = i + StartRow;
ActiveSheet.getRange(setRow, 10).setValue("pass");
var SendTo = CurrentRow[4] ;
var Subject = "[Action Required] - Virtual Interview Signup";
var CC = CurrentRow[7] ;
var ReplyTo = CurrentRow [7] ;
//send the actual email
MailApp.sendEmail({
to: SendTo,
cc: CC,
subject: Subject,
replyto: ReplyTo,
htmlBody: message,
});
}
}
}
on a successful mail sent inside your if() condition you can use break; which will break the flow go out of loop.
The "message" variable must be declared inside the for loop. Now you combine the new message with the old one in each iteration.
If you want to learn more, read about the scope of variables.
I have a sheet that I would like to send a specific range (A1:O44) as a pdf document and leave out further information in other cells. Currently, it is all functioning to send these sheets and all is working fine, however, I cannot seem to get the script correct to limit it to a certain range in the sheet.
There are 2 tabs in the sheet and I would like both of those to be sent in one email as part of the same attachment (as is currently set up). The email is generated by clicking the submit button from the custom submit menu on the top:
https://docs.google.com/spreadsheets/d/1EFJKr9281PND_h8TXNeumaCXb8KWLJQw9Itn0i_AZmI/edit#gid=1494149448
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('SUBMIT')
.addItem('SUBMIT', 'menuItem1')
.addToUi();
}
function menuItem1() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
sendSheetToPdfwithA1MailAdress();
}
function sendSheetToPdfwithA1MailAdress(){ // this is the function to call
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("TEAM1"); // it will send sheet 0 which is the first sheet in the spreadsheet.
// if you change the number, change it also in the parameters below
var practiceName = sh.getRange('E2').getValue()
var startDate = sh.getRange('I4:L5').getValue();
var emailBody = sh.getRange('I45').getValue();
var staffName = sh.getRange('c43').getValue()
var shName = Utilities.formatString('%s %s',practiceName, startDate)
var message = "<HTML><BODY>"
+ "<P>Hi,"
+ "<br /><br />"
+ " Please find attached the payroll info for " + practiceName + " for the period " + startDate + "."
+ "<br /><br />"
+ "" + emailBody + "."
+ "<br /><br />"
+ " If you have any question please let me know"
+ "<br /><br />"
+ "Thanks, " + staffName + "."
+ "<br /><br />"
+ "</HTML></BODY>";
sendSpreadsheetToPdf(0, shName, sh.getRange('A46').getValue(),"Payroll Summary", " " + message + " ");
}
function sendSpreadsheetToPdf(sheetNumber, pdfName, email,subject, htmlbody) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("TEAM1");
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = spreadsheet.getId()
var sheetId = sheetNumber ? spreadsheet.getSheets()[sheetNumber].getSheetId() : null;
var url_base = spreadsheet.getUrl().replace(/edit$/,'');
var email2 = sh.getRange('A47').getValue();
var email3 = sh.getRange('A48').getValue();
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
+ (sheetId ? ('&gid=' + sheetId) : ('&id=' + spreadsheetId))
// following parameters are optional...
+ '&size=A4' // paper size
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=true&printtitle=false&pagenumbers=true' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
}
var response = UrlFetchApp.fetch(url_base + url_ext, options);
var blob = response.getBlob().setName(pdfName + '.pdf');
if (email) {
var mailOptions = {
attachments:blob, htmlBody:htmlbody, email2:email2
}
MailApp.sendEmail(
email,
subject+" (" + pdfName +")",
"html content only",
mailOptions);
MailApp.sendEmail(
email2,
subject+" (" + pdfName +")",
"html content only",
mailOptions);
MailApp.sendEmail(
email3,
subject+" (" + pdfName +")",
"html content only",
mailOptions);
}
}
Disclaimer: I don't believe the use of query parameters to control the PDF export is officially supported by Google, but this GitHub Gist from Spencer-Easton has the info you need to accomplish your goal.
The issue is in the query parameters stored in url_ext.
You must include the gid, which you can get from the sheet's URL from the sheet editor (/edit#gid=NUMBERS).
You need to add to url_ext:
gid=NUMBERS (look in URL to get numbers)
ir=false (unknown what it refers to)
ic=false (unknown what it refers to)
r1=0 (starting row, zero index)
c1=0 (starting col, zero index)
r2=43 (end row, note change of index)
c2=14 (end col, note change of index)
I added those and was able to email just the selected portions of the selected sheet.
I've created a Google spreadsheet that includes some basic script which emails someone when a task to which they are assigned comes due. The script works on the original sheet but when I duplicate the sheet and change nothing but the dates the script no longer works.
I'm not receiving any errors, the emails simply are not being sent.
I'm very new to Google script and wasn't able to find a good answer for this. Does anybody know what the issue might be?
Thanks so much!
function myFunction() {
var date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
var FormattedTodayDate = Utilities.formatDate(date,'GMT-0600','MM/dd/yyyy')
//Here we get all the spreadsheets from the spreadsheet app
var spreadSheets = SpreadsheetApp.getActiveSpreadsheet();
//Here we pick out the first spreadsheet. 0 is the first tab of the spreadsheet "Active Sheet"
var currentSheet = spreadSheets.getSheets()[0];
//Here we set up where on the sheet we want to get the data
var startRow = 3;
var numRows = currentSheet.getLastRow()-1;
var numCols = currentSheet.getLastColumn();
//We use those numbers to find a range throughout the table (A2 - H* etc) where we want to grab the data from
var dataRange = currentSheet.getRange(startRow, 1, numRows, numCols);
//Using the data range, grab all the values
var data = dataRange.getValues();
for(i in data){
var row = data[i];
var reminderDate = new Date(row[1]);
var dueDate = new Date(row[3]);
var FormattedReminderDate = Utilities.formatDate(reminderDate,'GMT-0600', 'MM/dd/yyyy')
var FormattedDueDate = Utilities.formatDate(dueDate,'GMT-0600', 'MM/dd/yyyy')
if(row[0] === ''){
// Logger.log(FormattedTodayDate);
//
// Logger.log(FormattedReminderDate);
//
// Logger.log(FormattedReminderDate == FormattedTodayDate);
if(FormattedReminderDate == FormattedTodayDate){
MailApp.sendEmail({
to: row[7],
subject: row[4] + " task due on " + FormattedDueDate,
htmlBody: "The following " + row[4] + " task is coming due on <b>" + FormattedDueDate + "</b> : <br/> <br>" + row[5] + "<br> </br>Go to Google Spreadsheet for more details: https://docs.google.com/spreadsheets/d/1jIBX5By1jhro1V-gHXPPlhIOXDLut6CMvaq92uW5bFw/edit#gid=0&range=" + row[8] + "<br/><br> Please remember to add the word 'done' to the 'done?' column when you complete this task.",
})
}
if(FormattedDueDate == FormattedTodayDate){
MailApp.sendEmail({
to: row[7],
subject: row[4] + " task due TODAY",
htmlBody: "The following " + row[4] + " task has not been completed and is due TODAY: <br/> <br>" + row[5] + "<br> </br>Go to Google Spreadsheet for more details: https://docs.google.com/spreadsheets/d/1jIBX5By1jhro1V-gHXPPlhIOXDLut6CMvaq92uW5bFw/edit#gid=0&range=" + row[8] + "<br/><br> Please remember to add the word 'done' to the 'done?' column when you complete this task.",
})
}
}
}
}
Thanks everyone. Turns out this issue was caused by our spam filter blocking the notifications.