Multiple forms triggered to single sheet - javascript

I have a google spreadsheet that takes information from two different forms.
Manually triggering using the spreadsheet UI does not let you distinguish which form is specified when choosing "OnFormSubmit" in the triggers menu.
Thus, I am using the following code (for my sheet) to manage two different triggering events for two different form-submits:
function onOpen(e) {
var form = FormApp.openById('ID of Form 1');
ScriptApp.newTrigger('onForm1Submit')
.forForm(form)
.onFormSubmit()
.create();
var signup = FormApp.openById('[ID of Form 2]');
signup.setRequireLogin(true)
ScriptApp.newTrigger('SignUpEvent')
.forForm(signup)
.onFormSubmit()
.create();
}
function SignUpEvent(e) {
\\stuff
}
function onForm1Submit(e) {
\\stuff
}
But when I do it this way, I am recieving a failure notification on form submit:
Function: UpdateLadder
Error Message: "Authorization is required to perform that action."
Trigger: "formSubmit"
First off, how am I recieving these email notifications to begin with? I didn't manually ask for email notifications of error messages.
Secondly, what's the best way for me to get "authorization"?

For the onFormSubmit() function, you can write code to match the destination sheet to a certain function. For example, I have a spreadsheet that has 2 forms that both have sheets for destination within it. In the onFormSubmit() trigger code, here is what I did:
function onFormSubmit(e) {
Logger.log("[METHOD] onFormSubmit");
var sheet = e.range.getSheet();
var sheetId = sheet.getSheetId();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var student = ss.getSheetByName("Student Requests");
var studentId = student.getSheetId();
var teacher = ss.getSheetByName("Teacher Requests");
var teacherId = teacher.getSheetId();
if (sheetId == studentId){
sendStudentEmail(e.range);
}
if (sheetId == teacherId){
sendTeacherEmail(e.range)
}
}
So, in this example, a Teacher can request using a form, and if the onFormSubmit is related to the Teacher form, then send the Teacher email, etc..

I'm reasonably certain that onFormSubmit ties one spreadsheet to one form; it doesn't know what to do with two onFormSubmit triggers.

Related

How can I send email notification to the mail address I entered in the form

