I am able to read a manipulate scripts but am unable to write my own effectively. I would like to ask for some help.
The Issue: I have a sheet where managers most go in and make their approval or rejection right from the sheet. Their email addresses are in the sheet as data.
Goal: I have been trying to find a script I can modify that will look at the column the user's emails at kept and use that data to set the permission for the row automatically instead of me having to create the protected ranges manually for 700+ managers.
I really appreciate any help on this.
The following is what little code I've been experimenting with. I don't know if it will help.
enter code herefunction initiate (){
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
for(n=0;n<values.length;++n){
var user = values[1][1] ; // x is the index of the column starting from 0
var manager = user.valueOf();
Logger.log(manager);
}
var protections = values.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
Related
I have a Google Spreadsheet where I retrieve data using the Google Analytics addon. In a tab I combine all the data and use this specific tab for my Google Datastudio report. In here I analyze my Google Ads campaign data - the costs and the conversions.
This Spreadsheet has multiple tabs (each tab contains a specific conversion) I need to be able to read and write for multiple tabs. It has to read the Google Analytics data and write this data in another sheet.
I originally created this script to read from 1 tab and to write to 1 tab and it was working. Then I noticed I will need to do this for almost all the tabs.
This is my current script:
function readAndWrite() {
var spreadsheetId = 'xxx';
var counter = 0;
var rangeName = ['readingTab1!A16:C','readingTab2!A16:C','readingTab3!A16:C','readingTab4!A16:C','readingTab5!A16:C'];
var rangePush = ['writingTab1','writingTab2','writingTab3','writingTab4','writingTab5','writingTab5'];
var values = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeName[counter]).values;
var data = [];
if (!values) {
Logger.log('No data found.');
} else {
Logger.log('Starting script.');
Logger.log('There are ' + values.length + ' unique rows.');
Logger.log(values[0][2]);
while (counter < 5){
data.length = 0;
for (var row = 0; row < values.length; row++) {
var campaign = values[row][0];
var date = values[row][1];
var cost = values[row][2];
data.push({range: rangePush[counter], values: [[date, campaign, cost]]});
}
counter++;
Sheets.Spreadsheets.Values.batchUpdate({data: data, valueInputOption: "USER_ENTERED"},spreadsheetId);
}
}
Logger.log('Finished.');
}
In the rangeName I have created an array with the names and ranges of my tab where it should read from.
In the rangePush I created an array with the names where it should push the data to.
When I run the script I receive the following error:
GoogleJsonResponseException: API call to sheets.spreadsheets.values.batchUpdate failed with error: Invalid data[1886]: Unable to parse range: writingTab2.
Is there anyone who can see what is going wrong and able to help me out here?
I hope my explanation is clear, if not please let me know.
Thanks!
I believe your goal is as follows.
You want to copy the values from readingTab#!A16:C sheet to writingTab# sheet using Sheets API with Google Apps Script.
Those sheet names are put to the arrays and each index of both is corresponding to the copy and paste sheets.
If my understanding is correct, how about the following modification?
At first, about your error, when I saw your sample Spreadsheet, there are 3 read and write sheets of 'readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C' and 'writingTab1', 'writingTab2', 'writingTab3'. But your script uses while (counter < 5){. By this, the range becomes undefined after counter is more than 4. I thought that this might be the reason of your issue.
In order to achieve your goal by removing this issue, how about the following modified script?
Modified script:
When spreadsheets.values.batchGet and spreadsheets.values.batchUpdate methods are used, the script becomes the following simple script.
function readAndWrite() {
var spreadsheetId = 'xxx';
var rangeName = ['readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C'];
var rangePush = ['writingTab1', 'writingTab2', 'writingTab3'];
var values = Sheets.Spreadsheets.Values.batchGet(spreadsheetId, {ranges: rangeName}).valueRanges;
values.forEach((v, i) => v.range = rangePush[i]);
Sheets.Spreadsheets.Values.batchUpdate({ data: values, valueInputOption: "USER_ENTERED" }, spreadsheetId);
}
In this modified script, it supposes that there are 3 read and write sheets of 'readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C' and 'writingTab1', 'writingTab2', 'writingTab3'. When you add more sheets, please add them to the arrays of rangeName and rangePush.
Note:
In this script, please check the values of rangeName and rangePush and the sheet names of your Spreadsheet again.
This script uses Sheets API. So, please enable Sheets API at Advanced Google services.
I think that in your situation, the Spreadsheet service instead of Sheets API can also achieve your goal. But I think that the process cost will be lower when Sheets API is used. But, when you want to achieve your goal without using Sheets API, you can also use the following script.
function readAndWrite2() {
var spreadsheetId = 'xxx';
var rangeName = ['readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C'];
var rangePush = ['writingTab1', 'writingTab2', 'writingTab3'];
var ss = SpreadsheetApp.openById(spreadsheetId);
rangeName.forEach((r, i) => ss.getRange(r).copyTo(ss.getSheetByName(rangePush[i]).getRange("A1")));
}
References:
Method: spreadsheets.values.batchGet
Method: spreadsheets.values.batchUpdate
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.
I am writing a script that fetches a data from a spread sheet and based on every entry entered in the spread sheet, it fetches the data which is basically email addresses and deletes a user's account from the domain.
//** Delete the users on submitting the "Submit" button
function onFormSubmit(e) {
//Logger.log(e);
//Run this function passing on event "e" when the form is submitted.
//Object e has form parameters sourced from the sheet attached to the form
deleteUsers(e);
}
//Logs all the info from the spreadsheet and deletes the user
function deleteUsers() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
Logger.log('managerEmail: ' + data[i][0]);
Logger.log('email: ' + data[i][1]);
Logger.log('trasferToEmail: ' + data[i][3]);
var managerEmail = data[i][0];
var email = data[i][1];
var trasferToEmail = data[i][3];
var request = {
'url': 'https://www.googleapis.com/admin/directory/v1/users/' + email,
'method' : 'DELETE'
};
}
But I am still unsuccessful in deleting an account. I actually tried to implement it based on this doc https://developers.google.com/admin-sdk/directory/v1/reference/users/delete but didn't know how to use it. Any ideas? Sorry if this is a stupid question! I am a novice in Google scripts.
You're sending e to deleteUsers(), but that function doesn't receive any parameters. No need to access the spreadsheet data when it's already provided by the onFormSubmit()–look at the event object documentation for reference.
function deleteUser(e) {
var data = e.namedValues;
var managerEmail = data["Manager Email"][0]; //You'll need to adjust the field names to match with your form data
var email = data["Email"][0];
var transferToEmail = data["Transfer to Email"][0];
var response = AdminDirectory.Users.remove(email);
}
To make sure that your trigger is correctly set up, first have the form responses get saved to your spreadsheet. Then Edit > Current project's triggers and copy these settings:
To make AdminDirectory work, you need to enable advanced services. (In the script editor, go to Resources > Advanced Google services and switch "on" Admin Directory API. Then click the link at the bottom of the modal to enable the Admin SDK in the API Console.)
If I was mistaken about what data the form is collecting and you really do need to pull the data from spreadsheet (assuming the form isn't connected to the sheet), then you need to create a trigger for when there is a submission to that form. Run this function to install the trigger.
function installFormTrigger() {
var form = FormApp.openById("FORM_ID");
ScriptApp.newTrigger("deleteUsers")
.forForm(form)
.onFormSubmit()
.create();
}
Then your original deleteUsers() function will work almost as you had it, but with the addition of AdminDirectory.
function deleteUsers() {
var sheet = SpreadsheetApp.getActive().getSheetByName("SheetName"); //HIGHLY recommend using this instead
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
var managerEmail = data[i][0];
var email = data[i][1];
var trasferToEmail = data[i][3];
var response = AdminDirectory.Users.remove(email);
}
}
Note that in your for loop, you might come across an invalid email or AdminDirectory could through an error, so I'd suggest implementing try...catch and logging.
It would be better to use AdminDirectory.Users.remove(email); rather than making a request to the API like you are doing.
Keep it in a variable if you want to log the response var request = AdminDirectory.Users.remove(data[i][1]);
To activate the AdminDirectory;
Go to Resources -> Advanced Google services
Enable the Admin Directory and then click on the link to the Google API console.
Click Enable APIs and Services
Search for Admin SDK
Click on Admin SDK and then click 'Enable'
Does anyone know if there's a way to access the old (ie, pre-edit) formula, as opposed to oldValue, of the edit event object? I thought e.oldFormula would work, but it's undefined and not even in the documentation.
I'm working on a script to require a password when editing certain ranges but, in order to undo the edit if the user fails to provide the correct password, I need to know what was in the cell before. For cells that contain numbers or strings, e.oldValue works great, but most of the cells I want to protect contain formulas (that's why I want to protect them) so e.oldValue stops the data from updating like it should.
Here's the code I've got that only works if the cell contains no formulas (other than that, it works great:
// Prompt for password before allowing edit of formula cells
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getActiveCell();
var editRange = e.range;
var editCol = editRange.getColumn();
if (editCol != 4 ) {
var password = "321"; // not actual password
var passwordAttempt = Browser.inputBox('Enter Password to edit protected range, or hit cancel to cancel edit:', Browser.Buttons.OK_CANCEL);
if(passwordAttempt == password) {
Browser.msgBox('Edit Accepted');
} else {
if (passwordAttempt != 'cancel'){
Browser.msgBox('Incorrect password. Contact James Atkins for password to edit protected ranges.');
}
if(e.oldValue){
Browser.msgBox('old value is defined as ' + e.oldFormula);
e.range.setValue(e.oldValue);
} else {
e.range.clear();
}
}
FYI, I'm doing this because the built-in protected ranges in Google Sheets don't distinguish between a user changing the contents of a cell by manually editing it and by activating a script that changes the contents. I want to allow all users to activate scripts that sort and clear certain ranges, but not let them mess with the contents manually.
You can solve this problem by creating a clone of your sheet to keep track of your formula. Make the clone sheet off limits to end users. So they cannot edit it.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cloneSheet = ss.getSheetByName("cloneSheet")
var cell = sheet.getActiveCell();
var editRange = e.range;
var cloneRange = cloneSheet.getRange(editRange.getA1Notation())
var editCol = editRange.getColumn();
if (editCol != 4 && sheet.getName() != "cloneSheet") {
var password = "321"; // not actual password
var passwordAttempt = Browser.inputBox('Enter Password to edit protected range, or hit cancel to cancel edit:', Browser.Buttons.OK_CANCEL);
if(passwordAttempt == password) {
Browser.msgBox('Edit Accepted');
cloneRange.setValue(editRange.getFormula()) //modify the clone to match the new formula.
} else {
if (passwordAttempt != 'cancel'){
Browser.msgBox('Incorrect password. Contact James Atkins for password to edit protected ranges.');
}
if(e.oldValue){
Browser.msgBox('old value is defined as ' + cloneRange.getFormula());
e.range.setValue(cloneRange.getFormula());
} else {
e.range.clear();
}
}
}
}
However, end user can still see the password. If you really what end user to make only limited changes the below solution is better.
Alternate Solution
When an end user executes a script, even though you have written the script, it runs with scope/identity of the end user. So if they don't have edit permission then the script that runs under their alias also does not have it either. This is in response to your comment:
google sheets don't distinguish between a user changing the contents of a cell by manually editing it and by activating a script that changes the contents.
So how to get around this problem, you can use a google web app to run sort and clear scripts as you. The end user uses a script that makes a call to this script using UrlFetch, this gets around the problem of scopes and prevents them from having a look at your code/password etc.
Here is SO question that addressing a similar problem:
Google Apps Script: Temporarily run script as another user
I have created custom Ribbon Button and I want to check Rating Settings is enable or not.
If Rating Settings is Enable then Enable Ribbon Button, else Disable Ribbon Button.
This is code to get current List, but I don't see any function to check Rating Settings:
var clientContext = new SP.ClientContext();
var oWebsite = clientContext.get_web();
var collList = oWebsite.get_lists();
var listId = SP.ListOperation.Selection.getSelectedList();
var sdlist = oWebsite.get_lists().getById(listId);
clientContext.load(sdlist);
function CheckRatingSettings(){
var fields = sdlist.get_fields();
//What next to check Ratting Setting, I can't find any function to get that
}
I dont think you can query that from the client API. Your best bet should be to implement a WCF service that checks that on the server and then query that Service through javascript.
Check CKS developer tools, they have among other goodies a template for visual studio to create WCF services on Sharepoint.
Another option is to check if the field "AverageRating" exists on the list, this field is added to the list when you enable ratings. Bear in mind that the field might exist but the ratings may be disabled though.
This is not tested (and assuming your code is getting the right listid, something along the lines of this:
var fields = slList.get_fields();
clientContext.load(fields );
function CheckRatingSettings(fields){
for (var i = 0; i < fields.length; i++) {
if (fields[i].internalName == "AverageRating") {
return true;
}
}
return false;
}