addEditors to Document from formSubmit - google-apps-script

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

Related

Can I duplicate a trigger with my new spreadsheet?

I have a new client template that I want to keep from being filled out prior to being duplicated. I've set a trigger to run a duplication and renaming function for the file when the file is opened.
function newRecord(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var scheck = sheet.getName();
if (scheck=='#New Client Record'){
var file = DriveApp.getFilesByName('#New Client Record').next();
file.setName('New Client')
file.makeCopy('#New Client Record');
}
}
This checks to see that the file is the correct file before proceeding, changes the name of the template and then duplicates the file and renames it the old template name. I configured it in this way so that when we start filling out the data right away while we're talking on the phone with a client, we aren't sullying our original template which remains pristine. It works nicely except that the trigger from file #1 doesn't transfer to file #2 which takes on the new identity of the original template.
So my question is - can I duplicate the trigger as well? Or is there script that can open the duplicate file and close the template file to protect my form?
EDIT: I threw a bit more at this last night and tried to add an install trigger with limited effect (I probably sound like a boomer talking about smoking the drugs with this - I'm not a programmer and have only a rudimentary vocabulary for script). I added the following function, which is transferring, but does not seem consistent in its functionality (excusing the pun).
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('newRecord')
.forSpreadsheet(ss)
.onOpen()
.create();
}
Thanks for any assistance any of you can provide on this. I've stumbled my way this far on my own with the archives, but I've finally gotten myself stuck.
How to get a form to create a new sheet with values from a template
Here is a quick example of how to carry this out. What you need to see this example in action:
A template sheet that looks like this:
A form that will have 3 "Short answers" that are required.
Client Name
Phone
Email
A standalone script (Drive Web app > New > More > Google Apps Script).
A folder into which all the newly created sheets will go.
At the top of the script (outside any function) you can define 3 global variables with the appropriate IDs:
const FORM_ID = ...
const TEMPLATE_ID = ...
const CLIENT_FOLDER_ID = ...
First, to set up an onFormSubmit trigger, you can do this by running the following function only once.
function createTrigger(){
const form = FormApp.openById(FORM_ID)
ScriptApp.newTrigger("formSubmitReceiver")
.forForm(form)
.onFormSubmit()
.create()
}
After which comes the main function that will:
Receive the formSubmit event and create a range of values from it.
In this example, it will generate a followUpDate that will be 7 days from the submission of the form.
Create a copy of the template file.
Fill the range B1:B4 with the relevant info.
Resulting in a new spreadsheet:
function formSubmitReceiver(e){
const itemResponses = e.response.getItemResponses()
const values = itemResponses.map( itemResponse => [itemResponse.getResponse()] )
const followUpDate = new Date()
followUpDate.setDate(followUpDate.getDate() + 7)
values.push([followUpDate])
const newFile = DriveApp.getFileById(TEMPLATE_ID).makeCopy()
const parentFolder = DriveApp.getFolderById(CLIENT_FOLDER_ID)
newFile.moveTo(parentFolder)
newFile.setName(values[0][0])
const newId = newFile.getId()
const newSpreadsheet = SpreadsheetApp.openById(newId)
const sheet = newSpreadsheet.getSheetByName("Sheet1");
const range = sheet.getRange("B1:B4")
range.setValues(values)
}
Depending on how complex you make your form, and what type of questions you choose (i.e multiple choice etc) this can get more complicated but hopefully it will give you a good idea of how to get something like this working. The simplest way is just to use "Short Answers" as this will just return a string. Also remember to make the questions "Required" if you don't want to handle empty values. Again, this all depends on how exactly you want to implement it and the complexity of the information involved!
References and Further Reading
FormResponse
getItemResponses()
ItemResponse
getFileById(id)
makeCopy()

How to make a custom validation function like requireTextMatchesPattern(pattern) function using Google apps script?

