Google Sheet Appscript only collects my username and no other users - google-apps-script

I have a changelog script that only collects my username and does not collect for any other user. I'm trying to figure out the bug but unable to find a solution.
function changelog_script(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNameTracker = ss.getActiveSheet().getName();
if (sheetNameTracker !== "Changelog") {
let sheet = ss.getSheetByName('Changelog');
var range = e.range && e.range.getA1Notation();
var user = e.user.getUsername();
var function_source = "changelog_script";
var time = new Date();
var changeType = e.changeType || 'EDIT';
console.log(user);
var oldValue = e.oldValue;
var newValue = e.value;
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow + 1, 1, 1, 8).setValues([
[time.toLocaleString(), function_source, changeType, sheetNameTracker, range, user, oldValue, newValue]]);
}
}
function onChange(e) {
if (e.changeType == "EDIT") return;
changelog_script(e);
}
Example Google Sheet with attached App script.
Thanks in advance. 🙏

Related

how to use a project on more than one sheet

I have the code below in my project, that helps me autofill the timestamp in "datetime" column in "test" sheet.
I want it to work for other sheets too. But I couldn't get it to work. Any help?
var SHEET_NAME = 'test';
var DATETIME_HEADER = 'datetime';
function getDatetimeCol(){
var headers = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME).getDataRange().getValues().shift();
var colindex = headers.indexOf(DATETIME_HEADER);
return colindex+1;
}
function onEdit() {
var ss = SpreadsheetApp.getActiveSheet();
var cell = ss.getActiveCell();
var datecell = ss.getRange(cell.getRowIndex(), getDatetimeCol());
if (ss.getName() == SHEET_NAME && cell.getColumn() == 1 && !cell.isBlank() && datecell.isBlank()) {
datecell.setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm");
}
};
If you want to have an onEdit() trigger runs on multiple sheets use installable trigger.
Take your code and put it in a standalone script, not bounded to a sheet.
Then create function to setup onOpen Trigger
/**
* Creates a trigger for when a spreadsheet opens.
*/
function createSpreadsheetOnOpenTrigger() {
var id = 'YOUR_SHEET_ID';
var ss = SpreadsheetApp.openById(id);
ScriptApp.newTrigger('NAME_OF_FUNCTION_TO_RUN')
.forSpreadsheet(ss)
.onOpen()
.create();
}
reference : link
Then you just have to change id to setup trigger for all sheets you want the code run.
In the code take care to change to get infomration regarding celle and sheet from the event object :
function functionToRunOnEdit() {
var sheet = **e.range.getSheet()**;
var cell = **e.range**;
var name = sheet.getName();
var datecell = ss.getRange(cell.getRowIndex(), getDatetimeCol(sheet));
if (SHEET_NAMES.includes(name) && cell.getColumn() == 1 && !cell.isBlank() && datecell.isBlank()) {
datecell.setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm");
}
};
Reference : link
I have also changed the variable name ss into sheet becausse it is a sheet but not a spreadsheet.
var SHEET_NAMES = ['test', 'test2'];
var DATETIME_HEADER = 'datetime';
function getDatetimeCol(sheet){
var headers = sheet.getDataRange().getValues().shift();
var colindex = headers.indexOf(DATETIME_HEADER);
return colindex+1;
}
function onEdit() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var name = sheet.getName();
var datecell = ss.getRange(cell.getRowIndex(), getDatetimeCol(sheet));
if (SHEET_NAMES.includes(name) && cell.getColumn() == 1 && !cell.isBlank() && datecell.isBlank()) {
datecell.setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm");
}
};

how can remove all of the protection for all of user in google sheet script?

