User/Email stamp with onEdit Google Sheets - google-apps-script

What I want to do is to place the user's email who is clicking the checkbox to true in the cell directly next to the checkbox that they're clicking on. I've got the code so that I can place my own email there. I've even played with it to the point where it will remove the "#gmail.com," but I cannot get this to show for other users.
I had it, at one point, placing the current users emails in the box, but then it would quickly change to my own. However, I don't remember the state of the code as it was then, and haven't been able to replicate it.
I've tried everything suggested here.
I've also referenced this post here.
Here is my code:
function createTrigger(){
ScriptApp.newTrigger('myFunction').forSpreadsheet(SpreadsheetApp.openById('1Y0-bpAhGzPSS5DIPnoZH4OcY2b9IL8LhvBfc2is0zVU')).onEdit().create();
}
function myFunction(e) {
var groupOneCheck = 3;
var groupTwoCheck = 7;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var checkCell = ss.getActiveCell();
if (checkCell.getColumn() == groupOneCheck) {
var dateTimeCell = checkCell.offset(0,2);
var userCell = checkCell.offset(0,1);
dateTimeCell.setValue(new Date());
userCell.setValue([Session.getEffectiveUser().getEmail()]);
}
if (checkCell.getColumn() == groupTwoCheck) {
var dateTimeCell = checkCell.offset(0,2);
var userCell = checkCell.offset(0,1);
dateTimeCell.setValue(new Date());
userCell.setValue([Session.getEffectiveUser().getEmail()]);
}
}
I want to do the same thing for both of the if statements because they correspond to different columns.
Here is a link to the sheet that I am currently testing with.

It works for me if I use an installable trigger because simple triggers cannot perform functions that require permission.

Related

onFormSubmit() not working in Google Form?

[UPDATE]
I had a look at add-ons and I am afraid this won't work. So let me take a step back and describe what I am trying to achieve.
I have a spreadsheet A, with a list of individual events. Each event is a line item in the spreadsheet. The spreadsheet is very long for one, and has many fields that I don't need to expose to event owners (different events different owners). Which means if I allow all these different people edit access to the sheet, it becomes really chaotic.
The solution I came up with is to generate unique IDs programmatically for each event, which I've done. Then for each event, I create an individual form and a pre-filled link, with pre-filled answers that is pulled from the cell values. I intend to give the pre-filled links to event owners when they need to make any updates.
The issue is now I have 100+ forms, and I don't want to have 100+ corresponding tabs set as destinations of these forms. These 100+ forms need to submit responses to one same sheet (tab). Instead I wrote a function for submitted responses to find the right event (the event unique ID is the title of the form) and updates the right cell. This is what you see below processSubmission().
I have tried to write the processSubmission() in the spreadsheet where the events are listed. If I don't set this spreadsheet as destination of these 100+ forms then the spreadsheet doesn't know there is a "submission" event. Therefore the setting the trigger onFormSubmit() in the spreadsheet doesn't work.
Then I moved onFormSubmit() -> processSubmission() and it doesn't set the trigger because as you all pointed out, it's an installable trigger.
What I did manage to to write an onOpen() -> create the onFormSubmission() trigger. That means I had to manually open 100 forms and close them to create that trigger. The triggers are created alright. But turned out for the trigger to actually run I need to manually grant permission!
When I looked at add-on triggers, it says "Add-ons can only create triggers for the file in which the add-on is used. That is, an add-on that is used in Google Doc A cannot create a trigger to monitor when Google Doc B is opened." So I think that also rules out the add-on triggers. So now I am out of ideas.
[ORIGINAL]
I made a custom function for the processing of submission responses. I use the form title as a key, and the response answers are written to the corresponding headers in the row with the right key.
My first try was something like this. But it simply didn't execute when the form was submitted:
function onFormSubmit(e){
var form = FormApp.getActiveForm();
var key = form.getTitle();
var responses = e.response;
var ss= SpreadsheetApp.openById(ss_id);
var sheet = spreadsheet.getSheetByName('Launch list');
var frozenRow = sheet.getFrozenRows();
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var headers = sheet.getRange(1, 1, 1, lastColumn).getValues()[0];
var keyCol = headers.indexOf(key_header) + 1;
var header1Col = headers.indexOf(header_1) + 1;
var header2Col = headers.indexOf(header_2) + 1;
var header3Col = headers.indexOf(header_3) + 1;
var keysRange = sheet.getRange(frozenRow+1, keyCol , lastRow - frozenRow, 1);
var allKys = keysRange.getValues();
for (i=0; i<allKys.length; i++){
var keyValue = allKys[i][0];
if (keyValue === key){
var rowNum = l + frozenRow + 1;
break;
}
else {
continue;
}
}
var dataRow = sheet.getRange(rowNum, 1, 1, lastColumn).getValues()[0];
var lookUp = {};
lookUp[item_title_1] = header1Col ;
lookUp[item_title_2] = header2Col ;
lookUp[item_title_3] = header3Col ;
var items = form.getItems();
var cnt = 0;
var changes = [];
for (i=0; i< items.length; i++){
var item = items[i];
var title = item.getTitle();
var itemResponse = responses.getResponseForItem(item);
var existingValue = dataRow[lookUp[title] -1];
if ((itemResponse.getResponse() !=='' || itemResponse.getResponse() !== undefined) && itemResponse.getResponse() != existingValue){
cnt++;
var cell = sheet.getRange(rowNum, lookUp[title], 1, 1);
cell.setValue(itemResponse.getResponse());
changes.push(title);
}
else {
continue;
}
}
Logger.log('Made ',cnt,'changes for launch ',featureID,': ',changes);
}
I also tried a slightly different approach but also didn't work:
function onFormSubmit(){
processSubmission();
}
// Processing form submission
function processSubmission() {
var form = FormApp.getActiveForm();
var key = form.getTitle();
var responses = form.getResponses()[form.getResponses().length-1];
// The rest is the same.
}
Manually running the function in the second approach proved my function processSubmission() works. Manually add a onFormSubmit() trigger via the Apps Script Dashboard is not going to be possible because I am generating hundreds of forms (one for each key) programmatically so I chose to have onFormSubmit(e) in the template and every new form is a copy of the template which should also have copies of these functions. But it just doesn't work! Any insight?
The onFormSubmit trigger is an installable trigger which means that it requires to be set up before being able to use it.
It's also important to keep in mind the following, according to the installable triggers documentation:
Script executions and API requests do not cause triggers to run. For example, calling FormResponse.submit() to submit a new form response does not cause the form's submit trigger to run.
What you can do instead is to create the trigger programmatically, something similar to this:
function createTrigger() {
ScriptApp.newTrigger('onFormSubmit')
.forForm('FORM_KEY')
.onFormSubmit()
.create();
}
Reference
Apps Script Installable Triggers;
Apps Script FormTriggerBuilder Class.

