I'm trying to figure out how to do conditional statements tilizing app script in Google form with my already existing form. I have an email field which supposedly checks from a google sheet values in one column (email column). If what has been inputted into the email field exists in the Google sheet column for email, an alert prompts that the email already exists, help text will also show with a message like "email already exist". If the Ok button of the alert prompt is clicked, user can now go back into the Google form and edit their answers into the email field, and if the email address inputted does not exist in the Google sheet, nothing happens, and the user can proceed answering the form and lastly submit the form.
I have tried textvalidation but it seems text validation would only be able to answer one part of what I wanted to do with my form - show a help text. Below is my working app script:
var sheet = SpreadsheetApp.openById("IdOfMyGoogleSheet");
function validationTest() {
var data = sheet.getRange("Form Responses 1!F2:F").getValues();
var form = FormApp.openById('formID');
var item = form.getItemById(itemID)asTextItem();;
var textValidation = FormApp.createTextValidation()
.setHelpText("Email already exist")
.requireTextContainsPattern(data)
.build();
item.setValidation(textValidation);
}
Thanks in advance!
The first this to note is that a pattern is a regular expression. You also need to use requireTextDoesNotMatchPattern instead of requireTextContainsPattern. Here is what I've tested:
function setPattern() {
// Get the firm item
const form = FormApp.openById('form ID')
const item = form.getItemById('itemID').asTextItem()
// Get the list of values
const spreadsheet = SpreadsheetApp.openById('Spreadsheet ID')
const sheet = spreadsheet.getSheetByName('Form Responses 1')
const values = sheet.getRange(1, 1, sheet.getLastRow(), 1)
.getValues()
.flat()
// Transform the values into a regex expression (pattern)
const pattern = `^(${values.map(_escapeRegex).join('|')})$`
// Contruct and set validation
const validation = FormApp.createTextValidation()
.setHelpText("Email already exist")
.requireTextDoesNotMatchPattern(pattern)
.build()
item.setValidation(validation)
}
function _escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
Note that this allows users to add non-emails to the field, but not to repeat values.
References
Is there a RegExp.escape function in JavaScript? - Answer by bobince (Stack Overflow)
Class TextValidationBuilder (Google Apps Script reference)
Regular expression (Wikipedia)
Related
I have set up a script that creates a Google form and links it to a spreadsheet, is there a way to collect the responses and place them in certain cells in another sheet then unlinks and deletes the form response sheet.
the script I need will have to be flexible enough that it won't matter what the response sheet is named as I will be making multiple one use forms hence why I would also like to delete the response sheet after the answer is moved
for example say the answer is A (a1), B (b1) and C (c1) and I want to move it to sheet 'C' and into columns F, G and H after that's done i would like the response sheet to unlink and be deleted
any help would be greatly appreciated
Issue:
You want form response data to be submitted to a sheet of your choice, not the one that is created when linking the form to the spreadsheet.
Solution:
In that case, I'd suggest not linking the form to the spreadsheet at all, and use an onFormSubmit trigger to write the submitted data to your desired sheet.
Workflow:
Install an onFormSubmit trigger. You can do that manually, following these steps, or programmatically, by executing this function once:
const SOURCE_FORM_ID = "YOUR_FORM_ID"; // Change according to your needs
function installOnFormSubmitTrigger() {
const form = FormApp.openById(SOURCE_FORM_ID);
ScriptApp.newTrigger("onFormSubmitTrigger")
.forForm(form)
.onFormSubmit()
.create();
}
Once the trigger is installed, a function named onFormSubmitTrigger (it doesn't have to be named that way) will execute every time someone submits a response to the form. This function should append the response data to your desired sheet. It could be something like this (check inline comments):
const TARGET_SPREADSHEET_ID = "YOUR_SPREADSHEET_ID"; // Change according to your needs
const TARGET_SHEET_NAME = "Sheet1"; // Change according to your needs
function onFormSubmitTrigger(e) {
const targetSpreadsheet = SpreadsheetApp.openById(TARGET_SPREADSHEET_ID);
const targetSheet = targetSpreadsheet.getSheetByName(TARGET_SHEET_NAME);
if (targetSheet.getLastRow() === 0) { // Add headers if they don't exist yet
const itemTitles = e.source.getItems().map(item => item.getTitle()); // Get item titles
itemTitles.unshift("Timestamp"); // Append "Timestamp" to the sheet (if desired)
targetSheet.appendRow(itemTitles); // Append form item titles to the sheet
}
const itemResponses = e.response.getItemResponses();
const responses = itemResponses.map(itemResponse => itemResponse.getResponse()); // Get user responses
responses.unshift(new Date()); // Add today's date to the responses (if desired)
targetSheet.appendRow(responses); // Append responses to the sheet
}
Note:
If you don't want to submit to the first columns in the spreadsheet, simply add empty strings to the responses array, or use Range.setValues instead.
Reference:
Installable Triggers
appendRow
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);
}
Objective & Implementation
I created a questionnair using Google Forms. I want to authenticate users using a simple one time password that should be entered in the first question.
The password is stored inside a spreadsheet that contains only one cell. https://prnt.sc/r8i8q1
The script below reads the content of this cell and create a custom validator against the value of the first item of the form.
function validatePassword() {
var passwordSpreadsheet = "[passwords_spreadsheet_id]";
var ss = SpreadsheetApp.openById(passwordSpreadsheet);
var passwordSheet = ss.getSheetByName("passwords");
var form = FormApp.getActiveForm();
var items = form.getItems();
var item = items[0];
if (item.getType() == 'TEXT') {
var textItem = item.asTextItem();
var namedRanges = passwordSheet.getNamedRanges();
if (namedRanges.length > 0) {
var range = namedRanges[0].getRange();
var values = range.getValues();
var currentPassword = values[0][0];
var textValidation = FormApp.createTextValidation()
.requireTextMatchesPattern(currentPassword)
.build();
textItem.setValidation(textValidation);
}
}
}
Current Situation
The above code is working as expected for one password, but, the problem is that I couldn't find a way to create a custom validator against range of values.
Questions
Is there any way to have this simple authentication mechanisim in Google Forms via Google Apps Script?
Is there a way to make this password a One Time Only password?
If (1.) and (2.) are not available, then, what is the best way to authenticate Google Forms?
Thank you in advance!
Solution
Using Apps Script
The following piece of code will make a password verification according to what you right on the Spreadsheet as a password. It has comments to explain what line of code does:
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);
}
Using the UI
The esieast way to validate forms with password is to use the custom functionalities of Google forms. In your case you should follow these steps:
In the first section of your form only place a required short text answer.
Go to the three dots in the lower right part of the question and select Response Validation
Then in the options change them to Regular expression, Matches and introduce your desired regular expression (it could just be a String) and the right error message.
Here is an example of this in action:
I hope this has helped you. Let me know if you specifically need to get the passwords from the Spreadsheet. Let me know if you need anything else or if you did not understood something. :)
I am not a programmer in anyway so i am trying to use examples that I have found relating to this request but I am having a hard time finding the right way to do this. So any help would be much appreciated.
I am trying to send an email when a form is submitted and the value of one of the fields (Column B on the response sheet) matches "Not Satisfied". The email that I want to send should contain a message along with the value of column C.
Currently I have a trigger setup to fire the function on form submit and with the following code.
function sendEmail(email_address, email_subject, email_message) {
var lastRow = sheet.getLastRow();
var value = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1").getActiveCell().getValue().toString();
if (value.match("Not Satisfied" )) {
MailApp.sendEmail('example#gmail.com', 'User notr satisfied with [column C]', 'A user has reported that they are not satisfied with [column c]');
}
}
I know that I probably need to change this to just look at the last row that has data as well instead of getting the active cell as this is not an "onedit" script. But I am not sure how to change the script to do that.
Thanks in advance.
The form will only pass the form values to the trigger function. You can call sendEmail from within the main function that is associated with the trigger.
Snippet Source: Get Google Forms Data in an Email Message.
function SendGoogleForm(e)
{
var columns = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1").getRange(1,1,1,s.getLastColumn()).getValues()[0];
var found = false;
for ( var keys in columns ) {
if ( e.namedValues[columns[keys]] == "Not Satisfied") ) {
found = true;
}
}
if (found)
MailApp.sendEmail(email, subject, message);
}
I'm building a form for students to enter in and I want to Auto input their email address and I'm unsure how to do this. I'm come up with the following but I'm very new to this and may be way off. I'm unsure how to implement it and I'm getting a null response. Do I need a form question which says email ? how do I get the user's email to be recorded but in a hidden way?
function formEnterEmail(e) {
var userEmail = Session.getActiveUser().getEmail();
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
// Set the status of the new ticket to 'New'.
// Column F is the Status column
sheet.getRange(lastRow, getColIndexByName("Email")).setValue(userEmail);
function getColIndexByName(colName) {
var sheet = SpreadsheetApp.getActiveSheet();
var numColumns = sheet.getLastColumn();
var row = sheet.getRange(1, 1, 1, numColumns).getValues();
for (i in row[0]) {
var name = row[0][i];
if (name == colName) {
return parseInt(i) + 1;
}
}
return -1;
}
}
You can collect the user's email address in Google Forms only if you are using Google Apps for Business or Google Apps for Education, and then only from users in your domain. If you don't meet those requirements, then your only option is to request that users fill in the information, or to email a URL to a pre-filled-form.
This isn't really "hidden", as the live form will report that the user id is being collected.
If you're creating your form from a script, you can control the email collection feature by using Form.setCollectEmail(true). However, you can also set this up without using any code.
In the form editor, look for "Form Settings". They should appear above all the questions. Set the check boxes that require domain login, and username collection.
The spreadsheet that collects responses will automatically contain a "Username" column:
If you are building a form that you will be emailing to the students, then when you set up the form you can have it log all their email addresses for you without you having to write any script at all.
Here is a great way to get started writing a new form... go to this page:
http://www.google.com/drive/apps.html?usp=ad_search
and scroll down to the "Forms" section then click on "Create".
This should walk you through everything you will need to set up the form, and the results will be put into a spreadsheet for you, along with the students' email addresses. You can keep the resulting spreadsheet private so that you will be the only one who can view all the responses.
I hope that helps.