Final Objective
The final objective is to have the ability to define custom validator for Google Forms item like requireTextMatchesPattern(pattern).
Use Case
This custom validator will be used for example to compare what the user enters in the form item field with more than one value. or at least to have a custom functionality to execute when the user enters not valid data in the field.
Example
I have 3 participants, I want to make a simple authentication mechanism to make sure that the targeted audiences are going to participate. I have a spreadsheet that contains 3 passwords. The first question in the Form will require the user to enter a password. If the password doesn't match with one of the stored passwords in the spreadsheet, then, a validation message will appear to the user.
Partially Solution
Based on this question we can make a simple validation using requireTextMatchesPattern validator or directly from UI. The problem is that this validator limits the compare values to one.
function validatePassword() {
// Create new custom form
var form = FormApp.create('New Form');
var ss = SpreadsheetApp.openById('SHEETID');
var password = ss.getSheetByName('SHEETNAME').getRange('A1').getValue();
// Create first question to check the password, it must be required so that the user does not have access to the rest
// of the form if failed to log in
var item = form.addTextItem().setRequired(true);
item.setTitle('TEST TITLE');
// Create validation for this question matching the password that we got from the sheet
var textValidation = FormApp.createTextValidation()
.setHelpText('You must enter the right password')
.requireTextMatchesPattern(password)
.build();
item.setValidation(textValidation);
}
What I am trying to do is to replace the .requireTextMatchesPattern(password) with a call to a custom validation function that does some validation process and then returns the type of TextValidationBuilder.
Research
I found this source code which defines an interface of TextValidationBuilder. I don't know if it is the key to accomplish the main objective.
Thanks!
What I can understand from your question is that for example, you have 3 passwords (words) in 3 cells (Ex: from A1 to A3). Hence, you want to use them as conditions for a form and the issue ahead of you for the moment is that you are only able to do it with only one password (word).
As you probably noticed, the requireTextMatchesPattern(pattern)'s argument is a pattern, therefore you can have a Regex structured as word1|word2|word3, which will verify if the 3 passwords are the correct ones. Your code will look like this now:
function validatePassword() {
// Create new custom form
var form = FormApp.create('New Form');
var ss = SpreadsheetApp.openById('your-sheet-id');
var passwords = ss.getSheetByName('Sheet1').getRange('A1:A3').getValues();
// Ex passwords: asd, 123, asd123
const conditions = passwords.map(element => `${element}`).join('|')
// Create first question to check the password, it must be required so that the user does not have access to the rest
// of the form if failed to log in
var item = form.addTextItem().setRequired(true);
item.setTitle('TEST TITLE');
// Create valid ation for this question matching the password that we got from the sheet
var textValidation = FormApp.createTextValidation()
.setHelpText('You must enter the right password')
.requireTextMatchesPattern(conditions)
.build();
item.setValidation(textValidation);
}
I was nodding enough to solve a problem I had, and while writing a question right here, I realized that I solved my problem hahaha.
I was looking for how to update the restrictions (validation) of a form based on a sheet of spreadsheets, which was updated with each form submission. Of course, I set it as a trigger when the spreadsheets change.
It is a bit complicated to explain, but basically the values recorded in a column are taken in other spreadsheet and "subtracted" from the "accepted" column, then these values are concatenated with the function (textjoin ("|",1,A:A)). Finally, this resulting data is entered into a variable ('allowed') which is entered into .requireTextMatchesPattern (allowed).
This is the simplest way I found for my problem:
Update the allowed list to avoid double voting or participation, and at the same time limit the participants with some known information (telephone number, identifier, last name, etc.)
I am sure that with Scripts there may be other solutions, but using spreadsheets seems easier and simpler to me.
Anyway, I leave the formula here in case it works for someone:
function test(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[2];
var allowed = sheet.getRange("B1").getValue();
var form = FormApp.openById('urlID');
var item = form.getItemById(388494367).asTextItem();
var validation = FormApp.createTextValidation()
.requireTextMatchesPattern(allowed)
.build();
item.setValidation(validation);
}

Inserting variable data into an existing Google Sheet in Google Scripts

