Google App script Remove Editors From Protection - javascript

In Google Sheets I am trying to remove editors using script from all the protection except the sheet owner and for this I am using below code, but after running the code the entire protection is being removed, however in place of removing the protection I want to remove all the user from the protection except the sheet owner. Further, when the code is run only one protection is removed at a time I want to apply it for all the protection.
function remove(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var protection = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE)[0];
if (protection && protection.canEdit()) {
protection.remove();
}}
Any help on above will be appreciated.

The changes that you should do to make the code only allow the spreadsheet owner be able to edit a protected range were already included on the Tainake's answer to your previous question Google Sheet Remove Editors After First Edit
From the question:
Further, when the code is run only one protection is removed at a time I want to apply it for all the protection.
sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE) returns an array of objects. To remove all the the protections your code should have to iterate over all the elements of this array. One of many ways to do this is by using Array.prototype.forEach, i.e.
function remove() {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE)
.forEach(protection => {
if (protection && protection.canEdit()) {
protection.remove();
}
});
}
Resources
https://developers.google.com/apps-script/guides/sheets
https://developers.google.com/apps-script/reference/spreadsheet/protection

Related

Monitor google sheet change event externally [duplicate]

What I wish to achieve:
Whenever a cell is changed in any google sheet on my shared drive (by
any user on the domain) I want to call an API endpoint and include
information about which cell was edited.
My approach:
I believe Google App Scripts Add-on is what I need. Installed for all users on the domain.
I see there are "bound" scripts and standalone scripts. For standalone scripts I am not able to create any other triggers than timer and calender based triggers. Bound scripts seem to be permanently bound to a single sheet and won't impact other sheets in any way.
What am I missing?
I find a few end-to-end tutorials on blogs for making bound scripts, but nothing for generic cross-domain stuff.
You can achieve all this through a standalone script. Create a standalone script and follow these steps:
Step 1: Get spreadsheet ids
First you would have to get the id of the different Spreadsheets in your shared drive. You can do it in Google Apps Script itself if you use the Advanced Drive Service (see Reference below). To activate this service, go to Resources > Advanced Google services... in your script editor and enable Drive API.
Then, write a function that will return an array of the spreadsheet ids in the shared drive. You will have to call Drive.Files.list for that. It could be something along the following lines (please write your shared driveId in the corresponding line):
function getFileIds() {
var params = {
corpora: "drive",
driveId: "your-shared-drive-id", // Please change this accordingly
includeItemsFromAllDrives: true,
q: "mimeType = 'application/vnd.google-apps.spreadsheet'",
supportsAllDrives: true
}
var files = Drive.Files.list(params)["items"];
var ids = files.map(function(file) {
return file["id"];
})
return ids;
}
Step 2: Create triggers for each spreadsheet
Install an onEdit trigger programmatically for each of the spreadsheets (an edit trigger fires a function every time the corresponding spreadsheet is edited, so I assume this is the trigger you want). For this, the ids retrieved in step 1 will be used. It could be something similar to this:
function createTriggers(ids) {
ids.forEach(function(id) {
var ss = SpreadsheetApp.openById(id);
createTrigger(ss);
})
}
function createTrigger(ss) {
ScriptApp.newTrigger('sendDataOnEdit')
.forSpreadsheet(ss)
.onEdit()
.create();
}
The function createTriggers gets an array of ids as a parameter and, for each id, creates an onEdit trigger: everytime any of these spreadsheets is edited, the function sendDataOnEdit will run, and that's where you will want to call your API endpoint with information about the edited cell.
Step 3: Call API endpoint
The function sendDataOnEdit has to get data from the edited cell and send it somewhere.
function sendDataOnEdit(e) {
// Please fill this up accordingly
var range = e.range;
var value = range.getValue();
UrlFetchApp.fetch(url, params) // Please fill this up accordingly
}
First, it can get information about the cell that was edited via the event object, passed to the function as the parameter e (you can get its column, its row, its value, the sheet and the spreadsheet where it is located, etc.). For example, to retrieve the value of the cell you can do e.range.getValue(). Check the link I provide in reference to get more details on this.
Second, when you have correctly retrieved the data you want to send, you can use UrlFetchApp.fetch(url, params) to make a request to your URL. In the link I provide below, you can see the parameters you can specify here (e.g., HTTP method, payload, etc.).
Please bear in mind that you might need to grant some authorization to access the API endpoint, if this is not public. Check the OAuth reference I attach below.
(You have to edit this function accordingly to retrieve and send exactly what you want. What I wrote is an example).
Summing this up:
In order to create the triggers you should run createTriggers once (if you run it more times, it will start creating duplicates). Run for example, this function, that first gets the file ids via Drive API and then creates the corresponding triggers:
function main() {
var ids = getFileIds();
createTriggers(ids);
}
Also, it would be useful to have a function that will delete all the triggers. Run this in case you want to start from fresh and make sure you don't have duplicates:
function deleteTriggers() {
var triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
ScriptApp.deleteTrigger(trigger);
})
}
Reference:
Advanced Drive Service
Drive.Files.list
onEdit trigger
Install trigger programmatically
onEdit event object
UrlFetchApp.fetch(url, params)
Connecting to external APIs
OAuth2 for Apps Script
ScriptApp.deleteTrigger(trigger)
I hope this is of any help.