I have a Sheet which only columns A-C I want to share with user1, and only columns F-H to user2. I do this work with Apps Script. Neither user1 nor user2 are the owner. To execute some code I have to delete all the protection for all users. I wrote the code below for this, but it only works for the owner and not for other users.
function onOpen (e){
var s1 = SpreadsheetApp.getActive();
var range1 = s1.getRange('A:A');
var range2 = s1.getRange('B:J');
var range3 = s1.getRange('K:Z');
var range4 = s1.getRange('AA:AC');
var range5 = s1.getRange('AD:AE');
var range6 = s1.getRange('A1:AE1');
var protection1 = range1.protect().setDescription('1');
var protection2 = range2.protect().setDescription('2');
var protection3 = range3.protect().setDescription('3');
var protection4 = range4.protect().setDescription('4');
var protection5 = range5.protect().setDescription('5');
var protection6 = range6.protect().setDescription('6');
protection1.removeEditors(protection1.getEditors());
protection2.removeEditors(protection2.getEditors());
protection3.removeEditors(protection3.getEditors());
protection4.removeEditors(protection4.getEditors());
protection5.removeEditors(protection5.getEditors());
protection6.removeEditors(protection6.getEditors());
protection1.addEditor('rafi#gmail.com');
protection1.addEditor('naseri#gmail.com');
protection2.addEditor('sabeti#gmail.com');
protection2.addEditor('chavoshi#gmail.com');
protection2.addEditor('naseri#gmail.com');
protection3.addEditor('naseri#gmail.com');
protection3.addEditor('rezvani#gmail.com');
protection3.addEditor('nurian#gmail.com');
protection3.addEditor('fazeli#gmail.com');
protection4.addEditor('naseri#gmail.com');
protection5.addEditor('naseri#gmail.com');
protection5.addEditor('nurian#gmail.com');
protection5.addEditor('fazeli#gmail.com');
protection5.addEditor('rezvani#gmail.com');
protection6.addEditor('chavoshi#gmail.com');
protection6.addEditor('naseri#gmail.com');
if (protection1.canDomainEdit()) {
protection1.setDomainEdit(false);
}
if (protection2.canDomainEdit()) {
protection2.setDomainEdit(false);
}
if (protection3.canDomainEdit()) {
protection3.setDomainEdit(false);
}
if (protection4.canDomainEdit()) {
protection4.setDomainEdit(false);
}
if (protection5.canDomainEdit()) {
protection5.setDomainEdit(false);
}
if (protection6.canDomainEdit()) {
protection6.setDomainEdit(false);
}
}
}
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = s.getActiveRange();
if(s.getName() == "Order List" && r.getColumn() == 30 && r.getValue() == true) {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Arrived Complete");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
var s10 = SpreadsheetApp.getActive();
var protections = s10.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
onOpen (e);
}
function clearFormat() {
var s = SpreadsheetApp.getActiveSpreadsheet();
var ss = s.getActiveSheet();
var sheetname = ss.getName();
var s2c = SpreadsheetApp.getActiveSpreadsheet();
var ss2cc = s2c.getActiveSheet();
var sheetname = ss2cc.getName();
if (sheetname == "Arrived Complete") {
ss.clearConditionalFormatRules();
}
if (sheetname == "Arrived Canceled") {
ss2cc.clearConditionalFormatRules();
}
}
I am afraid that there is no direct way of doing your goal: to show certain parts of a Sheet only to some users. But there is hope to accomplish something similar, because there are two known workarounds for that:
One way is to publish a webapp that checks the username (like you did in your code) and prints only the intended column.
Another way is to share different Sheets with different users, with only the data that you want to show them. Each Sheet will be automatically synchronized with a master sheet that joins all of them together; but only the administrator will access it.
As you can see, these are two close workarounds. I hope that they work for you. Don't hesitate to write back with any question or doubt.

About Google Sheets that is shared with users