I want to send email notification the mail address I entered in the response.
I tried this script in google forms Apps Script but I am unable to read "<EMAIL_ADDRESS>" in the last code. Have added a google form link as well with this script. Thanks!!
https://docs.google.com/forms/d/1io_qYZTnf97Yyotw4hdWTLfaQElALaLx0TBifHPge7c/edit
// Restrict the script's authorization
// to the form it is bound to.
//#OnlyCurrentDoc
// Create a form submit installable trigger
// using Apps Script.
function createFormSubmitTrigger() {
// Get the form object.
var form = FormApp.getActiveForm();
// See if the trigger has already been set up.
// Since we know this project should only have a single trigger
// we'll simply check if there are more than 0 triggers. If yes,
// we'll assume this function was already run so we won't create
// a trigger.
var currentTriggers = ScriptApp.getProjectTriggers();
if(currentTriggers.length > 0)
return;
// Create a trigger that will run the onFormSubmit function
// whenever the form is submitted.
ScriptApp.newTrigger("onFormSubmit").forForm(form).onFormSubmit().create();
}
// A function that is called by the form submit
// trigger. The parameter e contains information
// submitted by the user.
function onFormSubmit(e) {
// Get the response that was submitted.
var formResponse = e.response;
// Get the items (i.e., responses to various questions)
// that were submitted.
var itemResponses = formResponse.getItemResponses();
// Create a variable emailBody to store the body
// of the email notification to be sent.
var emailBody = "New form response:\n\n";
// Put together the email body by appending all the
// questions & responses to the variable emailBody.
itemResponses.forEach(function(itemResponse) {
var title = itemResponse.getItem().getTitle();
var response = itemResponse.getResponse();
emailBody += title + "\n" + response + "\n\n";
});
// Send the email notification using the
// sendEmail() function.
sendEmail(emailBody);
}
// A function that sends the email
// notification.
function sendEmail(emailBody) {
MailApp.sendEmail("<EMAIL_ADDRESS>", "New form response", emailBody);
}`
Tried reading the google form responses and fetching the mail address.

SpreadsheetApp / FormApp - Changelog

I have a script that records a log of all changes in a spreadsheet:
function onEdit() {
var timestamp = new Date();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getActiveCell();
var user = Session.getActiveUser();
var columnLabel = sheet.getRange(1, cell.getColumn()).getValue();
var changelogSheet = ss.getSheetByName("Changelog");
changelogSheet.appendRow([timestamp, cell.getA1Notation(), columnLabel, cell.getValue(), user.getEmail()]);
}
This codes beautifully creates a row of data in "Changelog" sheet every time there is an edit on "Sheet1". It tells you the date and time, the cell where it was edited, the contents of edit and user who edited it.
My issue is that "Sheet1" is linked to a Google Form that allows users to edit their responses. When users indeed edit their responses on Google Forms, the edit is not registered on my script and there is no log for it.
I presume my script only logs physical edits on the sheet itself.
Is there a way (perhaps using FormApp) to do a similar code that logs every edit on a Google Form?
I really appreciate your help.
You should create a new onFormSubmit trigger that will include similar logic and create a new row in the Google Sheet when a form response is submitted.

Script to delete a user account from G Suite Admin SDK

I am writing a script that fetches a data from a spread sheet and based on every entry entered in the spread sheet, it fetches the data which is basically email addresses and deletes a user's account from the domain.
//** Delete the users on submitting the "Submit" button
function onFormSubmit(e) {
//Logger.log(e);
//Run this function passing on event "e" when the form is submitted.
//Object e has form parameters sourced from the sheet attached to the form
deleteUsers(e);
}
//Logs all the info from the spreadsheet and deletes the user
function deleteUsers() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
Logger.log('managerEmail: ' + data[i][0]);
Logger.log('email: ' + data[i][1]);
Logger.log('trasferToEmail: ' + data[i][3]);
var managerEmail = data[i][0];
var email = data[i][1];
var trasferToEmail = data[i][3];
var request = {
'url': 'https://www.googleapis.com/admin/directory/v1/users/' + email,
'method' : 'DELETE'
};
}
But I am still unsuccessful in deleting an account. I actually tried to implement it based on this doc https://developers.google.com/admin-sdk/directory/v1/reference/users/delete but didn't know how to use it. Any ideas? Sorry if this is a stupid question! I am a novice in Google scripts.
You're sending e to deleteUsers(), but that function doesn't receive any parameters. No need to access the spreadsheet data when it's already provided by the onFormSubmit()–look at the event object documentation for reference.
function deleteUser(e) {
var data = e.namedValues;
var managerEmail = data["Manager Email"][0]; //You'll need to adjust the field names to match with your form data
var email = data["Email"][0];
var transferToEmail = data["Transfer to Email"][0];
var response = AdminDirectory.Users.remove(email);
}
To make sure that your trigger is correctly set up, first have the form responses get saved to your spreadsheet. Then Edit > Current project's triggers and copy these settings:
To make AdminDirectory work, you need to enable advanced services. (In the script editor, go to Resources > Advanced Google services and switch "on" Admin Directory API. Then click the link at the bottom of the modal to enable the Admin SDK in the API Console.)
If I was mistaken about what data the form is collecting and you really do need to pull the data from spreadsheet (assuming the form isn't connected to the sheet), then you need to create a trigger for when there is a submission to that form. Run this function to install the trigger.
function installFormTrigger() {
var form = FormApp.openById("FORM_ID");
ScriptApp.newTrigger("deleteUsers")
.forForm(form)
.onFormSubmit()
.create();
}
Then your original deleteUsers() function will work almost as you had it, but with the addition of AdminDirectory.
function deleteUsers() {
var sheet = SpreadsheetApp.getActive().getSheetByName("SheetName"); //HIGHLY recommend using this instead
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
var managerEmail = data[i][0];
var email = data[i][1];
var trasferToEmail = data[i][3];
var response = AdminDirectory.Users.remove(email);
}
}
Note that in your for loop, you might come across an invalid email or AdminDirectory could through an error, so I'd suggest implementing try...catch and logging.
It would be better to use AdminDirectory.Users.remove(email); rather than making a request to the API like you are doing.
Keep it in a variable if you want to log the response var request = AdminDirectory.Users.remove(data[i][1]);
To activate the AdminDirectory;
Go to Resources -> Advanced Google services
Enable the Admin Directory and then click on the link to the Google API console.
Click Enable APIs and Services
Search for Admin SDK
Click on Admin SDK and then click 'Enable'

How to detect the change when my google sheet is updated and send an Email

I have been trying to send an email when my sheet gets new values, The sheet is getting values automatically, and email needs to be sent to a specific Email address, and it needs to be sent to every new entry, so google integrated "Notify on change is out of the question".
Here is my scenario:
my source sheet: "Leads"// name of the sheet
my target sheet: "Closer"//name of the sheet
when a new row is inserted into the Leads sheet Its values automatically get inserted into the Closer sheet in the end. So for each new values inserted in the Leads, Closer gets a new row.
I have designed a script of my own for that but it sends me too many emails, what I want my script to do is when a new row is inserted send the email, whether that is to self or any email, I know how to send email I am just getting a lot of problems, with getting the change functionality in my script.
Here is what I have done so far:
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Leads");
var mycell = sheet.getRange("A:A");
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
var recipients = "someone#gmail.com";
var subject = "Notification for sheet updation "+ss.getName();
var body = ss.getName() + "has been updated. Visit " + ss.getUrl() + " to view the changes.";
if (mycell == 1)
{
MailApp.sendEmail(recipients, subject, body);
}}
This function works, but it sends 5,6 emails for each row submitted, I have tried many things but I cannot figure out what is the problem with this code.I just want my code to send one email for each row inserted, I would really appreciate some help with this script, Thank you.

How to get old Formula (not oldValue) from onEdit() in Google Sheet Script

Does anyone know if there's a way to access the old (ie, pre-edit) formula, as opposed to oldValue, of the edit event object? I thought e.oldFormula would work, but it's undefined and not even in the documentation.
I'm working on a script to require a password when editing certain ranges but, in order to undo the edit if the user fails to provide the correct password, I need to know what was in the cell before. For cells that contain numbers or strings, e.oldValue works great, but most of the cells I want to protect contain formulas (that's why I want to protect them) so e.oldValue stops the data from updating like it should.
Here's the code I've got that only works if the cell contains no formulas (other than that, it works great:
// Prompt for password before allowing edit of formula cells
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getActiveCell();
var editRange = e.range;
var editCol = editRange.getColumn();
if (editCol != 4 ) {
var password = "321"; // not actual password
var passwordAttempt = Browser.inputBox('Enter Password to edit protected range, or hit cancel to cancel edit:', Browser.Buttons.OK_CANCEL);
if(passwordAttempt == password) {
Browser.msgBox('Edit Accepted');
} else {
if (passwordAttempt != 'cancel'){
Browser.msgBox('Incorrect password. Contact James Atkins for password to edit protected ranges.');
}
if(e.oldValue){
Browser.msgBox('old value is defined as ' + e.oldFormula);
e.range.setValue(e.oldValue);
} else {
e.range.clear();
}
}
FYI, I'm doing this because the built-in protected ranges in Google Sheets don't distinguish between a user changing the contents of a cell by manually editing it and by activating a script that changes the contents. I want to allow all users to activate scripts that sort and clear certain ranges, but not let them mess with the contents manually.
You can solve this problem by creating a clone of your sheet to keep track of your formula. Make the clone sheet off limits to end users. So they cannot edit it.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cloneSheet = ss.getSheetByName("cloneSheet")
var cell = sheet.getActiveCell();
var editRange = e.range;
var cloneRange = cloneSheet.getRange(editRange.getA1Notation())
var editCol = editRange.getColumn();
if (editCol != 4 && sheet.getName() != "cloneSheet") {
var password = "321"; // not actual password
var passwordAttempt = Browser.inputBox('Enter Password to edit protected range, or hit cancel to cancel edit:', Browser.Buttons.OK_CANCEL);
if(passwordAttempt == password) {
Browser.msgBox('Edit Accepted');
cloneRange.setValue(editRange.getFormula()) //modify the clone to match the new formula.
} else {
if (passwordAttempt != 'cancel'){
Browser.msgBox('Incorrect password. Contact James Atkins for password to edit protected ranges.');
}
if(e.oldValue){
Browser.msgBox('old value is defined as ' + cloneRange.getFormula());
e.range.setValue(cloneRange.getFormula());
} else {
e.range.clear();
}
}
}
}
However, end user can still see the password. If you really what end user to make only limited changes the below solution is better.
Alternate Solution
When an end user executes a script, even though you have written the script, it runs with scope/identity of the end user. So if they don't have edit permission then the script that runs under their alias also does not have it either. This is in response to your comment:
google sheets don't distinguish between a user changing the contents of a cell by manually editing it and by activating a script that changes the contents.
So how to get around this problem, you can use a google web app to run sort and clear scripts as you. The end user uses a script that makes a call to this script using UrlFetch, this gets around the problem of scopes and prevents them from having a look at your code/password etc.
Here is SO question that addressing a similar problem:
Google Apps Script: Temporarily run script as another user

Categories