I am trying to protect a range through google apps script in my google sheet. Whenever the data point in column O is checked i.e. value is yes. I would like that row be protected.
Secondly, I have prior protected columns E:H and M:N, which should stay protected.
There are around 1000 rows, 10-15 new rows that need to be protected daily. By the code I have currently written, it removes the protection and then re-adds it, which takes a lot of time. If I remove the part where it removes the rights then it still re-adds the same protection regardless.
Is there anyway to check if the cell is protected, if protected move onto the next row?
Secondly, I'm unable to provide access to the other emails i.e. "add editors"
function removecompleted(){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var purchased = ss.getSheetByName("Purchased Inventory")
last_row_purchased = purchased.getLastRow()
var emails = [
'user#domain.com'
];
var protections = purchased.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
for(var i = 1; i < last_row_purchased; i++) {
if(purchased.getRange(i, 15).getValue() == "Yes")
{
target_range = purchased.getRange(i, 1, 1, 15)
protection = target_range.protect().setDescription('Sample protected range');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors())
protection.addEditors(emails);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
}
var range = purchased.getRange('E:H');
var protection = range.protect().setDescription('Always Protected');
var range = purchased.getRange('M:N');
var protection = range.protect().setDescription('Always Protected');
}
You can do this on edit, but rather than checking to see if the cell is protected (which is not that much faster than just setting the protection anyway) you can just check the row of each protected range and remove the protection if its row is equal to the row of the cell that was just unticked:
function onEdit(e) {
// List of sheet names the function should run on
const sheets = ["Sheet1", "Sheet2", "Sheet3", "etc"]
const sheet = e.source.getActiveSheet()
// if current sheet is not in permitted sheets, return
if (!~sheets.indexOf(sheet.getName())) {
return
}
const row = e.range.getRow()
if (e.range.getColumn() === 15) {
const ss = e.source
if (e.value === "TRUE") {
range = sheet.getRange(`${row}:${row}`)
const me = Session.getEffectiveUser().getEmail()
const protection = range.protect()
protection.getEditors().forEach(user => {
protection.removeEditor(user.getEmail())
})
protection.addEditor(me)
}
else {
const protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE)
protections.forEach(function(p) {
if (p.getRange().getRow() === row) {
if (row === 1 && p.getRange().getColumn() !== 1) return
p.remove()
}
})
}
}
}
Code Rundown:
Get the row number of the edited range
Check if the edited cell was column 15
If the value of the cell is "TRUE", protect the row, and remove all editors aside yourself.
If the value of the cell is "FALSE", then get all the sheet's protections, check the value of their row, and if the row matches the edited cell's row, then remove that protection
NB : The final check also has a condition to check if the column is not equal to 1 so to not remove the protections on E:H or M:N.
References:
Class Protection
Class: Sheet
Class: Range
Related
Let's say certain cells will be protected if it matches certain condition
function A() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var protection;
for(var i = 3;i<ss.getLastRow();i=i+1){
if(ss.getRange(i,4).getValue()==7){ // check if the condition is matched
for(var j=5; j<=20; j=j+2){
if(ss.getRange(i,j).isBlank()){
protection = ss.getRange(i,j).protect(); // protect that certain cell
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit())
protection.setDomainEdit(false);
}
}
}
}
Now I want to delete the protection I made on that cell if the condition changed, let's say the upcoming code will be:
if(ss.getRange(i,4).getValue()<=6){
...
How can I only delete the protection applied to that cell instead of all the protection in the entire sheet?
Thank you
I tried
getProtections().remove()
but it does not target certain cells right?
I believe your goal is as follows.
You want to unprotect the cells when the value of column "D" is less than 6.
About Now I want to delete the protection I made on that cell if the condition changed, when I saw your showing script for protecting the cells, it seems that when the if-statement of if(ss.getRange(i,4).getValue()==7){} is true, the empty cells of the row are protected. From this situation, in your goal, when the if statement of if(ss.getRange(i,4).getValue()<=6){} is true, you want to remove all protected cells of the row.
If my understanding is correct, how about the following sample script? In this case, the protected ranges can be retrieved with getProtections(SpreadsheetApp.ProtectionType.RANGE).
Sample script 1:
In this sample, when this script is run, the protected cells are unprotected by searching the values of column "D".
function myFunction() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var protects = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE).reduce((o, p) => {
var row = p.getRange().getRow();
o[row] = o[row] ? [...o[row], p] : [p];
return o;
}, {});
var values = ss.getRange(3, 4, ss.getLastRow() - 2, 1).getValues();
values.forEach(([d], i) => {
if (d <= 6) {
var obj = protects[i + 3];
if (obj) {
obj.forEach(p => p.remove());
}
}
});
}
Sample script 2:
In this sample, when the cells of column "D" is edited, the script is run by the installable OnEdit trigger. So, please install the OnEdit trigger to the function installedOnEdit. When you use this script, please edit the cells of column "D". By this, the script is run. When this script is directly run by the script editor, an error occurs. Please be careful about this.
function installedOnEdit(e) {
var sheetName = "Sheet1"; // Please set your sheet name.
var { range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 4 || range.rowStart < 3 || range.getValue() > 6) return;
var protects = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE).reduce((o, p) => {
var row = p.getRange().getRow();
o[row] = o[row] ? [...o[row], p] : [p];
return o;
}, {});
var obj = protects[range.rowStart];
if (obj) {
obj.forEach(p => p.remove());
}
}
Note:
When I saw your script A(), I thought that when the script is modified, the process cost might be able to be reduced a little. So, how about the following modification?
function A() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var values = ss.getRange(3, 1, ss.getLastRow() - 2, 20).getValues();
values.forEach((r, i) => {
if (r[3] == 7) {
for (var j = 4; j < 20; j = j + 2) {
if (r[j].toString() == "") {
protection = ss.getRange(i + 3, j + 1).protect();
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit())
protection.setDomainEdit(false);
}
}
}
});
}
References:
getProtections(type)
reduce()
forEach()
Installable Triggers
I have a Google Sheet named Dashboard which is sheet protected manually except A4:C4 cells.
Now what I want is, when "Start 1-Period or Start 2-Period" in cell C6 is selected, the Dashboard will be fully protected that means, it will include protection in cells A4:C4.
Then after 5 minutes, the dashboard protection will go back to its previous stage, that is dashboard is protected except A4:C4 cells.
Please note that there are five editors in the Dashboard Sheet which will remain unchanged. For details of the scenario, please check the attached image.
function onEdit(e){
if (e.range.getA1Notation() === 'C6' && e.range.getValue() === "Start 1-Period") {
refreshSheet();
onePeriod();
}
if (e.range.getA1Notation() === 'C6' && e.range.getValue() === "Start 2-Period") {
refreshSheet();
twoPeriod();
}
}
function refreshSheet() {
//For protecting dashboard while scripts running
var spreadsheet = SpreadsheetApp.getActive();
var dashboard = spreadsheet.getSheetByName("Dashboard");
var protectionms = dashboard.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protectionms.length; i++) {
var protectionm = protectionms[i];
if (protectionm.canEdit()) {
protectionm.remove();
}
}
var protectionm = dashboard.protect();
var range = dashboard.getRange('A4:C4');
Utilities.sleep(300000);
protectionm.setUnprotectedRanges([range]);
}
Every time you edit cell C6 you apply protection rules to the range A4:C4 according to the selected value. In more detail, every time you edit cell C6 the script will initially remove all current protections. If the selected value of cell C6 is changed to Please Select then no protection is applied. Otherwise, if the new value of C6 is either Start 1-Period or Start 2-Period you apply protection to the range: A4:C4.
Here is the complete solution:
function onEdit(e) {
var sh_name = "Sheet1";
var sh = e.source.getSheetByName(sh_name)
var row = e.range.getRow();
var col = e.range.getColumn();
if ( e.source.getActiveSheet().getName() == sh_name && row==6 && col==3 ){
var protections = sh.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
var protection = sh.protect();
var selection = sh.getRange('C6').getValue();
var range = sh.getRange('A4:C4');
if ( selection == 'Please Select' ) {
protection.setUnprotectedRanges([range]);
}
}
}
You just need to copy this code to the script editor, save the changes and then the script will take care of this process.
Note that my script applies to the sheet with name Sheet1. Change that part of the code if you work with a different sheet.
References:
Class Protection
onEdit Trigger
Making a self-scheduling spreadsheet for my co-workers. The goal is for them to put their hours (12 or 6) in the cell that corresponds with the date and their name. I am trying to have it so that a date column will lockout after the sum cell for that column reaches a certain total.
I am using this (Google Script Lock Cells) as my basis but I am not having much luck.
function myFunction() {
function onOpen() {
var ss = SpreadsheetApp.getActive();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cell = source.getRange("B12").getValue();
var range = ss.getRange("B2:B11");
if (cell == 10) {
// Protect range B2:B11 if cell 'B12' = 10
var protection = range.protect().setDescription('Sample protected range');
Logger.log
} else {
// Remove protection if cell 'B12' is anything other than 10
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
protection.remove();
}
}
}
}
I am also looking to have the sum represent shits rather than hours so if a person wrote "12" the sum column would record "1"
Any help would be amazing.
You should change several things
Do not nest onOpen inside myFunction - it will not work
If you want the protections to be update on every update of B12 - you should use the onEdit trigger instead of onOpen
It is better to replace cell == 10 through cell >= 10 to account for the possibility that the value accidentally trespasses the maximum
4.It is not enough to create a protection, to make it useful you need to remove editors from it
Only the owner of the script (you) should maintain access
For this, use an installable trigger instead of a simple one, to make sure that it runs always as you and not the user that opens the document
Sample (to be bound to an installable onEdit trigger):
function bindAnOnEditTiggerToMe() {
var ss = SpreadsheetApp.getActive();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cell = source.getRange("B12").getValue();
var range = ss.getRange("B2:B11");
if (cell >= 10) {
// Protect range B2:B11 if cell 'B12' = 10
var protection = range.protect().setDescription('Sample protected range');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
} else {
// Remove protection if cell 'B12' is anything other than 10
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
protection.remove();
}
}
}
I am trying to implement a script that locks a certain range of cells when a condition is true. Here is the link to my document:
https://docs.google.com/spreadsheets/d/1XShGxlz2fA2w2omth-TvYc7cK0nXvVMrhwRKafzVjOA/edit?usp=sharing
Basically I am sharing this document with a group of people so they fill their mail addresses in column B and put a number 1 in column C so it increments my counters for each slot. What I am trying to do is to lock each slot when it is full so other people can no more edit these slots but the problem is with my if statement if (cell1 == 10). The range is always locked even if the if condition is not realized. Here is my code :
function onOpen() {
var ss = SpreadsheetApp.getActive();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cell=60;
var cell1 = source.getRange("G2").getValue();
if (cell1 == 10){
// Protect range B2:B11, then remove all other users from the list of editors.
var ss = SpreadsheetApp.getActive();
var range = ss.getRange('B2:B11');
var protection = range.protect().setDescription('Sample protected range');
// Ensure the current user is an editor before removing others. Otherwise, if the user's edit
// permission comes from a group, the script will throw an exception upon removing the group.
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
}
As Andy says in the comments, you need to explicitly remove the protection if cell G2 does not equal 10. (This code removes all protections).
Reading the Protection Class page, where you got the snippets from, I couldn't get a handle on the way editor privileges would factor into your needs, so this script will work if others are editors. If you don't want them to be editors, you'll have to add the relevant code.
function onOpen() {
var ss = SpreadsheetApp.getActive();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cell = source.getRange("G2").getValue();
var range = ss.getRange('B2:B11');
if (cell == 10) {
// Protect range B2:B11 if cell 'G2' = 10
var protection = range.protect().setDescription('Sample protected range');
Logger.log
} else {
// Remove protection if cell 'G2' is anything other than 10
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
protection.remove();
}
}
}
I am trying to implement a script that locks a certain range of cells when a condition is true. Here is the link to my document:
https://docs.google.com/spreadsheets/d/1XShGxlz2fA2w2omth-TvYc7cK0nXvVMrhwRKafzVjOA/edit?usp=sharing
Basically I am sharing this document with a group of people so they fill their mail addresses in column B and put a number 1 in column C so it increments my counters for each slot. What I am trying to do is to lock each slot when it is full so other people can no more edit these slots but the problem is with my if statement if (cell1 == 10). The range is always locked even if the if condition is not realized. Here is my code :
function onOpen() {
var ss = SpreadsheetApp.getActive();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cell=60;
var cell1 = source.getRange("G2").getValue();
if (cell1 == 10){
// Protect range B2:B11, then remove all other users from the list of editors.
var ss = SpreadsheetApp.getActive();
var range = ss.getRange('B2:B11');
var protection = range.protect().setDescription('Sample protected range');
// Ensure the current user is an editor before removing others. Otherwise, if the user's edit
// permission comes from a group, the script will throw an exception upon removing the group.
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
}
As Andy says in the comments, you need to explicitly remove the protection if cell G2 does not equal 10. (This code removes all protections).
Reading the Protection Class page, where you got the snippets from, I couldn't get a handle on the way editor privileges would factor into your needs, so this script will work if others are editors. If you don't want them to be editors, you'll have to add the relevant code.
function onOpen() {
var ss = SpreadsheetApp.getActive();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cell = source.getRange("G2").getValue();
var range = ss.getRange('B2:B11');
if (cell == 10) {
// Protect range B2:B11 if cell 'G2' = 10
var protection = range.protect().setDescription('Sample protected range');
Logger.log
} else {
// Remove protection if cell 'G2' is anything other than 10
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
protection.remove();
}
}
}