Allow only selected account to submit a Google Form - google-apps-script

Is there a way (Apps Script maybe?) to inform google forms submitter that his submission will not be gaethered because the form is restricted to selected google accounts?
My try looked like this, but it has 2 problems :
-it is accepting answers submitted from people other than verified and verified2 (no idea how to add that)
-it only displays my custom message if someone tries to submit second answer
function onFormSubmit(e){
var af = FormApp.getActiveForm();
var defaultClosedFor = af.getCustomClosedFormMessage();
af.setCustomClosedFormMessage("You are not using an account with submission access. Please log in to account with correct authorization");
var responses=af.getResponses();
if(responses[responses.length-1].getRespondentEmail()=="verified#gmail.com" ||
responses[responses.length-1].getRespondentEmail()=="verified2#gmail.com"
){
af.setCustomClosedFormMessage(defaultClosedFor);
}
}

I see multiple problems with the code:
Custom Closed Form Messages are only displayed when the form is closed for responses manually or the form only allows one response per email account.
Email addresses are not collected on the form. It has to be enabled manually on the Form settings.
Solution:
Enable Collect Email Addresses option on Google Form Settings:
Use setConfirmationMessage() to display custom messages depending on the entered email when the form is submitted.
Sample code:
function createTrigger() {
var form = FormApp.openById('****your-form-id****');
ScriptApp.newTrigger('validateForm')
.forForm(form)
.onFormSubmit()
.create();
}
function validateForm(e){
var af = FormApp.getActiveForm();
af.setCollectEmail(true);
var responses=af.getResponses();
var defaultConfirm = "Your response has been recorded.";
var rejectMessage = "You are not using an account with submission access. Please log in to account with correct authorization.";
if(responses[responses.length-1].getRespondentEmail()=="verified#gmail.com" ||
responses[responses.length-1].getRespondentEmail()=="verified2#gmail.com")
{
af.setConfirmationMessage(defaultConfirm);
}
else {
af.setConfirmationMessage(rejectMessage);
af.deleteResponse(responses[responses.length-1].getId());
}
}
Note: Please run createTrigger() first manually in GAS to create the on form submit trigger.

Related

Google Sheet/script trigger for auto email when new cell value is yes/no

New user here and not versed in code much. Im working on a COVID-19 form for our company and looking for some help with google script/trigger where when an employee fills out the google form and selects yes/no on the google form the google sheet that collects the data will send off an email based on the value in the cell.
IE: employee A enters "no" to agreeing to comply to policy it will email the manager informing them that someone entered "no".
I have a test formula that is working but when I set up a min by min trigger for it to kick off it just continues to kick off. Im assuming this is obviously due to it not having a code to only send new entries??
Any help would be greatly appreciated.
To reiterate im trying add a script that sends an email only ONCE per user when someone fills out the google form and they choose the wrong answer.
Code I have now:
function onEdit() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var currentValue = sheet.getRange("F2:F1000").getValue();
if (currentValue = ("Yes")) {
MailApp.sendEmail("test#example.com", "ALERT: Please see person in question!", "The message body that you want to send.");
}
}
Try this:
You haven't identified the person in question.
function onMyEdit(e) {
const sheet=e.range.getSheet();
if(sheet.getName()=='Sheet1') && e.range.columnStart==6 && e.range.rowStart>1 && e.value=='No') {
//MailApp.sendEmail("test#example.com", "ALERT: Please see person in question!", "The message body that you want to send.");
Logger.log("ALERT: Please see person in question!");
}
}
Since this is sending an email which requires authorization you will have to create an installable trigger using the Edit/Current Project Triggers menu.

How to dynamically change an existing text item value on Google Forms using Google Apps Script

