dwyl / learn-to-send-email-via-google-script-html-no-server

:email: An Example of using an HTML form (e.g: "Contact Us" on a website) to send Email without a Backend Server (using a Google Script) perfect for static websites that need to collect data.
GNU General Public License v2.0
3.15k stars 910 forks source link

Concatenating Name and String to make a custom subject for each email. #418

Closed sgebr01 closed 2 years ago

sgebr01 commented 2 years ago

I'm trying to concatenate the name of the person that is submitted in the form and my own string( - form submitted), to make a custom subject for each email(John Doe - Form Submitted). However, when I tried it myself with the code that is placed below, it didn't work, the name wasn't inserted in the subject at all. Here's the code I was using.

if (sendEmailTo) {
      MailApp.sendEmail({
        to: String(sendEmailTo),
        subject: String(mailData.name) + " - Form Submitted" ),
        replyTo: String(mailData.email), 
        htmlBody: formatMailBody(mailData, dataOrder)
      }); 

Why isn't it working? Thanks.

nelsonic commented 2 years ago

@sgebr01 did you forget to include the } to terminate the if statement above? make sure it's there in your code. 💭

sgebr01 commented 2 years ago

@nelsonic Yes, I did, I just forgot to put it in the post. I've also tried concatenating the two in a variable and then passing them as the subject but that didn't work either. Here is the code.


    var sendEmailTo = (typeof TO_ADDRESS !== "undefined") ? TO_ADDRESS : mailData.formGoogleSendEmail;

    var emailSubject = String(mailData.name) + " - Take Action Form Submitted"

    // send email if to address is set
    if (sendEmailTo) {
      MailApp.sendEmail({
        to: String(sendEmailTo),
        subject: String(emailSubject),
        replyTo: String(mailData.email), 
        htmlBody: formatMailBody(mailData, dataOrder)
      });
    }
nelsonic commented 2 years ago

Hmm, that code looks like it should work. 💭 Consider sharing more of your code (temporarily) so we can help debug.

sgebr01 commented 2 years ago

Here is all of of my code on Google App Scripts, I did change the email it is correct in the actual program..

/******************************************************************************
 * This tutorial is based on the work of Martin Hawksey twitter.com/mhawksey  *
 * But has been simplified and cleaned up to make it more beginner friendly   *
 * All credit still goes to Martin and any issues/complaints/questions to me. *
 ******************************************************************************/

// if you want to store your email server-side (hidden), uncomment the next line
var TO_ADDRESS = "support@nonProfit.com";

// spit out all the keys/values from the form in HTML for email
// uses an array of keys if provided or the object to determine field order
function formatMailBody(obj, order) {
  var result = "";
  if (!order) {
    order = Object.keys(obj);
  }

  // loop over all keys in the ordered form data
  for (var idx in order) {
    var key = order[idx];
    result += "<h4 style='text-transform: capitalize; margin-bottom: 0'>" + key + "</h4><div>" + sanitizeInput(obj[key]) + "</div>";
    // for every key, concatenate an `<h4 />`/`<div />` pairing of the key name and its value, 
    // and append it to the `result` string created at the start.
  }
  return result; // once the looping is done, `result` will be one long string to put in the email body
}

// sanitize content from the user - trust no one 
// ref: https://developers.google.com/apps-script/reference/html/html-output#appendUntrusted(String)
function sanitizeInput(rawInput) {
   var placeholder = HtmlService.createHtmlOutput(" ");
   placeholder.appendUntrusted(rawInput);

   return placeholder.getContent();
 }

function doPost(e) {

  try {
    Logger.log(e); // the Google Script version of console.log see: Class Logger
    record_data(e);

    // shorter name for form data
    var mailData = e.parameters;

    // names and order of form elements (if set)
    var orderParameter = e.parameters.formDataNameOrder;
    var dataOrder;
    if (orderParameter) {
      dataOrder = JSON.parse(orderParameter);
    }

    // determine recepient of the email
    // if you have your email uncommented above, it uses that `TO_ADDRESS`
    // otherwise, it defaults to the email provided by the form's data attribute
    var sendEmailTo = (typeof TO_ADDRESS !== "undefined") ? TO_ADDRESS : mailData.formGoogleSendEmail;

    var emailSubject = String(mailData.name) + " - Take Action Form Submitted"

    // send email if to address is set
    if (sendEmailTo) {
      MailApp.sendEmail({
        to: String(sendEmailTo),
        subject: String(emailSubject),
        replyTo: String(mailData.email), 
        htmlBody: formatMailBody(mailData, dataOrder)
      });
    }

    return ContentService    // return json success results
          .createTextOutput(
            JSON.stringify({"result":"success",
                            "data": JSON.stringify(e.parameters) }))
          .setMimeType(ContentService.MimeType.JSON);
  } catch(error) { // if error return this
    Logger.log(error);
    return ContentService
          .createTextOutput(JSON.stringify({"result":"error", "error": error}))
          .setMimeType(ContentService.MimeType.JSON);
  }
}