I'm building a script that will ultimately take data from a csv file, populate a spreadsheet, use that spreadsheet to autofill a number of documents, and then automatically e-mail those documents to customers. It's also moving the documents from each time the script runs to a new folder with just that day's reports. I'm pretty new to Google Scripts, and this has been my learning project.
The steps I've got to work so far is the creation of documents for the spreadsheet with dummy data.
I ended up creating a second script to send e-mails which sends e-mails, but it is set up to look for all documents in the folder that are google docs, so it send a copy of every document to each customer. I thought that if I pointed the e-mail back to the original spreadsheet to grab just the correct document IDs (instead of the type) , I could only send customers the reports that belonged to them (all in PDF form). The step of creating a new folder and moving the documents into it afterwards works fine.
What I'm having an issue with is inputting data into my document ID column in the original spreadsheet. I have been able to watch it put the document ID of the first document into every row that has info to iterate over in the column, and then replace every row again with the second document's ID, etc.
I looked for ways to add data to a spreadsheet. Every method I've found so far involves creating a new column or row with new information from data within the spreadsheet. I need to put in data that I'm just now creating outside of the spreadsheet and then put it in the right place so I can point to it later.
I've gone over the methods within the documentation. It looks like .getCell.setvalue(variable) should work...if I could find a way to get the cell from the range (Which keeps showing me out of range).
function createDocument() {
var headers = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A1:P1');
var tactics = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A2:P');
var templateId = '1DU13OJHWyYnO5mLFovm97pWwXuU7ZTTDVJb2Mpdeebk';
for(var i = 0; i < tactics.values.length; i++){
var customer = tactics.values[i][0];
var pcname = tactics.values[i][1];
var date = tactics.values[i][2];
var virusvalue = tactics.values[i][3];
var cpuuse = tactics.values[i][4];
var ramuse = tactics.values[i][5];
var harddrive = tactics.values[i][6];
var netuse = tactics.values[i][7];
var downtime = tactics.values[i][8];
var cpuperform = tactics.values[i][9];
var ramperform = tactics.values[i][10];
var harddiskperform = tactics.values[i][11];
var reccomend = tactics.values[i][13];
var custID = tactics.values[i][14];
var newdoc = tactics.values[i][15];
//Make a copy of the template file
var documentID = DriveApp.getFileById(templateId).makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
//rename the copied file
DriveApp.getFileById(documentID).setName('MCHA ' + customer + ' - ' + pcname);
Logger.log('value1 ' + documentID);
//THIS IS THE AREA I'M TRYING TO FIX
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
var cell = ss.getRange('P2:P').getCell([i], [15]);
cell.setValue(documentID);
SpreadsheetApp.flush();
Logger.log('value2 ' + documentID);
//This area has code to replace the tags in the document with values from the spreadsheet. I cut it for not being relevant.
}
}
Obviously defining the range is just going to fill and autofill each cell. That code worked when I tried it
I originally tried using the variable from my earlier list for the autofill, but I've since realized that shouldn't work at all.
I tried to set the range of cells and then set the current cell by the same notation ([i][15] etc.) which throws an 'Range not found' error. I feel like I must be missing some syntax.
A link to the folder with all the documents is below, if that helps.
https://drive.google.com/drive/folders/1sRhti3R8R-Cym0hr2S4XkAVn3wyBbSRu?usp=sharing
I may not have entirely understood the problem you are facing. But I can see the cause of the 'Range not found' error.
Your loop starts with a value of 0 for i. This causes the script to look for a range called P0 in the first iteration.
One way to avoid that is to try :
var cell = ss.getRange("P"+(i+1));
For code efficiency, I'd also suggest moving some of the code outside the loop. For example, the following line runs each time in the loop. But it could be defined once outside the loop and then the variable ss could be reused inside the loop.
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
Similarly, you could define the template file outside the loop and then sue it inside to make copies:
var templateFile = DriveApp.getFileById(templateId); // Outside the loop
And then inside the loop:
var documentID = templateFile.makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
Google apps script best practices suggests minimising calls to the spreadsheet, i.e. get the data in one call, process it, and then post the data to the sheet in one call. More here.
Hope this helps.

