How to delay cell protection when value is entered in a cell? - javascript

I have used following OnEdit() trigger code to lock cell after entering data first time:
function LockCells(event){
var range = event.range;
var description = 'Protected'; // + stringDate;
var protection = range.protect().setDescription(description);
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
And when I enter a value in a cell as a user (not sheet admin), it instantaneously blocks the cell from re-entering value. Can we delay this process? I mean if we enter value now but the protection on that cell is applied after 10 minutes or one hour but not immediately?

I believe your goal is as follows.
Your function of LockCells is executed by the OnEdit installable trigger.
You want to run the script in the function LockCells after the OnEdit trigger is run.
In this case, how about the following modified script?
Modified script 1:
For example, when the OnEdit trigger is run, when you want to run the script in the function LockCells after about 6 minutes, the modified script can be a bit simple as follows.
function LockCells(event) {
Utilities.sleep(5 * 60 * 1000); // For example, after 5 minutes, the script is run.
var range = event.range;
var description = 'Protected'; // + stringDate;
var protection = range.protect().setDescription(description);
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
Modified script 2:
When you want to run the script in the function LockCells after more than 6 minutes, the modified script is as follows. Please copy and paste the following script to the script editor of Spreadsheet. And, please reinstall the OnEdit installable trigger to the function LockCells. By this, when you edit the cell, the edited cell is protected after 10 minutes in this sample script.
var time = 10 * 60 * 1000; // 10 minutes
function LockCells(event) {
var date = new Date().getTime();
var range = event.range;
var a1Notation = `'${range.getSheet().getSheetName()}'!${range.getA1Notation()}`;
var p = PropertiesService.getScriptProperties();
var ranges = p.getProperty("ranges");
ranges = ranges ? JSON.parse(ranges).concat({ date, a1Notation }) : [{ date, a1Notation }];
p.setProperty("ranges", JSON.stringify(ranges));
ScriptApp.newTrigger("lockCellsByTrigger").timeBased().after(time).create();
}
function lockCellsByTrigger(e) {
ScriptApp.getScriptTriggers().forEach(t => {
if (t.getUniqueId() == e.triggerUid) ScriptApp.deleteTrigger(t);
});
var limit = time;
var now = new Date().getTime();
var p = PropertiesService.getScriptProperties();
var ranges = p.getProperty("ranges");
if (!ranges) return;
ranges = JSON.parse(ranges);
var {rranges, r} = ranges.reduce((o, e) => {
o[e.date + limit < now ? "rranges" : "r"].push(e);
return o;
}, {rranges: [], r: []});
if (rranges.length == 0) return;
p.setProperty("ranges", JSON.stringify(r));
var description = 'Protected';
var me = Session.getEffectiveUser();
rranges.forEach(({a1Notation}) => {
var protection = SpreadsheetApp.getActiveSpreadsheet().getRange(a1Notation).protect().setDescription(description);
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
});
}
When you want to change the time, please modify time. In the current stage, after 10 minutes, the edited cell is protected.
The flow of this script is as follows.
When a cell is edited, LockCells is run by the installable OnEdit trigger.
Put the a1Notation of edited cell and the date to Properties Service, and install the time-driven trigger after 10 minutes.
When the time-driven trigger runs the function lockCellsByTrigger, the edited cells after 10 minutes are protected.
References:
Properties Service
newTrigger(functionName)

Related

Moving rows with an updated timestamp to another Google Sheet

I'm using two scripts to achieve my goal. The first script inserts a timestamp in the adjacent column when an update is made to a field. This works great. The next script monitors the timestamp column and when the timestamp changes, copy entire row to a "recent updates" sheet. I'm then going to use the Awesome Table plugin to create a news feed for all the recent updates.
When the timestamp column is blank and an edit is made the timestamp is appropriately entered into the timestamp column. The second script picks it up and crops it into my "recent updates" sheet...
...but if a previous update was made and the timestamp field is already present the script runs without error, but does not copy the new row to "recent updates". How can I get the row to paste every time the timestamp field changes?
/**
* #file Copy row to new cell when date value changes
* {#link https://support.google.com/docs/thread/13191603}
*/
/**
* Runs the snippet.
* Please, register this function for EDIT event
* once from the owner of the Spreadsheet
*
* #param {GoogleAppsScript.Events.SheetsOnEdit} e
*/
function CopyUpdates(e) {
if (!e) return;
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var currentRange = currentSheet.getActiveRange();
var currentRow = currentRange.getRow();
if (
e.value &&
currentSheet.getName() == "Open Actions - Cutover Punchlist" , "Open Actions - FSA Interfaces" , "Open Actions - General" &&
currentRow > 2 &&
currentRange.getColumn() == 9
) {
var dataRange = currentSheet.getRange(currentRow + ':' + currentRow);
var destinationSheet = currentSheet.getParent().getSheetByName("RecentUpdates");
var destinationRow = destinationSheet.getLastRow() + 1;
dataRange.copyTo(destinationSheet.getRange(destinationRow, 1), {
contentsOnly: true
});
}
}
The script already is working properly upon testing. Maybe there is something that interferes with your trigger which can be caused by the first function. Thus you will need to merge them.
I renamed it to onEdit(e) instead. I merged them since they are actually a subset of onEdit(e), just having different conditions. It should be fine to merge them under the same function.
Code:
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
function onEdit(e) {
if (!e) return;
var currentRange = currentSheet.getActiveRange();
var currentRow = currentRange.getRow();
if ( e.value &&
currentSheet.getName() == "Open Actions - Cutover Punchlist" , "Open Actions - FSA Interfaces" , "Open Actions - General" &&
currentRow > 2 ) {
if (currentRange.getColumn() == 8) { // adjacent column (first function conversion, if H column is edited)
var adjacentCell = currentSheet.getRange('H' + currentRow);
var timestampCell = adjacentCell.offset(0, 1);
timestampCell.setValue(new Date());
// since H is edited, timestamp column is updated
// so we copy (regardless if the old value is blank or a timestamp)
copyUpdates(currentRow);
}
if (currentRange.getColumn() == 9) { // timestamp column (second function conversion, if I column is edited)
// edited timestamp manually, copy
copyUpdates(currentRow);
}
}
}
function copyUpdates(currentRow) {
var dataRange = currentSheet.getRange(currentRow + ':' + currentRow);
var destinationSheet = currentSheet.getParent().getSheetByName("RecentUpdates");
var destinationRow = destinationSheet.getLastRow() + 1;
dataRange.copyTo(destinationSheet.getRange(destinationRow, 1), {
contentsOnly: true
});
}
Sample Data:
Sample Testing:
1. Wrote "add timestamp" to "H3" (Should trigger your first function)
2. Wrote "add timestamp" to "H4" (Should trigger your first function)
3. Edited "H4" to "change timestamp" (Should trigger your first function)
4. Edited "I4" to "1/22/2021" (Should trigger your second function)
Sample Data outcome:
RecentUpdates outcome:

Edit script to apply across all sheets

This is the script that I am using currently. I want it to apply to all sheets in my document automatically. Please help. The following is the code.
/**
* Creates a Date Stamp if a column is edited.
*/
//CORE VARIABLES
// The column you want to check if something is entered.
var COLUMNTOCHECK = 1;
// Where you want the date time stamp offset from the input location. [row, column]
var DATETIMELOCATION = [0,1];
// Sheet you are working on
var SHEETNAME = '46'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//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 date to appear.
if( selectedCell.getColumn() == COLUMNTOCHECK) {
var dateTimeCell = selectedCell.offset(DATETIMELOCATION[0],DATETIMELOCATION[1]);
dateTimeCell.setValue(new Date());
}
}
}
I want the script to execute on every sheet so that whenever I add a new sheet the script runs automatically and helps me add a time stamp to column two when I add a value to column 1.
Please help.
I am a complete noob here and as such some detailed explanation will help me immensely.
Basically, just remove the if statement and the code will apply to EVERY sheet.
In the answer given below, I've also used some of the objects returned by the onEdit trigger.
/**
* Creates a Date Stamp if a column is edited.
*/
//CORE VARIABLE
// The column you want to check if something is entered.
var COLUMNTOCHECK = 1;
function onEdit(e) {
var sh = e.range.getSheet();
if (e.range.columnStart == COLUMNTOCHECK){
// edited the right column do something
var targetcell = sh.getRange(e.range.rowStart,2);
targetcell.setValue(new Date());
}
else {
// do nothing
}
}