I have a Google Sheets shared with others. My sheet name is "OrderList". I am looking for a script that grabs the user's email address and inputs into another cell when a user edits the sheet. I have written the following code, however it only returns the sheet owner's Gmail address:
var COLUMNTOCHECK1 = 5;
var COLUMNTOCHECK2 = 6;
var DATETIMELOCATION1 = [0,2];
var DATETIMELOCATION2 = [0,3];
var SHEETNAME = 'OrderList'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() == COLUMNTOCHECK1) {
var email = Session.getActiveUser().getEmail();
var dateTimeCell =
selectedCell.offset(DATETIMELOCATION1[0],DATETIMELOCATION1[1]);
dateTimeCell.setValue(new Date());
var dateTimeCell1 =
selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell1.setValue(email);
}
if( selectedCell.getColumn() == COLUMNTOCHECK2) {
var dateTimeCell =
selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell.setValue(new Date());
}
}
}
Since you're running this onEdit(e) you can use Event Objects, these are great for capturing the data you're trying to achieve. You'll need to set up an onEdit trigger for your project to capture the email address though. The code below should achieve what you're expecting.
var COLUMNTOCHECK1 = 5;
var COLUMNTOCHECK2 = 6;
var DATETIMELOCATION1 = [0,2];
var DATETIMELOCATION2 = [0,3];
var SHEETNAME = 'OrderList'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() == COLUMNTOCHECK1) {
var email = e.user.getEmail();
var dateTimeCell =
selectedCell.offset(DATETIMELOCATION1[0],DATETIMELOCATION1[1]);
dateTimeCell.setValue(new Date());
var dateTimeCell1 =
selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell1.setValue(email);
}
if( selectedCell.getColumn() == COLUMNTOCHECK2) {
var dateTimeCell = selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell.setValue(new Date());
}
}
}
As you can see, I've change your var email to use an event object to capture the email address of the user that is editing the spreadsheet:
var email = e.user.getEmail();

Google Apps Script Undo function

I am setting up a spreadsheet and I need to protect cells on copy. As Google Sheets doesn't copy protection when a new sheet is copied, I came across this code. It is an undo function but it only works with text and not formula. Any ideas on how to change it?
I have changeed .getValue to .getFormula but with no luck.
var masterSheetName = "Master" // sheet where the cells are protected from updates
var helperSheetName = "Backup" // sheet where the values are copied for later checking
var ss = SpreadsheetApp.getActiveSpreadsheet();
var masterSheet = ss.getActiveSheet();
if (masterSheet.getName() != masterSheetName) return;
var masterRange = masterSheet.getActiveRange();
var helperSheet = ss.getSheetByName(helperSheetName);
var helperRange = helperSheet.getRange(masterRange.getA1Notation());
var newValue = masterRange.getValues();
var oldValue = helperRange.getValues();
Logger.log("newValue " + newValue);
Logger.log("oldValue " + oldValue);
Logger.log(typeof(oldValue));
if (oldValue == "" || isEmptyArrays(oldValue)) {
helperRange.setValues(newValue);
} else {
Logger.log(oldValue);
masterRange.setValues(oldValue);
}
}

Google Script: When cell is filled, have prompt ask if user wants to run script