/**
 * record_data inserts the data received from the html form submission
 * e is the data received from the POST
 */
function record_data(e) {
  var lock = LockService.getDocumentLock();
  lock.waitLock(30000); // hold off up to 30 sec to avoid concurrent writing

  try {
    Logger.log(JSON.stringify(e)); // log the POST data in case we need to debug it

    // select the 'responses' sheet by default
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    var sheetName = e.parameters.formGoogleSheetName || "responses";
    var sheet = doc.getSheetByName(sheetName);

    var oldHeader = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var newHeader = oldHeader.slice();
    var fieldsFromForm = getDataColumns(e.parameters);
    var row = [new Date()]; // first element in the row should always be a timestamp

    // loop through the header columns
    for (var i = 1; i < oldHeader.length; i++) { // start at 1 to avoid Timestamp column
      var field = oldHeader[i];
      var output = getFieldFromData(field, e.parameters);
      row.push(output);

      // mark as stored by removing from form fields
      var formIndex = fieldsFromForm.indexOf(field);
      if (formIndex > -1) {
        fieldsFromForm.splice(formIndex, 1);
      }
    }

    // set any new fields in our form
    for (var i = 0; i < fieldsFromForm.length; i++) {
      var field = fieldsFromForm[i];
      var output = getFieldFromData(field, e.parameters);
      row.push(output);
      newHeader.push(field);
    }

    // more efficient to set values as [][] array than individually
    var nextRow = sheet.getLastRow() + 1; // get next row
    sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);

    // update header row with any new data
    if (newHeader.length > oldHeader.length) {
      sheet.getRange(1, 1, 1, newHeader.length).setValues([newHeader]);
    }
  }
  catch(error) {
    Logger.log(error);
  }
  finally {
    lock.releaseLock();
    return;
  }

}

function getDataColumns(data) {
  return Object.keys(data).filter(function(column) {
    return !(column === 'formDataNameOrder' || column === 'formGoogleSheetName' || column === 'formGoogleSendEmail' || column === 'honeypot');
  });
}

function getFieldFromData(field, data) {
  var values = data[field] || '';
  var output = values.join ? values.join(', ') : values;
  return output;
}
nelsonic commented 2 years ago

If you haven't already done it, consider adding some Logger statements to your doPost code to try and debug it:

// send email if to address is set
var emailData = {
    to: String(sendEmailTo),
    subject: String(mailData.name) + " - Take Action Form Submitted",
    replyTo: String(mailData.email), 
    htmlBody: formatMailBody(mailData, dataOrder)
}

Logger.log(emailData);

if (sendEmailTo) {
  MailApp.sendEmail(emailData);
}
sgebr01 commented 2 years ago

OK, I will try that out.

sgebr01 commented 2 years ago

I deployed it and then tried out the form(I wasn't expecting it to work, I was trying to get some debugging info) and then it worked for some reason. I'm not sure why this happened. I was able to get the name to be appended with the pre-written string. Isn't the point of loggers to debug, not to fix?

nelsonic commented 2 years ago

@sgebr01 sometimes just the act of logging something helps to see what the "problem" was ... 🔍 I could not see why the code wasn't working if it's any consolation, hence the Logger suggestion above. 💭

sgebr01 commented 2 years ago

How can I see what data the loggers returned now so that I can see why it wasn't working before?

nelsonic commented 2 years ago

https://developers.google.com/apps-script/guides/logging#stackdriver_logging

mckennapsean commented 2 years ago

with some of the tips, it sounds like the form started working for you (albeit, why we are not quite sure). is that right, @sgebr01 ?

did you learn anything else from the logger, or do you need any more assistance here? closing if things are otherwise good now :) thanks for posting all your findings so far!

sgebr01 commented 2 years ago

Yes, the form did work, I'm still curious as to why though. I would be interested in finding out more from the logger, since I have noticed that changes take several tries for them to go into effect(such as changing the email that the responses will be sent to) - but I've been confused with the logger and don't have much experience with it.