Efficient Way of sending Spreadsheet over email using GAS function?

I am creating an addon for Google Sheets that my local High School's volunteer clubs can use to keep track of their member's volunteer hours. Most of the code is done and works very nicely, and I am currently working on a system that will send a member a spreadsheet listing all of the volunteer events that they have logged. I have GAS create a separate spreadsheet, and then send an email with that separate spreadsheet attached in PDF. When the email is received, the PDF is empty except for a singular empty cell at the top left of the page.
I am pretty new to GAS but have been able to grasp the content pretty easily. I have only tried one method of sending the Spreadsheet and that is by using the .getAs(MimeType.PDF). When I changed the "PDF" to "GOOGLE_SHEETS," GAS returned the error: "Blob object must have non-null data for this operation." I am not entirely sure what a Blob object is, and have not found any website or video that has fully explained it, so I am not sure how to go about troubleshooting that error.
I think I'm having a problem grabbing the file because it either sends an empty PDF or it returns an error claiming it needs "non-null data."
function TigerMail()
{
var Drive = DriveApp;
var app = SpreadsheetApp;
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var Name = LOOKUP.getRange("E1").getValue();
Name = Name + "'s Hours";
//app.openById(Name+"'s Hours");
var HOURS = app.create(Name);
var ESheet = HOURS.getSheets()[0];
var ROW = LOOKUP.getLastRow();
var arr = LOOKUP.getRange("D1:J"+ROW).getValues();
var cell = ESheet.getRange("A1:G"+ROW);
cell.setValues(arr);
////////////////////////////////////////////////////
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var cell = LOOKUP.getRange("D1");
var Addr = cell.getValue();
var ROW = LOOKUP.getLastRow();
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
TigerMail.sendEmail(Addr, "Hours", "Attached is a list of all of the
events you have volunteered at:", {attachments: [FORMAT]} );
}
the final four lines are where the errors are occurring at. I believe I am misunderstanding how the .next() and .getFilesByName() work.
(above the comment line: creating a spreadsheet of hours)
(below the comment line: grabbing the spreadsheet and attaching it to an email)
Here is the link to the Google Sheet:
https://docs.google.com/spreadsheets/d/1qlUfTWaj-VyBD2M45F63BtHaqF0UOVkwi04XwZFJ4vg/edit?usp=sharing
In your script, new Spreadsheet is created and put values.
You want to sent an email by attaching the file which was converted from the created Spreadsheet to PDF format.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
About Drive.getFilesByName(Name), unfortunately, there is no method of getFilesByName() in Drive.
I think that when you want to use the created Spreadsheet, HOURS of var HOURS = app.create(Name) can be used.
About var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS), in the case of Google Docs, when the blob is retrieved, the blob is automatically converted to PDF format. This can be also used for your situation.
In order to save the values put to the created Spreadsheet, it uses SpreadsheetApp.flush().
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify as follows.
From:
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
To:
SpreadsheetApp.flush();
var FORMAT = HOURS.getBlob();
Note:
In your script, it seems that var ROW = LOOKUP.getLastRow() is not used.
References:
flush()
getBlob()
If I misunderstood your question and this was not the result you want, I apologize.

Accessing user entered data upon submit in google forms