I have an existing Google Form in which there is a TextItem with a title "Which location was this performed at?".
Whenever the form is loaded (opened), I need to set a location value (loc) to this existing textbox and show it to the user.
function populateMemberIds(loc){
var form = FormApp.openById(formUrl);
var questions = form.getItems();
for (var i=0; i<questions.length; i++){
if(questions[i].getTitle()=="Which location was this performed at?"){
var textItem = questions[i].asTextItem();
//I get stuck here
}
}
I already setup the openForm trigger which allows to run the populateMemberIds function to be run on each form load. Again, what I need is to change the value of the "Your answer" section of the text item with the location value (loc).
I would appreciate any help.
You can't modify a form response filled by a user, you can either create a form response programmatically or edit a response after being submitted. The onOpen form trigger runs when someone opens the form to edit it rather than answer it [1]:
This event does not occur when a user opens a form to respond, but
rather when an editor opens the form to modify it.
Moreover, triggers functions comes with an event parameter already defined [1] so you can't set your own function parameter(s) as you're doing with your loc parameter.
EDIT
You can programmatically create and submit a form response [2], from which you can also get a URL with a prefilled form for the user to finish [3].
function populateMemberIds(loc){
var form = FormApp.openById("[FORM-ID]");
var questions = form.getItems();
var response = form.createResponse();
for (var i=0; i<questions.length; i++){
if(questions[i].getTitle()=="title form"){//Which location was this performed at?"){
var textItem = questions[i].asTextItem();
var itemResponse = textItem.createResponse(loc) ;
response.withItemResponse(itemResponse);
}
}
//Submit programmatically the form response
response.submit();
//URL with prefilled form response
Logger.log(response.toPrefilledUrl());
}
function test () {
populateMemberIds("US");
}
[1] https://developers.google.com/apps-script/guides/triggers/events#google_forms_events
[2] https://developers.google.com/apps-script/reference/forms/form-response
[3] https://developers.google.com/apps-script/reference/forms/form-response#toprefilledurl
The onOpen Google Apps Script triggers (simple and installable) for Google Forms are executed only when the form is opened in the form editor, not when the form is opened by using the view / edit response links.
There are two ways to "prefill" a Google Forms response:
Use the prefilled response URL
Create a response programmatically, then use the editResponseUrl
Related
Is it possible to 'prefill' a google form using data from a google spreadsheet?
How to generate a pre-filled form URL for Google Form

Hide / show specific Sheets tabs for specific users on shared file

My google doc should prompt a login box upon opening the Google Sheets file and, based on the password, it should display a specific tab. E.g. for "password1" it should allow the user to see only tab "Sheet1" and other sheets should be hidden. Similarly, for "password2" it should allow the user to see and work on tab "Sheet2" only.
I tried to run the following code, however it shows some errors.
function showLoginDialog() {
var sheet3 = SpreadsheetApp.getActiveSheet('Sheet3');
sheet3.hideSheet();
var sheet2 = SpreadsheetApp.getActiveSheet('Sheet2');
sheet2.hideSheet();
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet('Sheet1');
var ui = SpreadsheetApp.getUi();
var prompt = ui.prompt('Password','Enter Password',ui.ButtonSet.OK_CANCEL);
var response = prompt.getResponseText();
var button = prompt.getSelectedButton();
if(button==ui.Button.OK) {
if(response=='pwd3') {
sheet3.activate();
}//end of inner if
if(response=='pwd2'){
sheet2.activate();
}
}//end of main if
}//end of function
The Google Sheets file should ask for the password upon opening it, and it should display the respective sheet based on the password.
You want to open only one of all sheets in the Spreadsheet by the inputted value from the dialog.
For example, when the value of pwd2 is inputted, you want to open only "Sheet2".
You want to open the dialog when the Spreadsheet is opened.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
The flow of the modified script is as follows.
Open only "Sheet1" as a default, when the Spreadsheet is opened.
Open a dialog.
When pwd2 is inputted, only "Sheet2" is opened.
When pwd3 is inputted, only "Sheet3" is opened.
If you want to open the dialog when the Spreadsheet is opened, please install OnOpen event trigger to RunOnOpen().
How to install OnOpen trigger:
Open the script editor.
Edit -> Current project's triggers.
Click "Add Trigger".
Set RunOnOpen for "Choose which function to run".
Set "From spreadsheet" for "Select event source".
Set "On open" for "Select event type".
Modified script:
function RunOnOpen() {
// Updated
var openSheet = function(ss, sheets, sheet) {
var s = ss.getSheetByName(sheet);
s.showSheet();
ss.setActiveSheet(s);
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheet) {
sheets[i].hideSheet();
}
}
};
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
openSheet(ss, sheets, "Sheet1"); // Open only "Sheet1" when this function is run.
var ui = SpreadsheetApp.getUi();
var prompt = ui.prompt('Password','Enter Password',ui.ButtonSet.OK_CANCEL);
var response = prompt.getResponseText();
var button = prompt.getSelectedButton();
if (button == ui.Button.OK) {
var sheet = "";
if(response == 'pwd3') {
openSheet(ss, sheets, "Sheet3");
} else if(response == 'pwd2') {
openSheet(ss, sheets, "Sheet2");
}
}
}
Note:
In this modified script, when RunOnOpen() is manually run at the script editor, the script works. In order to do this, I didn't use the event object of OnOpen event trigger.
References:
hideSheet()
showSheet()
Installable Triggers
If I misunderstood your question and this was not the result you want, I apologize.
Updated:
When I tested several times for this script, there are the case that "Sheet1" sheet and another sheet are opened. In order to avoid this situation, I updated the script. Could you please confirm above script again?
Edit:
You want to make users use the specific sheet of Spreadsheet by inputting each password.
You don't want to publish the script for users.
You want to make several users use the Spreadsheet, simlutaneously.
If my understanding for your goal is correct, how about this workaround?
Create each Spreadsheet for each user.
By this, the simultaneous access for the Spreadsheet can be achieved.
Use Web Apps in order to open each Spreadsheet by inputting the password.
The flow of this workaround is as follows.
An user, who logged in Google, open Web Apps.
The user input the password and click "OK".
Checking the password at the server side and return the URL of Spreadsheet corresponding to the password.
Open the Spreadsheet from the URL.
By this flow, I think that top 3 aims can be achieved.
Sample script:
Before you use this script, please split the Spreadsheet for each sheet, and retrieve each URL of Spreadsheet.
When you use this, please deploy Web Apps with the following condition. If you want to know how to deploy Web Apps, please check this.
Execute the app as: Me.
Who has access to the app: Anyone
By this condition, users are required to log in to Google for accessing to Web Apps.
Users accesses to the URL of Web Apps.
Google Apps Script: Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index");
}
function selectSpreadsheet(value) {
var url = "";
if (value == "pwd2") {
url = "### URL of Spreadsheet for password of pwd2 ###";
} else if (value == "pwd3") {
url = "### URL of Spreadsheet for password of pwd3 ###";
}
return url;
}
HTML & Javascript: index.html
<input type="text" id="value" >
<input type="button" value="ok" onclick="openSpreadsheet()">
<script>
function openSpreadsheet() {
var value = document.getElementById("value").value;
google.script.run.withSuccessHandler((url) => {
if (url) window.open(url,'_top');
}).selectSpreadsheet(value);
}
</script>
Note:
When you modified the script, please deploy the project as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
I think that when the each Spreadsheet is shared for only each user, the security will be high.
The maximum number of the simultaneous access for Web Apps is 30. Ref
This is a simple sample script. So if you use this, please modify it for your situation.
From TheMaster's somment, Although it's highly unlikely that users are able to retrieve the passwords from source code, I recommend using private functions and/or properties service for storing of passwords for a extra layer of security.
As a sample, you can use like below by saving the passwords and URLs to PropertiesService.
function selectSpreadsheet(value) {
return PropertiesService.getUserProperties().getProperty(value);
}
References:
Class google.script.run
Web Apps
Taking advantage of Web Apps with Google Apps Script
I don't think this is secure for the following reasons:
Sheets' hidden/visible property is global and not restricted to a single user. If user1 and user2 login one after another at almost the same time, user1's sheet is visible after user1's login; As soon as user2 logs in, user1's sheet will be hidden for both users and user2's sheet will be visible to both user1 and user2
It's easy to unhide sheets. user1 can unhide user2's sheet at any time he wishes to, provided he has edit access to the spreadsheet(which is needed, if you want to display the login dialog).