How to use Google Script to send an email with information collected from Google Form?

For reference: Here is my Google Form.
Here is a Google PE Blank Sheet which has been tied to the output of my Google Form (answers will output on tab Form Responses 2).
I've built up a Google Form capable of collecting a good bit of data. It is meant to be filled out by a manager requesting SAP permissions for a new hire.
They proceed through the Form and enter their primary department (SAP Stream) which leads them to the next page/section that allows the manager to check off what the employee's required permissions will be.
Sometimes, an employee will interface with multiple departments, so the option for a supporting/secondary SAP Stream exists on the second page. This can be continued as needed until all of an employee's requisite roles/permissions are collected in a Google Sheet.
In the Sheet, you will see that:
Columns A-F include basic information
Columns H-T are for the Job Responsibilities
Columns U-AG are a range for the information of one's Supporting SAP Stream
Columns AH-AT are the Supporting SAP Stream's Job Responsibilities
Columns AU-BG collect the Secondary Supporting SAP Stream
I had built Google Script language to collect each of these bits of information in variables which would then get declared as global variables so they could be passed along to an HTML page in the App Script.
Finally, an email consisting of this HTML page would be sent out to the manager in charge of designating SAP permissions.
In previous iterations (which were much simpler), I used an IF statement to check whether a column at the end of the row was empty.
// Loop over the rows
data.forEach((row,i) => {
// Identify whether notification has been sent
if (row[59] === '') {
If it was, the Script would check the Sheet, collecting all of the aforementioned information, and send an email. Then, it would populate that last cell with "Notification Sent" so that the next time the Form was submitted and the Script checked, it would skip sending out a redundant email (and we would have a record that the process went through successfully).
The latest version included variables for
function jobResponsibilities(a){
var z = a.range.getRow();
var valuesA = a.range.getSheet().getRange(z,8,1,13).getValues();
var jobResponsibilities = valuesA.join(' ');
console.log(jobResponsibilities);
}
function supportingSAP(b){
var y = b.range.getRow();
var valuesB = b.range.getSheet().getRange(y,21,1,13).getValues();
var supportingSAP = valuesB.join(' ');
}
function secondaryJob(c){
var x = c.range.getRow();
var valuesC = c.range.getSheet().getRange(x,34,1,13).getValues();
var secondaryJob = valuesC.join(' ');
}
function secondarySAP(d){
var w = d.range.getRow();
var valuesD = d.range.getSheet().getRange(w,47,1,13).getValues();
var secondarySAP = valuesD.join(' ');
}
Collecting the four varying types of information. I used a getRange in order to scope out the exact parts of the Sheet which would have this information and join them together so that the variable could be pulled into the email in a simple table format.
For some reason, upon Form submission, the Notification IF doesn't seem to be running and the email never gets sent.
Is it a problem with the way I've ordered things? Did I improperly build the functions off what you showed me? Is it something as simple as my brackets at the bottom?

Concurrent uses of Google Scripts

I've created a simple signup form that takes in the persons name & email, saves it to a database and gives him a seven digit code in return from a database. Each code can only belong to one person. The code is run under my user from Google App Scripts.
I'm now wondering, if I need to use Lockservice or anything else to allow for concurrent use of the program? If the 2 people use this program at the same time for example, would this likely cause any problems - in example that the input.name would be from the answers of one user accessing the script and input.email would be originated from another? A simple illustration of my code
document.getElementById("registerBtn").addEventListener("click",register);
function register (){
//take values from input
var input = {};
input.name = document.getElementById("namefield").value;
input.email = document.getElementById("emailfield").value;
if (input.email && input.name) {
google.script.run.withSuccessHandler(successfun).withFailureHandler(failurefun).checkInfo(input)
}else{
M.toast({html: 'Please insert your data'});
}}
function successfun (output){
M.toast({html: 'Your code is:' + output});
}
function failurefun (output){
M.toast({html: 'This name or email have already been registered'});
}
/// ... Google Scripts:
function checkInfo(input) {
// open google spreadsheet
//check with indexOf if the email exists
//if the email does not exist, check if name exists
//if the name does not exist - append new row.
//if this is successful, open up another tab and take the first value from there, that does not have 2 //next to it & change the value next to it to 2.
//return confirmation code;
//If name or email exist: return error;
}
// Editing the question taking the guidance from the comment into account: If I'd have the script run not from my account but from each individual user, I would not have this issue?
According to the Apps Script Lock Service documentation:
Lock Service allows scripts to prevent concurrent access to sections of code. This can be useful when you have multiple users or processes modifying a shared resource and want to prevent collisions.
Thus, since this is an Apps Script service, it can be used only in your Apps Script code and later called from the HTML code.
You might also want to take a look at the Apps Script's quotas, which states that you can get at most 30 simultaneously executions.
An alternative to your approach is the solution proposed in this answer here which suggests to make use of the Utilities.getUuid().
Reference
Apps Script Lock Service;
Apps Script Quotas;
How can I facilitate concurrent users on a Google Apps Script Webapp?;
Apps Script Utilites Class - getUuid().

Is there a way to keep the sharing condition applied to copied sheets?

So this is my script for copying spreadsheets and renaming them from my current sheet but I need to keep the sharing conditions while copying. By sharing conditions I mean the tick in the box appears when you press make copy for a spreadsheet. I am kind of beginner in coding and searched for help but could not reach a thing.
function Copy_Rename_Sheet() {
var trial = '123456';
var spreadSheet = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.openById(trial);
var lr = spreadSheet.getLastRow();
for (var i = 1; i <= lr; i++) {
//Make a copy of the template file
var documentId = DriveApp.getFileById(trial).makeCopy().getId();
//Rename the copied file
var Name = spreadSheet.getRange(i, 1).getValue();
DriveApp.getFileById(documentId).setName(Name);
}
}
I understand that your goal is to copy the file sharing permissions while duplicating the file in DriveApp; in a similar way as the user interface allows you the option Share it with the same people while copying documents. If that is right, then you are very close to reaching your goal.
First you would need to read the permissions of the file. It's better to do that operation using Drive API from the Advanced Services, because that way you will have better control of your permissions. You can see here how to activate Advanced Drive Service. Then you can get a list of every permission affecting the file, but please remember to use the character * on the field request parameter to make sure that you receive a full response.
Then you can iterate that response to determine the permission role, its type and emailAddress of the user. On each iteration you could create an identical permission on the new file. If you have any additional doubt, feel free to ask me to clarify.

creating account by data from spreadsheet - google apps

I am working on a simple script that creates an account in the control panel of a domain - for example Gmail - and I was looking for a function in the Google apps script that creates an account automatically on inserting data to a spreadsheet
I searched the internet and I did find this though : https://developers.google.com/apps-script/class_usermanager
and the method I am using is : var user = UserManager.createUser("john.smith", "John", "Smith", "password
My question is, how can I insert the parameters from the spreadsheet that I have.
Sorry if this sounds a bit stupid I'm just new to Google apps script.
To read from the spreadsheet, you would use the SpreadsheetApp.
An example of reading a set of rows. (Let's say all rows).
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
.getValues() returns a 2D array. So you would access it by data[rowNum][colNum]. Let's say you want to add every row as a new user, you could do
for (var i in data) {
UserManager.createUser(data[i][0], data[i][1], data[i][2], data[i][3]);
}
How would you run said script? You could put all of it inside some function (function addAllUsers()) and then run it from the run menu in the Script Editor.

Categories