Project Trigger Not working - google-apps-script

I'm using Cognito forms to collect information and then using Zapier to pass this information to my google spreadsheet database. I have found that my scripts in google spreadsheets do not trigger the way I would expect.
This one:
function onEdit(event){
var ColCR = 96; // Column Number of "CR"
var changedRange = event.source.getActiveRange();
if (changedRange.getColumn() == ColCR) {
var state = changedRange.getValue();
var adjacent = event.source.getActiveSheet().getRange(changedRange.getRow(),ColCR+1);
var adjacentv = adjacent.getValue();
var timestamp = Utilities.formatDate(new Date(), "GMT-7", "M/dd/yy', 'h:mm a");
switch (adjacentv) {
case "":
adjacent.setValue("("+timestamp+")"+" "+state);
changedRange.clearContent();
break;
default:
adjacent.setValue(adjacentv+"\n"+"("+timestamp+")"+" "+state);
changedRange.clearContent();
break;
}
}
}
Works fine when a user edits the spreadsheet cell directly but not when Zapier updates the cell. It also works on several different types of triggers when the spreadsheet is modified by a user. What is the difference between editing the spreadsheet directly vs having an app like Zapier edit the form? Can I write a script that would see an edit by an app like Zapier?
This one:
function Timestamp() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var cell = sheet.getActiveCell();
var comments = cell.getComment();
var formattedDate = Utilities.formatDate(new Date(), "GMT-7", "M/dd/yy', 'h:mm a ");
Newline works in msgBox, but not in Note.
comments = comments + "Mod: " + formattedDate;
Browser.msgBox(comments);
cell.setComment(comments);
}
Works fine when a user edits a cell and is triggered by a On Change trigger and also works when Zapier updates a cell but when Zapier updates more then one cell in a row it only puts the comment in the first cell in the range. How would I modify the script so that it triggers on every change to every cell and not just the range?
Im sure it has something to do with how Zapier is interacting with my spreadsheet but I don't understand how Zapier edits are any different than user edits?
Thanks for any suggestions or recommendations.

I've experienced this issue as well, and in the end came to the conclusion that the Zapier action uses the API to update the Google Sheet, thus not triggering the trigger. Details available here in Google's documentation of Simple Triggers:
https://developers.google.com/apps-script/guides/triggers/

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.

Adding a number to an event in a Sheet once a Form has submitted data to the Sheet