I have a google-form that has the following two fields:
Email address: - A text box
Tool: - A radio button
Tool 1
Tool 2
Tool 3
The user would enter his email address and select a tool and click submit. I would like the following message to appear:
Thanks for responding. An email has been sent to you to at entered email address to download selected tool.
I have the following piece of code in the script editor
function emailFormSubmission() {
var form = FormApp.getActiveForm();//the current form
var dest_id = form.getDestinationId(); //the destination spreadsheet where form responses are stored
var ss = SpreadsheetApp.openById(dest_id);//open that spreadsheet
var theFormSheet = ss.getSheets()[0]; //read the first sheet in that spreadsheet
var row = theFormSheet.getLastRow(); //get the last row
var emailid = theFormSheet.getRange(row,2,1,1).getValue();//get column 2 corresponding to the email id. column 1 is timestamp. so, skip that.
var tool = theFormSheet.getRange(row,3,1,1).getValue();//get column 3 corresponding to the selected tool.
form.setConfirmationMessage('Thanks for responding. An email has been sent to you '+ emailid + ' to download' + tool);
}
I have also set the triggers to be Run -> emailFormSubmission, Events -> from Form , onFormSubmit.
What happens is: Suppose the first user ('A') enters his information and clicks submit. His entered information gets displayed correctly. When second user ('B') enters his information and clicks submit, A's information is displayed. When third user ('C') enters his information and clicks submit, then B's information is displayed. I found that the issue is with "getlastrow()" since the spreadsheet is updated after emailFormSubmission is processed.
Whats wrong with the above code? How do I fix this?
UPDATE
Based on #wchiquito's comments, I changed the code to following to make it work.
function emailFormSubmission(e) {
var form = FormApp.getActiveForm();
//Check this link on how to access form response:
//https://developers.google.com/apps-script/understanding_events?hl=en
var responses = e.response;//e is of type formresponse.
var emailid = responses.getItemResponses()[0].getResponse();
var tool = responses.getItemResponses()[1].getResponse();
Logger.log(emailid);
Logger.log(tool);
form.setConfirmationMessage('Thanks for responding. An email has been sent to '+ emailid + ' with instructions to download ' + tool +'. If you do not find our email in your inbox, please check your spam folder');
Logger.log(form.getConfirmationMessage());
}
Remember that the event On form submit (Understanding Events) receives a parameter that has the following structure:
values​​
range
namedValues​​
and you can do something like:
function emailFormSubmission(e) {
...
var row = e.range.getRow();
...
}
Try the following code to observe the structure of the parameter e:
function emailFormSubmission(e) {
...
Logger.log(e);
...
}
UPDATE
First, excuse my confusion, I showed you the structure of a Spreadsheet form submit event when you really are using a Form submit event.
Sure enough, a Form submit event has the following structure:
response
Returning an object of type FormResponse.
Therefore, defining the event: On submit form (Form submit event), you can do something like the following:
function emailFormSubmission(e) {
var itemResponses = e.response.getItemResponses();
for (var i = 0, len = itemResponses.length; i < len; ++i) {
Logger.log('Response #%s to the question "%s" was "%s"',
(i + 1).toString(),
itemResponses[i].getItem().getTitle(),
itemResponses[i].getResponse());
}
}
However, the confirmation message set according to the data sent as responses of the form, does not seem very clear, you can set the message, but will not display for the active response, if not for the next.
My first guess is these two lines right here:
var emailid = theFormSheet.getRange(row,2,1,1).getValue();//get column 2 corresponding to the email id. column 1 is timestamp. so, skip that.
var tool = theFormSheet.getRange(row,3,1,1).getValue();//get column 3 corresponding to the selected tool.
When you call getLastRow() on a sheet - you're getting the last row. Sure, but considering the order of events and how these values are processed, you need a +1, to get the most recent submission. Currently you're one row behind when your code runs to update the Form confirmation message.
So just change your code to the following:
var emailid = theFormSheet.getRange(row+1,2,1,1).getValue();
var tool = theFormSheet.getRange(row+1,3,1,1).getValue();
Spreadsheets are the most confusing of Google services, in my opinion. When you get values in the Spreadsheet, they're returned as an [] or [][] depending on what your Range is when you call getValues(). But getRange() on a sheet starts at index 1 (to make it easier to read in code I suppose). Often times I find that I have an off-by-one error because of the way data is passed around. Just keep that in mind as you work with Spreadsheets :)
Short answer: want you want can't be done with Google forms.
Explanation:
form.setConfirmationMessage() sets the confirmation message for the form as stored on the server, not for the current active form. Same applies for example for form.setTitle(). The active form will not be modified. One would expect different behaviour for the confirmation message, but alas, this is not the case.
Yes, you can do this with the add-on "Formfacade".
It's free to use in 1 form.