I have written a script to clean up and move data between 3 different sheets. A user first paste a data extract on to the "Extract" page, and then runs the script.
When a user paste the data into cell A1 on the "Extract" page, I would like a prompt box to ask the user if they would like to run the script; if Yes, run script, if No, don't run script and show message. How would I go about doing this?
This is what I have so far...everything after the first function works.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Extract");
if(ss.s().getRange("A1").getValue() != ""){
var ui = SpreadsheetApp.getUi()
var response = ui.alert('Would you jbot to find new claims for you?', ui.ButtonSet.OK_CANCEL);
if (response == ui.Button.OK) {
jbot();
} else {
Logger.log('The user clicked "No" or the close button in the dialog\'s title bar.');
}
}
}
function jbot(){
movetobot();
deleteRows();
removeDupesInOtherSheets();
deleteCol();
cleanup();
movetoqueue();
message();
};
function movetobot() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Extract");
var ts = ss.getSheetByName("bot");
s.getRange("A1:BW").moveTo(ts.getRange("A1"));
}
function deleteRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("bot");
var r = s.getRange("BO:BO");
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if(v[0,i]=='#POSTLESSEE' || v[0,i]=='#TDI CRC_OANKURA' || v[0,i]=='#Partpaymentoffer' || v[0,i]=='#TDI_CRC_DVANKURA' || v[0,i]=='#partpaymentdupe' )
s.deleteRow(i+1);
}
function removeDupesInOtherSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("bot").getDataRange().getValues();
var s2 = ss.getSheetByName("Queue").getDataRange().getValues();
// iterate 'Queue' and check in 'bot' if duplicate values exist
var nS1 = [];
var s2Col1 = [];// data in column1 of Queue
for(var n in s2){
s2Col1.push(s2[n][0]);
}
for(var n in s1){ // iterate '180418970' and test col 1 vs col 1 in 'Queue'
var noDup1 = checkForDup(s1[n],s2Col1)
if(noDup1){nS1.push(noDup1)};// if not present in 'Queue' then keep
}
Logger.log(nS1);// view result
ss.getSheetByName("bot").getDataRange().clear();// clear and update sheets
ss.getSheetByName("bot").getRange(1,1,nS1.length,nS1[0].length).setValues(nS1);
}
function checkForDup(item,s){
Logger.log(s+' = '+item[0]+' ?')
if(s.indexOf(item[0])>-1){
return null;
}
return item;
}
function deleteCol() {
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("bot");
var lastCol = sheet.getLastColumn();
var keep = [1,2,3,4,45,53,74, 75]; // array of column numbers to keep
for (var col=lastCol; col > 0; col--) {
if (keep.indexOf(col) == -1) {
// This isn't a keeper, delete it
sheet.deleteColumn(col);
}
}
}
function cleanup() {
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("bot");
sheet.getRange("B1:B").moveTo(sheet.getRange("H1"));
sheet.deleteColumn(2)
sheet.deleteRow(1)
var cell = sheet.getRange("D:D");
cell.setNumberFormat("m/d/yy");
var cell = sheet.getRange("F:F");
cell.setNumberFormat("m/d/yy");
var cell = sheet.getRange("A:A");
cell.setHorizontalAlignment("center");
var cell = sheet.getRange("C:D");
cell.setHorizontalAlignment("center");
var cell = sheet.getRange("F:G");
cell.setHorizontalAlignment("center");
sheet.autoResizeColumn(2)
sheet.autoResizeColumn(5)
sheet.autoResizeColumn(7)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("bot");
var cell = sheet.getRange("BW1");
cell.setFormula("=COUNT(A:A)");
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("bot");
var range = sheet.getRange("A1:G");
range.sort({column: 4, ascending: true})
}
function movetoqueue() {
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("bot");
// skip row 1 (header) and beyond column G
var range = sheet.getRange(1,1,sheet.getLastRow()-1,7);
sheet = spread.getSheetByName("Queue");
var rows = sheet.getRange(1,1,sheet.getLastRow(),1).getValues();
// search for first blank row column 1
for( var i=0; i<rows.length; i++ ) {
if( rows[i][0] === "" ) {
var offset = sheet.getRange(1,1).offset(i,0);
range.copyTo(offset);
break;
}
}
}
function message() {
SpreadsheetApp.getActive().getSheetByName("Queue").activate();
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("bot");
var range = sheet.getRange("bot!BW1:BW1");
var data = range.getValue();
Browser.msgBox(data + " new claims have been added to the queue. Thank you for using jBot!");
}
Your onEdit(e) will trigger the alert regardless of which cell is edited. You need to check the event range to limit it to cell A1 of Extract like this.
function onEdit(e) {
if( e.range.getSheet().getName() === "Extract" ) {
if( e.range.getA1Notation() === "A1" ) {
if( e.value !== "" ) {
var ui = SpreadsheetApp.getUi();
// Do the rest of your stuff here
}
}
}
}