I am trying to create a script to add an "Incident" number to the Incident Number column (Column 1) when a form is submitted and the Data is added to the linked Google Sheet (Sheet Name "IIR") by detecting new data in Column 4. Row one contains the Sheet Headers (developed by the questions in the Form). I pilfered a script with the same intent from other sources, the Script runs with no errors but I am not getting the desired result to the sheet (I am a Newbee).
//CORE VARIABLES
// The column you want to check if something is entered.
enter code here var COLUMNTOCHECK = 4; // Where you want the date time stamp offset from the input location. [row, column]
var ADDNUMBER = [1,1]; // Sheet you are working on
var SHEETNAME = "IIR"
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("IIR"); //checks that we're on the correct sheet.
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell(); //checks the column to ensure it is on the one we want to cause the number to appear.
if( selectedCell.getColumn() == COLUMNTOCHECK) {
var IncidentNumber = selectedCell.offset(ADDNUMBER[1],ADDNUMBER[1]);
for (r=1; r<999; r++) {
ss.getRange(r,1).setValue(r);
}
}
}}
Append incident number to time stamp in linked sheet
This function will add a incident number that is made up from the time stamp by using a two digit year followed by a two digit month followed by a two digit day and then a dash and then consecutive incrementing numbers. It also keeps the timestamp and adds the incident number to the bottom of the cell.
function formSubmit(e) {
if(e.values && e.values[1]) {
Logger.log(JSON.stringify(e));
var sh=e.range.getSheet();
sh.getRange(e.range.rowStart,1).setValue(getIncidentNumber(e));
}
}
function getIncidentNumber(e) {
var ps=PropertiesService.getScriptProperties();
var ts=e.values[0];
var key=Utilities.formatString('%s%s%s',ts.slice(8,10),ts.slice(0,2),ts.slice(3,5));
if(ps.keys && ps.getKeys().indexOf(key)==-1) {
ps.setProperty(key,1);
return key + "-" + 1;
}else{
var value=ps.getProperty(key);
ps.setProperty(key, Number(value) + 1)
var rv = ts + '\n' + key + "-" + Number(value).toFixed();
return rv;
}
}
If you want to get the Incident number from the sheet you should open the Script editor from the Form. In this way, the script will be bound to the actual form.
Afterwards, you should be using the following script:
var DESTINATION_SS_ID = "YOUR_DESTINATION_SHEET_ID";
function onFormSubmit(e) {
var incidentNumber = e.response.getItemResponses()[2].getResponse();
var ss = SpreadsheetApp.openById(DESTINATION_SS_ID);
ss.appendRow([incidentNumber]);
}
The script is using an installable trigger and instead of going through the bounded sheet from the form, like you did, it takes the response directly from the form and adds it to your wanted spreadsheet. This is done by using the onFormSubmit installable trigger.
To install the trigger you should follow these steps:
Go to your project's triggers by clicking this icon:
Create a new trigger with the following settings:
Save the project.
For the trigger to work, you/other users just have to send responses through the form. The results are obtained by using the .getItemResponses() method followed by the .getResponse() method, which essentially get you the responses you/other users have sent. Afterwards, these responses are added to the sheet by using the .appendRow() method; so every time a new entry gets submitted in the form, the Incident number is automatically added to your spreadsheet.
Furthermore, I suggest you take a look at these links since they might be of help in your future development:
Installable Triggers;
Event objects - Form submit;
FormResponse Class;
ItemResponse Class;
Sheet Class - appendRow().

Google sheets - User/Email stamp triggered ON EDIT works only with OWNER account - how to enable user/email stamps for all editors?

I need a solution to enable all editors to leave their name stamps after they make changes in a sheet. I was able to come up with script code which does work ONLY FOR OWNER.
I tried to authorize the script by running the script manually from editors accounts - the app has the authorization but even though it doesn't work for Editors.
Norbert Wagner: "However authorizing from the script editor did not work for me. But as soon as I added a menu in the document and executed the function from there, I got an authorization request in the document. From then on also the onEdit function works, for all users. I did this as the documents owner though. " -
maybe this is the solution? But how can I run the onEdit from the
document? Simple edit doesn't work, but how about this menu? Is there
a way to execute ALL SCRIPTS AND FORCE AUTHORIZATION from document level?
function onEdit(e) {
// Your sheet params
var sheetName = "Arkusz1";
var dateModifiedColumnIndex = 3;
var dateModifiedColumnLetter = 'C';
var range = e.range; // range just edited
var sheet = range.getSheet();
if (sheet.getName() !== sheetName) {
return;
}
// If the column isn't our modified date column
if (range.getColumn() != dateModifiedColumnIndex) {
var row = range.getRow();
var user = Session.getActiveUser().getEmail();
user = String(user);
user = user.replace('#gmail.com', '');
var dateModifiedRange = sheet.getRange(dateModifiedColumnLetter + row.toString());
dateModifiedRange.setValue(user);
};
};
This function works only when using owner account.
Even after Editor has authorised script manually from script editor - the onEdit user stamp function doesn't trigger.
Below is another function which I used for testing. When I run it manually from editors account IT WORKS - it saves editor's name in A1.
function USERNAME(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0]; // this will assign the first sheet
var cell = sheet.getRange('A1');
var d = new Date();
d.setHours(d.getHours() +1);
var user = Session.getActiveUser().getEmail();
user = String(user);
user = user.replace('#gmail.com', '');
cell.setValue(user);
}
In fact you have to use installable trigger.
Rename your function onEdit(e) with another name for example customFunction(e)
Then you setup an installable trigger using this function :
function createTrigger(){
ScriptApp.newTrigger('customFunction')
.forSpreadsheet(SpreadsheetApp.openById('YOU_SHEET_ID'))
.onEdit()
.create();
}
Or you can setup the trigger manually by going to 'Edit >> Current project's triggers >> Click on + button (bottom right)'
By this way trigger will run each time there is an Edit no matter the user.
Stéphane