Google Sheet Bespoke Reporting: Filter & Privacy

I seek an approach by which I can provide user a bespoke Google Sheet dashboard on the basis of some ID that is entered or transferred via URL.
To explain: as of now, raw data sits in a master Google Sheet and is processed and summarised in another Google Sheet dashboard that requires to enter an ID which acts as filter to the raw data so that the summary only presents insights associated with that particular ID and user - that works.
However, Each user should enter only their ID and see their summary. Right now all users have access to the same public Sheet and the possibility of parallel access is problematic.
How may I generate individual Sheets (one per user) that is based on a template?
Is this possible with default functionality, or Apps Script? Any advice is highly appreciated, thank you!
This is one option you can try, you can get the email ID of the user who opens the google spreadsheet like so:
function onOpen(e) {
var email = Session.getEffectiveUser().getEmail()
var ui = SpreadsheetApp.getUi()
ui.alert(email)
//doSomethingSpecificBasedOnEmail(email) call function that fliters data based on email ID
}
Note: There are few nuances to using Session.getEffectiveUser(). Based on permission and security setting, it can give you a blank user/email.
https://developers.google.com/apps-script/reference/base/session#getActiveUser()
Second Option:
If email ID is not an option and since you are ok with users entering an ID to access the data. This code will ask for an ID and create a copy of sheet called template and also set the value of A1 as the ID. The sheet can then use the ID to get ID specific data and make plots.
function onOpen(){
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Get ID:');
// Process the user's response.
if (response.getSelectedButton() == ui.Button.OK) {
var id = response.getResponseText();
var ss = SpreadsheetApp.getActive()
var lookupSheet = ss.getSheetByName(id)
if(lookupSheet == null){
var template = ss.getSheetByName("Template")
var newSheet = template.copyTo(ss)
newSheet.setName(id)
newSheet.getRange(1,1).setValue(id)
newSheet.activate()
} else
{
lookupSheet.activate
}
} else {
Logger.log('The user clicked the close button in the dialog\'s title bar.');
}
}
An example of this can be found here: https://docs.google.com/spreadsheets/d/1RPHUGKi7u9jJVc-3xCaYlrZ8kaHWvpl6gPZxUL1g32Y/edit?usp=sharing
Note: People can see each others sheet though, which I assume based on your post is not an issue. However, if that is not the case the best option is to use google Web App script.