How to get access new sheet, created by adding a new form to spreadsheet?

Im quite new to this thing and never had to post a question before.
Here is the situation :
I want to copy the same form over and over to the same spreadsheet('memoSs'). (original form being 'formFileSample').
I am trying to access the sheet linked to that newly added form. (form added using .setDestination()
Unfortunately that sheet is missing from the array returned by '.getSheets'!
Even though it appears in the spreadsheet 'memoSs' (checked by opening it)
I hope it is understandable with the script below.
any chance somebody might have a way in doing so?
function updateOfMemoSs() {
var memoId = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
var Interface = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Interface');
var memoSs = SpreadsheetApp.openById(memoId);
var formFileSample = DriveApp.getFileById(memoSs.getSheetByName('Sample (Source)').getFormUrl().match(/[-\w]{25,}/));
var sheetsToCopy = setInstrList(); //sets a list of names for the sheets to be created
//looks like [['xxx'],['xx'],['x']]
for(var i in sheetsToCopy){
var newFormId = formFileSample.makeCopy('memo '+sheetsToCopy[i][0]).getId();
var newForm = FormApp.openById(newFormId).setDestination(FormApp.DestinationType.SPREADSHEET, memoId);
// var memoSs = SpreadsheetApp.openById(memoId); //didnt work
var sheets = memoSs.getSheets().filter(sheet => sheet.getFormUrl()); //sets a list of linked sheets
Logger.log('formIds = ');
for(var j in sheets){
Logger.log(sheets[j].getName());
Logger.log(sheets[j].getFormUrl().match(/[-\w]{25,}/));
}//returns a list that does not include the linked sheet created earlier.(even though it appears in spreadsheet)
/* for(var j in sheets){
var sheet = sheets[j];
if(sheet.getFormUrl().match(/[-\w]{25,}/) == newFormId){
var newSheet = sheet; //therefore I never find a match for newFormId
}
}*/
}
// Logger.log('newSheet = ');
// Logger.log(newSheet.getName());
}
Thanks Cooper for considering the question.
It seems i haven't been patient enough in my research as the answer had already been posted in StackOverFlow : 'getSheets() Google Apps Script multiple calls issue'
Somehow after creating a sheet.The following should be called before calling getSheets() :
SpreadsheetApp.flush()

Google Scripts - Can't Find Destination Sheet After Form Creation

My script creates a Google Form programmatically and sets the destination to the current Spreadsheet:
var sheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var form = FormApp.create(acctName).setTitle(acctName).setDestination(FormApp.DestinationType.SPREADSHEET, sheetId);
// several form items added here
Next, I'd like to rename the destination Sheet and make a few formatting edits. When I do, however, the sheet doesn't seem to exist. For example, getSheets() does not contain the new Sheet! (And yes, ideally I'd like to open the sheet by ID; see function below which also doesn't work.)
var sheets = SpreadsheetApp.openById(form.getDestinationId()).getSheets();
SpreadsheetApp.setActiveSheet(sheets[0]);
The above code opens what I would consider index 1, not index 0, because the sheet isn't indexed. I even tried creating a few second delay (hoping that it was just a matter of time to allow the sync to happen between the client and server) but without any success.
I have also tried something like the following, as suggested elsewhere here on Stack Overflow, but the destination Sheet doesn't come up in getSheets() even when called through a separate function:
function getFormDestinationSheetId(form) {
var destinationId = form.getDestinationId();
if(destinationId) {
var spreadsheet = SpreadsheetApp.openById(destinationId);
spreadsheet.getSheets().forEach(
function(sheet) {
var sheetFormUrl = sheet.getFormUrl();
if(sheetFormUrl) {
var sheetForm = FormApp.openByUrl(sheetFormUrl);
if(sheetForm.getId() == form.getId()) {
return sheet.getSheetId();
}
}
}
);
}
return null;
}
I haven't been able to find anyone have a similar problem on the webs. Any advice would be appreciated. Thanks!
Welcome to Stack!
I assume your script is bound to a sheet? Depending on how you're calling the script you may not see the new sheets because of browser caching.
function myFunction() {
var sheet = SpreadsheetApp.getActive();
var sheetId = sheet.getId();
var form = FormApp.create('acctName').setTitle('acctName').setDestination(FormApp.DestinationType.SPREADSHEET, sheetId);
var ssSheets = sheet.getSheets();
var respSheet = ssSheets[0]
respSheet.getRange('A1').setBackground('RED');
respSheet.getRange('C1').setFormula('=COUNTA($C$2:$C)');
respSheet.setColumnWidth(2, 100);
SpreadsheetApp.flush();
}

Undo ability not working after adding Script

I have a script to hide or show Rows with a specific value, it is working well, only I have problem that if I do change the value of any cell in the Spreadsheet even in other sheet like (sheet2) and I want to Undo that change, the Undo ability not working till I repeat it more than 30 times!!! And If I delete the script, it works normally.
Do I have to add, change or delete any code of this script to make Undo Ability working normally as before adding script?
Thank you
I tried to changed this line: var ss = SpreadsheetApp.getActiveSpreadsheet();
to let Undo working normally
or to make it atleast be affecting in specific sheet but I could't.
Here is the full script...
function myShowHide() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("sheet1");
var lastRow = sheet.getLastRow();
for (i = 4; i <= lastRow; i++) {
var status = sheet.getRange("J" + i).getValues();
if (status == "X") {
sheet.showRows(i);
}
}
var sheet = ss.getSheetByName("sheet1");
var lastRow = sheet.getLastRow();
for (i = 4; i <= lastRow; i++) {
var status = sheet.getRange("J" + i).getValues();
if (status != "X") {
sheet.hideRows(i);
}
}
}
Assuming that you are running this with an installable onEdit() trigger then this will solve the problem of interacting with other sheets.
function myShowHide(e) {
var sh=e.range.getSheet();
var rg=sh.getRange(4,10,sh.getLastRow(),1);
var sA=rg.getValues();
for(var i=0;i<sA.length;i++) {
if(sA[i][0]=="X") {
sh.showRows(i+4);
}else{
sh.hideRows(i+4);
}
}
}
I setup an installable onEdit trigger for this function.
function myNewShowHide(e) {
var sh=e.range.getSheet();
if(sh.getName()!="Sheet1")return;
myShowHide(e);
}
But solving the problem on Sheet1 depends upon how you want the sheet to run. If you run it with an edit trigger then anytime your editing anywhere on the sheet it runs. You could limit the edit range but that requires knowing what you want to do with the sheet and I don't know that.
My recommendation is not to run this kind of function with an onEdit() trigger. This one will run much faster because I get all of the data at one time but the rows are still shown or hidden one at a time.