How do I protect a cell range from editing only by owner?

I have a Google Sheet with an onEdit trigger running Google Apps Script code to add cell range protection. The idea is that after editing a cell, the spreadsheet will be locked from editing. You should not be able to delete or modify a cell after adding your data, so the spreadsheet can act as a ledger of sorts.
Anyway, here is the relevant code from the trigger:
// Remove existing protected ranges
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0;i<protections.length;i++) {
if (protections[i].getDescription() === sheetname + range) {
protections[i].remove();
}
}
// Add Protected Range to prevent further editing up to the second from last row
var lastRow = SpreadsheetApp.getActiveRange().getLastRow() - 1;
var protectedRange = selectedSheet.getRange("A1:J".concat(lastRow));
var protection = protectedRange.protect().setDescription(sheetName() + ' Protection Range');
// Note: The spreadsheet owner is always able to edit protected ranges and sheets.
var sheetOwner = SpreadsheetApp.getActiveSpreadsheet().getOwner();
protection.addEditor(sheetOwner); // Owner is the only one who can edit.
protection.removeEditors(protection.getEditors()); // Remove all editors.
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
This code successfully adds the protection to the cells, however, it is always Editable by the current logged in user (so the Owner + current user can Edit), thus defeating the protection.
I have tried something like the following:
var sheetOwner = SpreadsheetApp.getActiveSpreadsheet().getOwner();
var me = Session.getEffectiveUser();
protection.addEditor(sheetOwner);
protection.removeEditor(me);
and also something like the following:
protection.addEditors(['email#email.com']);
protection.removeEditors(protection.getEditors());
but it seems like no matter what I do, the current user always gets granted with Edit permissions. Why is that? How do I remove the Edit permissions of the current user for the protected range?
I am not any code writer at all but somehow managed to restrict users from editing once they enter any data in a cell.
I am using below simple code and trigger is on edit. This auto lock a cell once input is given. Just mention email id of all users (whom edit right is given) in remove editors in below code
function LOCKALL() {
var spreadsheet = SpreadsheetApp.getActive();
var protection = spreadsheet.getActiveRange().protect();
protection.setDescription('LOCKALL')
.removeEditors(['mention email id' , 'mention email id']);
};

Delete old data rows in sheet using google forms

I want the sheet that collects data from a google form to delete old data. I have this script in the sheet but it doesn't seem to run when new data is added via the form.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("1054");
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
var currentDate = new Date();
var oneweekago = new Date();
oneweekago.setDate(currentDate.getDate() - 7);
for (i=lastrow;i>=2;i--) {
var tempdate = values[i-1][0];
if(tempdate < oneweekago)
{
sheet.deleteRow(i);
}
The sheet it is linked to is called 1054.
Do I need to put appscript in the Form? If so, what would that be?
I think it would be better to leave the sheet and the form it is attached to alone. Here's what I do on a form that's on my website.
function formSubmitEvent(e)
{
var ss=SpreadsheetApp.openById('SpreadsheetID');
var sht=ss.getSheetByName('ResponseReview');//this is another sheet I used for reviewing the responses.
sht.appendRow(e.values);//I append a row to the ResponseReview sheet
selfEmail(e.values);//and I send myself an email if my quota is above 75
}
Even if you delete old data on the linked sheet the form will continue adding data where it left off because evidently it keeps track of what the next row is.
In general, I leave the linked sheet alone. It is true that I built my form in Googgle Apps Script so at some point when I get a lot of responses then I will unlink the form and delete both and rebuild the form by running that script and a new form and sheet will be linked but I will continue my review process in the other sheet which also has the capability to create a Google Doc from the submitted data. In essence, it doesn't really matter to me where the form wants to put the data because I capture the onFormSubmit trigger and I put into my ResponseReview sheet from the values array. And I can do anything I want with that sheet without affecting the linkage.