Google Sheets: Trying to GetValue -> SetValue with Bitcoin Prices and print it anywhere in the sheets. 2 Codes

My goal:
I'm trying to print (or stamp) the value of bitcoin each time there is word called "printprice" anywhere in the sheet.
My problem:
I have been very unsuccessful getting this as I have been trying to get the solution from two different codes
This is what I'm trying to achieve:
function onEdit() {
// simple timestamp -- when a single "T" is entered in a cell, replace it with a timestamp
// see https://productforums.google.com/d/topic/docs/rC6MpQDC7n4/discussion
var cell = SpreadsheetApp.getActiveRange();
if (cell.getValue() == "Timestamp") {
cell.setValue(new Date());
}
}
^ this works and prints new Date each time there's word "timestamp" anywhere in the sheet.
What I have been trying to do is to combine the data above with the down below without any success.
/* USAGE:
* Sheet -> Tools -> Script Editor...
* Paste this script
* Update the map (below) to your preferences
* Create a button in your Sheet and Assign Script: `test`
* et voila profit
*/
function test() {
// maps currencies.tokens to sheet ranges
getPrices({
'USD': {
'ETH': 'G6',
'DASH': 'H6',
'LTC': 'I6',
'GNT': 'J6',
'REP': 'K6',
'BAT': 'L6'
}
});
}
function getPrices(model) {
for (var currency in model) {
var tokens = Object.keys(model[currency]).toString();
var url = 'https://min-api.cryptocompare.com/data/price?fsym=' + currency + '&tsyms=' + tokens;
var response = UrlFetchApp.fetch(url, {'muteHttpExceptions': true});
var json = JSON.parse(response.getContentText());
for (var token in model[currency]) {
updatePrice(
model[currency][token],
json[token]
);
}
}
}
function updatePrice(range, price) {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(range).setValue(1/price);
}
Hopefully you can provide help as this is a learning process for me as well!
Kind Regards Johan