onEdit(e) DriveApp.getFileById

Having issues getting the following to work.
The intent was on edit to push information from the specific cell or cells of active sheet to specific cells on a separate worksheet.
Note: I am new to google sheets
function onEdit(e) {
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Working");
var cell = source .getActiveCell();
if (cell.getRow() == 9 && cell.getColumn() == 2) {
var target = DriveApp.getFileById("1biaIVlafaNQTHjtR8ctASCpDmC2O1wwfJfAUCmzIztI")
.getSheetByName("Master_Sheet");
target.getRange("A1").setValue(cell.getValue());
}
}
The reason it does not work is because you are using onEdit(). This is a simple trigger that will fire off whenever you edit the sheet. Since simple triggers cannot perform operations that require authorization you are limited to working only in the Spreadsheet and cannot access any other files.
Read up on restrictions here
I am now able to push information to the target sheet using the following code.
function myFunction() {
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Source");
var sourceData = sourceSheet.getRange("A3:C3").getValues();
sourceData.splice(0,1); // Remove header
var targetSS = SpreadsheetApp.openById("1pyJzZ86WDh2FNXFufUAt2SkAUod32i7AzvG0EKmnvEU").getSheetByName("Destination");
var targetRangeTop = targetSS.getLastRow(); // Get # rows currently in target
targetSS.getRange(targetRangeTop+1,1,sourceData.length,sourceData[0].length).setValues(sourceData);
};