I have 2 lists with ranges C2:F21, which should be available to fill for all users in a certain period.
Range C2:F21 in the list A shouldn't be locked from 01.01.2020 to 05.01.2020, but in another time it should be locked for all users except me.
In the list B range C2:F21 shouldn't be locked only from 01.02.2020 to 05.02.2020.
I will be very grateful for any answer
The most "secure" way to handle this would be to actually save the data as a separate spreadsheet (only you have permission to edit) on the expiration date(s) which you could also automate with a script. You could then use a series of importrange function(s) to create a list of read only information if you need the users to see it in the master sheet. Anyone who is an editor of the spreadsheet has the ability (perhaps not the knowledge) to get around your cell protection in various creative ways.
If you still insist on using cell protection to accomplish this it is very simple, use the Protection class and add a trigger to the project using the little clock in the script editor, Select event source Time-driven and time based trigger Specific date and time.
First you would need to protect the ranges, something that you can do easily through the web UI by selecting the range you want to protect, right-clicking and selecting Protect range. You can do it too via Apps Script, by running a function like this one:
function protectRangeA() {
var ss = SpreadsheetApp.openById("your-spreadsheet-id"); // Change accordingly
var sheetA = ss.getSheetByName("your-sheet-name"); // Change accordingly
var protection = sheetA.getRange("C2:F21").protect();
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
This function gets a sheet with a certain name from a spreadsheet with a certain id (as others said before me, for security reasons - to avoid people messing with your script - it would be better if it was a standalone script, not bound to your spreadsheet), and protects the range C2:F21, using protect(), and you will be the only editor.
Second, you want to unprotect this range at a certain date, an protect it again at another date. To do that, you could create time-driven triggers. You could, for example, use an atDate trigger, that will run the function you specify at the year, month and day you specify. So, if you want to unprotect a range at 01.01.2020, you could, for example, have this function:
function createUnprotectTriggerA() {
ScriptApp.newTrigger("unprotectRangeA")
.timeBased()
.atDate(2020, 1, 1)
.create();
}
This will fire a function called unprotectRangeA near midnight of 01.01.2020 (+/- 15 minutes). The function that will be run at this time, then, should unprotect the desired range. It could be something on the following lines:
function unprotectRangeA() {
var ss = SpreadsheetApp.openById("your-spreadsheet-id"); // Change accordingly
var sheetA = ss.getSheetByName("your-sheet-name"); // Change accordingly
var protections = sheetA.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
This function uses getProtections to get all range protections that exist in the sheet and, before checking if the current user can edit them via canEdit(), it removes these with the use of remove().
And to protect it again at 05.01.2020, have this trigger, which will fire the function protectRangeA (previously defined) at this date:
function createProtectTriggerA() {
ScriptApp.newTrigger("protectRangeA")
.timeBased()
.atDate(2020, 1, 5)
.create();
}
Finally, because you want to protect/unprotect several ranges and you would need several triggers, you could call all the functions that create triggers in the same function, which you would have to run once to set all the triggers you need (I didn't define createProtectTriggerB and createUnprotectTriggerB but it would be the same as with the other range):
function createTriggers() {
createProtectTriggerA();
createUnprotectTriggerA();
createProtectTriggerB();
createUnprotectTriggerB();
}
Please beware that, as CodeCamper said, if users have permission to edit the spreadsheet, they could potentially use a script to mess up with your protections. So, depending on the people who are editing this, maybe you should have a copy of the spreadsheet that only you can access.
I hope this is of any help.
Related
I currently am able to successfully write the code for locking a single cell after editing (and only owner can edit after) correctly. But I want that function to execute after a delay of around 10-15 minutes (in case the editor changes their mind). So far this is what I have... the lockrow function works fine but I am unable to get the time trigger to work.
This is what I currently have:
function setUpTrigger() {
ScriptApp.newTrigger('lockRow')
.timeBased()
.after(600000)
.create() ;
}
function lockRow(e){
const sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
let protection = e.range.protect();
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit())
protection.setDomainEdit(false);
}
At Work, one thing I am tasked with is managing our on-call sign up sheet and need to lock the cells after someone signs up for a shift. This would make my job a lot less tedious.
I by no means would consider myself a "coder". I've gotten this far through trial and error but I have been stuck on this for 2 almost 3 weeks.
Any help would be much appreciated.
EDIT:
I asked a YouTuber for help and this is what he explained to me:
The 'e' value won't transfer through properly like this. You should
adjust your script so you declare and instanciate the needed values
(in this case just the range) in the setUpTrigger() function then pass
that value to the active function. So:
const r = e.range;
lockRow(r){
...
let protection = r.protect();
...
}
I'm honestly not even sure what he means.
Your lockRow function doesn't work when it's called by a time driven trigger because the time driven event object, in this case assigned to e, hasn't a range property, so it's very likely that you are getting a error on the following line:
let protection = e.range.protect();
You need to rethink your solution to define a way to set the range to be protected. One way to do this is by using the Properties Service, i.e. you could store a property having a list of all the ranges references (like A1) to be protected.
Please bear in mind that the property service can only store string values.
Example:
Here are a simple functions to set and get the range reference to be protected.
/**
* Run this function before creating the trigger, i.e. it might be called by the setUpTrigger function
*/
function setRangeToProtect(){
const properties = PropertiesService.getScriptProperties();
properties.setProperty('range','A1');
}
function getRangeToProtect(){
const properties = PropertiesService.getScriptProperties();
return properties.getProperty('range');
}
Then in the lockRow function replace
let protection = e.range.protect();
by
const range = sh.getRange(getRangeToProtect());
let protection = range.protect();
Depending on how your spreadsheet will be used, you will have to adapt the setRangeToProtect and getRangeToProtect and the way that they will be used / called.
[how to lock cell autometicly base on cell data & and time everyday?]https://docs.google.com/spreadsheets/d/1PQTpr9tes5_7c963nV_c0BSOdemYD6Vpyd1SB1elANE/edit#gid=1118459963
A possible solution for this is to use a time based trigger in Apps Script which will lock the cells you want.
You should start by writing a script similar to this:
function lockCells() {
let sheet = SpreadsheetApp.openById("SS_ID").getSheetByName("Attendance1");
let protection = sheet.getRange(3, 5, 15, 30).protect(); // CORRESPONDS TO E3:AH17
let editors = protection.getEditors();
protection.removeEditors(editors)
}
function timeTrigger() {
ScriptApp.newTrigger('lockCells')
.timeBased()
.atHour(11)
.create();
}
The code above consists in the two functions:
lockCells - which is used to protect the range by retrieving the editors and then removing them;
timeTrigger - which will trigger the lockCells function every day at 11:00.
Reference
Apps Script Range Class - protect();
Apps Script Protection Class - getEditors();
Apps Script Protection Class - removeEditors();
Installable Triggers.
I was wondering if there was a way to create a function for the following. I am trying to update a cell, say G3 with a value in D3 but I want to do it on a certain date. I want for that cell to update on that date and then lock in with the value.
For example, if D3 has a value of 30, I want for my G3 cell to update with 30 on 10/12/2012. After that date I want that value to lock in as the data in D3 will change over time. This formula would need to be copied as I am trying to find a trend detail on a weekly basis.
Any help would be appreciated, thanks in advance!
If you don't mind doing this in Google Apps Script, you can achieve this easily via a time-driven trigger. Specifically, with atDate(year, month, day) you can make a function run at a certain date. To do this, you would first have to create the trigger. You can do this manually from the script editor, as explained here. You could also do this programmatically, by running a function that creates such trigger. The function would be, in this case:
function createTrigger() {
ScriptApp.newTrigger("setAndLock")
.timeBased()
.atDate(2012, 12, 10)
.create();
}
Running this function will create a trigger that will fire a function called setAndLock at date 2012/12/10, which should copy the value from D3 to G3 and protect this last cell. Such a function could be similar to this one:
function setAndLock() {
var sheet = SpreadsheetApp.getActiveSheet();
// Copying the value from D3 to G3:
var origin = sheet.getRange("D3").getValue();
var dest = sheet.getRange("G3");
dest.setValue(origin);
// Protecting G3:
var protection = dest.protect();
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
Please take into account that the cell cannot be locked from the current user nor the owner of the spreadsheet, as mentioned here.
I hope this is of any help.
For the scheduling of tennis matches, we have created a sheet in which 170 duo-participants can fill in their names. This sheet is accessible to everyone (so you do not even have to have a Google account)
However, we are looking for the appropriate script to protect the cells that are filled in against possible changes.
Currently a protect is set up when a cell is edited.
But unfortunately, the protect does not work if it is filled in by a non-logged-in person. The protection is made, but not applied.
Who can help us customize the script, so that once a day (eg midnight) all filled cells are protected by anyone (by trigger)
I have set up a copy that is accessible to those who can help.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var Sheet = ss.getSheetByName("PLANNING");
var Range = Sheet.getActiveRange();
var Row = Range.getRowIndex();
var Reeks = Sheet.getRange("D"+ Row);
if (Reeks.getValue() != "") {
if (Reeks.getValue() != "FOUT") {
Range.setNote('geboekt op: ' + new Date());
var LockRange = Sheet.getRange(Row, 6, 1, 2);
var protection = LockRange.protect().setDescription('Row ' + Row + ' Protected');
protection.removeEditors(protection.getEditors());
}
}
}
Instead of a simple trigger use an installable trigger.
Change the name of your function name onEdit to something else like protectOnEdit
Add the installable trigger to run protectOnEdit (or whatever you named your function)
The above because simple trigger function can't execute methods that require authorization like removeEditors
I'm trying to find the reason why the script does not work. Setting up a trigger does not seem to work anyway.
I think it has to do with the other protections that arise. The sheet was originally protected with the EXCEPTION of a specific range. If the script wants to secure a row in that specific range, both statements go against each other.
I will try to figure it out further in the coming days
So I'm using a script on my googledoc spreadsheet that goes through a column and counts a certain number of occurrences of specified formatting. (i.e. in my spreadsheet "=myFunction()")
The function works fine, but my problem is despite setting up a trigger to run the script "onEdit", it never does it. I have to open the script up and save it each time I want it to update on my spreadsheet.
I've been looking up for hours but no one seems to have my question. There are no errors sent by the notifications. The code (though I don't think it's terribly relevant) for my function is:
function CountIfNotStrikeThrough2()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mysheet = ss.getActiveSheet();
var mydatarange = mysheet.getRange(1,1,390,1);
var numRows = mydatarange.getLastRow();
var rowindex = mydatarange.getRowIndex();
var columnindex = mydatarange.getColumnIndex();
var total =0;
for(i=rowindex;i<=numRows;i++)
{
if(mydatarange.offset(i-1, columnindex-1, 1, 1).isBlank() != true && mydatarange.offset(i-1, columnindex-1, 1, 1).getFontLine() != "line-through")
{
total++;
}
}
return total;
}
I know there's a thread here that has a comprehensive list of when the onEdit trigger doesn't activate. (here) For example, if you setup Data Validation and select an item from the list of values in the validation, it doesn't trigger the onEdit function.
Besides that, could you try a simple trigger instead of installable? This is to say, instead of explicitly associating CountIfNotStrikeThrough2() to the onEdit trigger, please try re-naming the function to "onEdit()" instead and see if that works. This creates an implicit or simple trigger. I've had trouble in the past with installable vs. simple triggers.
Also, please share the exact action you're performing to test the trigger.
Reference: https://developers.google.com/apps-script/understanding_triggers