Timing Tool for work

I have built a tool for timing indirect workers, it consists of a start and stop button which both place a time stamp into the google sheet and then calculates the difference to record a time. It works great however when I share it with some people it does not allow them to use it saying that they do no have access to run the script. If they open script editor they can manually run it however that will no fly because I will be sending this out to approximately 50 people.
Here is the code and start and stop are two different scripts. Please let me know if I am missing something and I appreciate the help. Thanks
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var start = new Date();
function StartScript() {
var last = ss.getLastRow();
ss.getRange(last+1,1).setValue(last+1)
var source = ss.getRange(last+1,1).getValue();
source = Number(source);
if (source <= 16) {
ss.getRange(last+1,2).setValue(start);
}
else {
ss.getRange(last+1,2).setValue("Stop Timing");
}
}
function stop() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var date = new Date();
var last1 = ss.getLastRow();
ss.getRange(last1, 3).setValue(date);
var lastrow = ss.getLastRow()
ss.getRange("D" + (lastrow)).setFormula("=C" + (lastrow) + "-B" + (lastrow));
}

Adding name to last edited (google script)

Currently i have the following google script:
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
if(s.getName()=='Setup BRF'){
if( r.getColumn() != 2 ) { //checks the column
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT", "dd-MM-yy' , 'HH:mm:ss");
SpreadsheetApp.getActiveSheet().getRange('J1').setValue(time);
};
};
};
Everytime i edit something on the sheet called Setup BRF it updates the time and date in J1 to show when it was last updated/edited.
My question is if its possible to add a name to lets cell K1 which shows who last updated this sheet. My skill with javascript is nihil so any kind of help is appreciated.
You could use the event object passed to the function
So change the definition to
function onEdit(e) {
and then you can use e.user to get to the user
Something like this
if( r.getColumn() != 2 ) { //checks the column
var row = r.getRow(),
time = new Date(),
user = e.user;
time = Utilities.formatDate(time, "GMT", "dd-MM-yy' , 'HH:mm:ss");
SpreadsheetApp.getActiveSheet().getRange('J1').setValue(time);
SpreadsheetApp.getActiveSheet().getRange('K1').setValue(user);
};

Categories