Google Apps Script — Automatically Email and Share

I am attempting to write a small Google Apps script that will send a confirmation email and automatically share a folder with a logged-in user after they complete a form. Here is the script:
function formSubmitReply(e) {
MailApp.sendEmail({
to: Session.getActiveUser().getEmail(),
subject: "Keuka College Brand Download Center",
htmlBody:
"<p>Thank you for requesting access to the Keuka College Brand Download Center. Your access has been approved.</p>" +
"<p>You may access the download center by <a href='https://drive.google.com/a/keuka.edu/folderview?id=0B856ZeGoaGT_MG5BMEtEVGwzYkk&usp=sharing'>using this link,</a> " +
"visiting <a href='http://brand.keuka.edu'>Keuka College Brand Central,</a> or through your Google Drive.</p><p>Please contact the Office of Marketing and Communications " +
"with any questions you may have.</p>",
name: "Keuka College",
replyTo: "marketing#keuka.edu"
});
var folder = DocsList.getFolder('Brand Download Center');
folder.addViewer(Session.getActiveUser());
}​
This seems to be working, except it keeps emailing it to me -- not the user who is completing the form. I am not sure if it is sharing correctly.
Could someone provide some insight? This is my first time working with Google Apps script.
Session.getActiveUser() is the user that uses the form editor and the user that created the trigger that calls this function.
What you want us the user that filled the form.
Look at the documentation here to see how you can retrieve that value. Note that it will only be available if you are in a domain.
EDIT (sorry for the delay)
Here is a sample code to show how to get the form respondent in formApp (works only in GAFE or GA Business)
It will send you a mail with the name of the last form submitter (don't forget to create an installable onEdit trigger to test this).
function onSubmit(){
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var lastResponse = formResponses[formResponses.length-1];
var user = lastResponse.getRespondentEmail()
var userString = 'user = '+user;
MailApp.sendEmail(Session.getActiveUser().getEmail(),'form submission report',userString);
}