I have a form that serves as an application to receive volunteer teams for my NGO. What I want to do is create a script that sends an email to the registrar upon someone completing the form. That's the easy part which I know how to do.
I want to include in that "email notification" a few key answers from some questions for the registrar to see. This I also know how to accomplish.
here is my current code that works:
function Team_ApplicationMailer(e) {
var recipient = "myemail#gmail.com";
var timestamp = e.values[0];
var name = e.values[3];
var country = e.values[1]
var subject = e.values[1]+' wants to Volunteer!' ;
var startdate = e.values[12];
var enddate = e.values[13]
var hyperlink = "mylink.com"
htmlBody = '<u><strong>'+name+'</u></strong> from: <u><strong>'+country+'</strong></u>just completed the Teams Application Form for these dates: <u><strong>'+startdate+' to '+enddate+'</u></strong><br><br>Form completed on: <u><strong>'+timestamp+'</u></strong><br><br>View the Form: Click Here';
MailApp.sendEmail(recipient, subject, htmlBody, {htmlBody:htmlBody});
}
Here's the tricky part I want to add: I want to also add data found in e.values[19] and e.values[20], but these values are from either a multiple choice (e.values[19]) or checkbox (e.values[20]) and I need the values both simplified—i.e. some text deleted—and possibly split when emailed.
The two questions are basically identical. e.values[19] is "What do you want to do: 1st Priority" with some multiple-choice options. and e.values[20] is "What else do you want to do" with some checkbox options. Both questions have the same 7 choices and each choice has a long description (hence the need to shorten)
Basically I want e.values[19] which value looks like this:
optionX: (Long Description)
to look like this in the email:
What do you want to do: optionX.
I don't want the long description of the option in the "()" to be included.
For e.values[20] I want the values to look like this in the email:
What else do you want to do: optionA, optionB, optionC
INSTEAD of This:
optionA: (Long Description),optionB: (Long Description),optionC: (Long Description)
I've looked into the .replace and .split functions but don't know how to make them work together, especially for the checkbox option.
Thanks for the help!
I wrote a function for this. It just removes anything inside parentheses (). This seems to be what you're looking for.
var s = e.values[19];
s = s.replace(/\([^)]*\)/g,"");
If you have parentheses inside parentheses, let me know, and I will have to make a quick edit.
So what's happening here.
It's looking for a (.
It's looking for as many characters as it can while matching a ) at the end. This is without having a ) inside.
If you had allowed a ) inside, it would match optionA:(...), optionB: (...), optionC: (...). We want to avoid this, so we do not allow ) inside. That's what [^)] means.
The g flag means to match "globally", or everywhere in the string.
Related
I am writing a form to update an inventory whenever an item is ordered. In this project, there is a google sheet which is linked to a form. The form contains a script that gets the current inventory from the sheet and subtracts one from this upon order.
The form has three questions: name, size, and comment.
The issue I am getting is that the system works once, and then somehow stores the first response for each successive form submission.
For a minimal working example: I've distilled the issue to the following code, which is triggered by a form submission:
function updateStock()
{
var customer_name = "";
const ss_id = ### I enter spreadsheet ID here ###
const form_id = ### I enter form ID here ###
var sheet = SpreadsheetApp.openById(ss_id);
var form = FormApp.openById(form_id);
var form_resp = form.getResponses()[0];
var customer_name = form_resp.getItemResponses()[0].getResponse();
Logger.log(customer_name);
// Rest of code follows from here
}
Upon entry for the first form I write:
Name: Peter
Size: M
Comment: none
Code returns:
1:43:40 AM Info Peter
(If I include the rest of the code, it correctly subtracts the inventory).
Then on the next (or tenth) submission, I might submit:
Name: Joe
Size: L
Comment: none
again, the code returns:
1:44:55 AM Info Peter
If I start from scratch or clear responses, I can get it to work once, but if not, the code will forever return "Peter" for the name, regardless of what I enter.
The form responses however are correct! It is just the call to
var form_resp = form.getResponses()[0];
var customer_name = form_resp.getItemResponses()[0].getResponse();
... that seems to have a "cached" value.
This seems like a weird bug, but am wondering if anyone has a clue as to what this might be (or, honestly, if there is a better way to achieve the same result!)
Thanks for taking the time.
Ugh. Very bad error on my part.
As vector pointed out:
The code: form.getResponses() returns all responses whereas I thought it was returning the current response (read the docs!)
Thus form.getResponses()[0] was naturally returning the first answer, whereas I should have written:
var N = form.getResponses().length-1;
var form_resp = form.getResponses()[N];
Whoops.
I need help regarding using Regex as data validation in a short-answer question in Google Forms. According to my understanding, if I use this expression:
"Doesn't Match" + [a-zA-Z]{1,}|[0-9]{1,15}|[0-9]{17,140}
I should still be able to fill the answer with 16 digits of numbers. However, I was unable to input any digits of number at all. Does anyone have any solution for this?
As a side note, I can't use "Matches" nor "Contains" because I have to link it to Google Spreadsheet for "unique value" data validation as well and Google Forms doesn't support multiple data validations. Here's my current script:
//Still needs much better solution, but this will do for now
function checkNIK(){
//Get current form
var form = FormApp.getActiveForm();
//Open spreadsheet containing NIK
var ss = SpreadsheetApp.openById(<id>);
//Get sheet
var responses = ss.getSheetByName('Form responses 1');
//Get list of all NIK in column F, which contains a few hundreds 16-digits numbers.
var disallowedArray = responses.getRange('F:F').getValues();
//Clean NIK list
disallowedArray = disallowedArray.filter(item => item != ''); //Empty cells
disallowedArray = disallowedArray.filter(item => item != 'NIK'); //Cell named "NIK"
//Transform NIK list array into a single string
var disallowedString = disallowedArray.join("|");
//Append additional expressions
//Doesn't work, adding "|[0-9]{17,140}" makes the form unable to accept 16 digits number anymore
disallowedString = disallowedString.concat('|[a-zA-Z\\s]{1,}|[0-9]{1,15}|[0-9]{17,140}');
//Print regex just to make sure
//console.log(disallowedString);
//Select the question you want to update
var item = form.getItemById(<id>).asTextItem();
//Create validation rule
var validation = FormApp.createTextValidation()
.setHelpText('Wrong or duplicate NIK.')
.requireTextDoesNotMatchPattern(disallowedString)
.build();
item.setValidation(validation);
}
Let me know if there's something I did wrong, both on-topic and off-topic.
Sorry for replying only one year later. The problem was on my side, not regex. After countless experiments, I was able to get the correct regex and condition to only accept precisely 16 numbers.
The condition that I used was
.requireTextDoesNotMatchPattern(allowedString).
This condition was what made this seemingly simple problem very hard to accomplish in Google Forms.
As for the regex itself:
[a-zA-Z\\s]{1,}|[0-9a-zA-Z\\s]{1,15}|[0-9a-zA-Z\\s]{17,140}$
Basically, it disallows anything that is not 16 characters in length. It also disallows any letter and special symbol.
Though I cannot provide a reasoning why you encountered such error. I found a workaround for you to be able to input 16 digits of numbers.
I just changed the order of the expression then test if it will work:
Regex: [0-9]{17,140}|[0-9]{1,15}
Input: 15 digits
Input: 16 digits
Input: 17 digits
Your need to enclose the regular expression in brackets.
([a-zA-Z]{1,}|[0-9]{1,15}|[0-9]{17,140})
I hit a wall here with this script. I am trying to get the body of a PayPal email that tells me I have a new subscription. I need the email address of the new subscriber. So...
I get the thread
I get the body. It's full of CSS and code, I don't see any info on the use. On the web page in the source it's all code it seems.
When I output it to a spreadsheet Show modal dialog it's looks perfect. I see the email address I am trying to get.
Is there a way to get that text? Then I can get the email address and the rest is EASY for me :-).
I hope I'm explaining things right.
This is far as I get with trying to get the text from this.
Thanks for any help you can spare. Maybe this is way over my head in which case, I'll drop it. But I just need the email address from this!
function getEmailFromFolder() {
// Log the subject lines of the threads labeled with MyLabel
var label = GmailApp.getUserLabelByName("NewVWMember");
var threads = label.getThreads();
var message = threads[0].getMessages()[0]; // Get first message
var body = message.getBody();
var output = HtmlService.createHtmlOutput(body);
//var n = body.search("mailTo");
var ui = SpreadsheetApp.getUi();
ui.showModalDialog(output, 'I want this!');
}
Answer:
You can use a regular expression to extract all emails out of a string.
Regular Expression:
There are multiple diffent ways of doing this, an example would be the following:
/([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/g
Rundown of this regular expression:
[a-zA-Z0-9._-]+: matches the username - any number of characters from [A-Z], [a-z], [0-9], . and _
# matches the literal character #
[a-zA-Z0-9._-]+: matches the domain - again like the username, any number of characters from [A-Z], [a-z], [0-9], . and _
\.: macthes the literal character .
[a-zA-Z0-9._-]+: matches the top-level domain, as beforethis can be any number of characters from [A-Z], [a-z], [0-9], . and _.
g: returns globally, so will not return after the first email has been found.
You can test out the regular expression on RegEx101 here.
Email Extraction:
With the regular expression, you can extract an array of email addresses from the message body with just a few extra lines of code:
function getEmailFromFolder() {
var regex = /([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/g;
var label = GmailApp.getUserLabelByName("NewVWMember");
var threads = label.getThreads();
var message = threads[0].getMessages()[0];
var body = message.getBody();
var output = HtmlService.createHtmlOutput(body);
var emails = output.getContent().match(regex).toString();
var html = HtmlService.createHtmlOutput(emails);
var ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, 'I want this!');
}
Here, output is a string of all the email addresses in the message. It needs to be converted to string to display in the modal dialog, but you can do with this as you see fit.
References:
Regular Expression - Wikipedia
RegEx 101
i want to send emails that contains text and variable values from the spreadsheet.
For that i ran the sendemails-script which works well. But at the example the cell that was declared as "var message" didnt contain variable values but only text.
Is it possible to add "references to values" to this text?
Something like:
Hello,
last week you bought (value of B3) apples and (value of B4) cherries.
I think in Excel you can split text and formular units in one row, is there something like that in GoogleDocs?
Thanks for your help in Advance, i hope you could understand what i mean :)
Marten
Amit's suggestion is a good idea although it might be a bit complex if you want to keep the whole thing simple and/or adapt it to your needs... It's never easy to analyse a script that is the result of a lot of work... from someone else.
So I wrote a very simple script that does exactly what you ask, nothing more (and nothing less I hope)
It uses 3 columns and the base message has 3 placeholders but you can of course expand it the way you want.
It will be easy to customize it to your use case. I added many log in the code so you see exactly what is going on.
function simpleMerge(){
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var headers = data.shift();
for(var n=0 ; n<data.length ; n++){
var baseMessage = "Hello #0#, \n\nlast week you bought #1# apples and #2# cherries.";
for(var c=0 ; c<data[n].length ; c++){
var placeHolder = '#'+c+'#';
var value = data[n][c];
Logger.log('replacing '+placeHolder+' with '+value);
baseMessage = baseMessage.replace(placeHolder,value);
}
Logger.log('message '+n+' = '+baseMessage);
//send message to who you want
}
}
Install the ultradox or mailchimp add-on or many others that do this with more flexibility.
I'm Working on creating script that will perform the actions described below. so far, I've managed to get the first two parts to function, but am now stuck on getting anything more to work. I've reviewed several response forums and tried the suggestions, but no success.
Desired script flow:
form submitted from spreadsheet form
completes fields:
Timestamp
Username (email collected on submission)
Student
Grade
Intervention Plan
Core Reading/Math
Team (email list)
1 script runs onFormSubmit that then creates a copy of a template document, renames that new copy to the e.value "student" submitted in form,
2 then replaces selected text strings within the document with values submitted in the form.
3 Add editors to new document and sends notification with desired instructions
4 creates event (one week from submission date) event details include instructions and link to shared document for team members to complete with their input, sends event invitation to email list.
Here is the working script so far.
function formSubmitValues(e) {
var timeStamp = e.values[0];
var userEmail = e.values[1];
var student = e.values[2];
var grade = e.values[3];
var conern = e.values[4];
var readingCore = e.values[5];
var mathCore = e.values[6];
var interventions = e.values[7];
var team = e.values[8].toString(); // "just to be sure"..Henrique says add .toString this allowed the replaceText part to work
//Makes copy of template document and renames
var tempID = ("1Rq0pDAnuGNfL6W3GB0ZuLeWM2uYzHpKzoyxoXlwjtgE") // use document ID from Template Document
var copyId = DocsList
.getFileById(tempID)
.makeCopy(student + " Initial Referral") // names new copy as student's name
.getId();
// trying to add editors to new document using email list generated in form submit value of "team"
DocsList.getFileById(copyId).addEditors([team]);
// replaces text within template with selected fields from formSubmitValues
var doc = DocumentApp.openById(copyId)
var body = doc.getActiveSection();
body.replaceText("%STUDENT%", student);
body.replaceText("%DATE%", timeStamp);
body.replaceText("%TEACHER%", userEmail);
body.replaceText("%TEAM%", team);
return doc;
}
REPORTED ISSUE RESPONSE: Here is what they said: "The function takes an array or strings, like: DocsList.getFileById(copyId).addEditors(['parent#domain.com', 'parent2#domain.com']);
I tried entering emails directly into script like this and things worked.
So my problem is not the 'addEditors method, but lies in getting the form submitted emails to be passed correctly. Any suggestions on how I would do this?
I have tried what I believe to be all combinations of using .toString(), or not, and using .Split(',').
RE-DEFINE PROBLEM : So it is an issue of how the emails are passed from the e.values form submit.
Here is where I'm at:
When I type emails into script directly: .addEditors(['parent#domain.com', 'email2#domain.net', 'email3#gmail.com']) it works, (I did have to move the addEditors method in the script to go right after the .makeCopy instead of at the end.)
This is what the Execution Transcript shows:
File.addEditors([[parent#domain.com, email2#domain.net]]) and it runs the rest of the script. note: One part I don't understand is the single quotes i.e. 'email' They must be typed in the script, but don't show up on Transcript when run. I've tried putting them around emails in the form, but it makes them show in Transcript and still doesn't run anyway.
So this is what script looks like now:
var tempID = ("1Rq0pDAnuGNfL6W3GB0ZuLeWM2uYzHpKzoyxoXlwjtgE") // use document ID from Template Document
var copyId = DocsList
.getFileById(tempID)
.makeCopy(student + " - TestingCopy") // names new copy as student's name + text
.addEditors([team.split(',')])
.getId();
But when I use the var team with or without .split(',') it does not work. But in the Transcript it shows:
File.addEditors([[rreynolds#domain.net, parent#domain.com]])
which looks identical as to what shows when it does work, but that is the last thing shown in Transcript and editors are not added to document and the script does not finish.
I'm obviously not understanding something here. Is there a way I could get the emails in the team e.values to be treated in a way that the addEditors method is requiring? In the spreadsheet cell they appear as a CSV. i.e rreynolds#domain.net, parent#domain.com
Do they have to be read one at a time, or something?
I'm learning a lot, and appreciate all your help. I am sorry for the confusion with all the comments, but am not sure of the correct way to address issues in this forum. For example: should I go back and edit my original script to show the current version, or add it someplace else? I'm trying to keep the conversation flowing, so that it is easier for others to follow - Thanks rob
please let me give a last (hopefully) clear answer : (thanks for sharing the spreadsheet, this is far more easy to work on ;-)
here is your code fully working.
I have created some intermediate variables to show how it works.
function formSubmitEditors(e) {
// defines spreadsheet form events on submit of form. This function formSubmitEditors is triggered on formSubmit
var timeStamp = e.values[0];
var fileName = e.values[1];
var team = e.values[2].replace(/, /g,"|"); // remove unwanted spaces and commas replace by | for visibility ;-)
Logger.log(team);// contains | as separators
var teamArray = team.split('|');
Logger.log(teamArray.length+' : '+teamArray);// check that it is an array of x elements
//Makes copy of template document and renames
var tempID = '1Rq0pDAnuGNfL6W3GB0ZuLeWM2uYzHpKzoyxoXlwjtgE' // use document ID from Template Document
var copyId = DocsList
.getFileById(tempID)
.makeCopy(fileName + " - TestingCopy") // names new copy as student's name + text
.getId(); //
var file = DocsList.getFileById(copyId).addEditors(teamArray);
// replaces merged-text values within template with selected fields from formSubmitValues
var doc = DocumentApp.openById(copyId)
var body = doc.getActiveSection();
body.replaceText("%FILE%", fileName);// you wrote %FILENAME% in place of %FILE%
body.replaceText("%DATE%", timeStamp);
body.replaceText("%TEAM%", team);// it will be shown with | as separators, if you don't like it replace team by teamArray.toString() to get commas again.
}
EDIT : I removed the toString() for team event, not necessary since e.parameters are already strings.
EDIT2 : to be complete and do what you needed in the initial question you could replace the end of the code with this one that creates the Cal event on next week and sends invites with link to the doc.
var file = DocsList.getFileById(copyId).addEditors(editorsArray);
var fileurl = file.getUrl();
Logger.log(fileurl)
// replaces merged-text values within template with selected fields from formSubmitValues
var doc = DocumentApp.openById(copyId)
var body = doc.getActiveSection();
body.replaceText("%FILE%", fileName);
body.replaceText("%DATE%", timeStamp);
body.replaceText("%MAILS%", editors);
var Cal = CalendarApp.getCalendarsByName('testencodage')[0];// replace with your calendar name you want to use
var newEvent = Cal.createAllDayEvent('Fill the questionnary', new Date(new Date(newtimeStamp).getTime()+7*24*3600*1000), { guests : e.values[2] , sendInvites : true , description :"Don't forget to fill this document "+fileurl})
}
I'd suggest a couple of things
Add a few Logger.log statements in your code to print out debug information.
Add a try ... catch block around the entire section of code and print out the exception. See if you are getting any exception.
Last, use the Execution transcript window to see where your script stopped, if it did.
There are 2 ways to add an editor by email : addEditors([emailAddresses]) and addEditor(emailAddress)
The first has an "s" and needs an array of email adress strings, the second takes a single string as argument. You should use the second or add [brackets] to the email string in your code.
concerning you comment // need to figure out where/how to: .addEditors(email1,email2,etc);
// is this done from DocsList or DocumentApp class?
addEditor() and addEditors() belong to the file class, a member of DocsList class , you can add user(s) using user(s) objects or user(s) email(s) as explained in the doc.
It could be used like this DocFile.addEditors([email1,email2])
EDIT : A lot of comments on this post, sorry about that, it has become quite uneasy to read... I tested these addEditors feature with spreadsheet and it works as expected, using simple array for multiple user emails and string for single email. The document service seems to have a problem with the addEditor() method and it should be reported to the issue tracker.
REPORTED I've reported issue #1512 - Rocketrob