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'
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
Here what I want to do is :
Send an email by mail merge by writing a script in App Script. (Success)
I have configured 2 domain email addresses in my Gmail along with my basic Gmail address.
I want to send an email via xyz#domainname.com instead of xyz#gmail.com, the Google Sheets owner.
here what I have done to send an email via Gmail
var first = 0;
var last = 1;
var email = 2;
var emailTemp = HtmlService.createTemplateFromFile("email");
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NAJ");
var data = ws.getRange("A2:C" + ws.getLastRow()).getValues();
data.forEach(function(row){
emailTemp.fn = row[first];
emailTemp.ln = row[last];
var htmlMessage = emailTemp.evaluate().getContent();
GmailApp.sendEmail(row[email], "Important Test", "Your email does not support HTML.",
{name: "Email App", htmlBody: htmlMessage}
);
});
}
The only thing I need is to change the sending email address.
Any help would be highly appreciated. pardon my bad English
Explanation:
Your goal is to send emails from a specific email account regardless of the email account that is currently executing the script.
Approach 1:
One way to approach that is to use an installable trigger. As per the documentation:
Installable triggers always run under the account of the person who
created them. For example, if you create an installable open trigger,
it runs when your colleague opens the document (if your colleague has
edit access), but it runs as your account. This means that if you
create a trigger to send an email when a document is opened, the email
is always be sent from your account, not necessarily the account that
opened the document.
The idea here is to create an installable onEdit trigger from xyz#domainname.com so whenever the other emails edit a particular cell, the emails will be send.
The limitation of this approach is that we need to make sure that we trigger the code when we really want to and not every time a cell is edited. We can create a checkbox, and upon clicking on the checkbox (it does not matter which account did that) then the script will be executed and the emails will sent by the email account who created the installable trigger.
Approach 2:
Create email aliases. Here is the official documentation on how to do that and also this thread might be useful to create them programmatically. The restriction here is that to add an email alias, you must be a Google Workspace administrator.
Solutions:
Solution 1:
Create a checkbox in cell D1 of the sheet NAJ. The cell is up to you but you need to adjust the script accordingly.
Change the name of the function to add the event object. This will allow us to get edit info such as which cell is edited.
The new function will be:
function myInstallableOnEditFunction(e) {
const arng = e.range;
if(arng.getSheet().getName()=="NAJ" && arng.getA1Notation()=='D1' && arng.getValue()==true){
var first = 0;
var last = 1;
var email = 2;
var emailTemp = HtmlService.createTemplateFromFile("email");
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NAJ");
var data = ws.getRange("A2:C" + ws.getLastRow()).getValues();
data.forEach(function(row){
emailTemp.fn = row[first];
emailTemp.ln = row[last];
var htmlMessage = emailTemp.evaluate().getContent();
GmailApp.sendEmail(row[email], "Important Test", "Your email does not support HTML.",
{name: "Email App", htmlBody: htmlMessage}
);
});
}
}
Finally, create an installable onEdit trigger function from the xyz#domainname.com account. You can do that either manually from the current project's trigger menu or programmatically.
After that, upon clicking on cell D1 by any account, the emails will be sent by xyz#domainname.com.
Solution 2:
After you have created email aliases (see Approach 2), you can use the getAliases() method, here is a sample script. I haven't tested on my own, but you can explore also this possibility.
// Log the aliases for this Gmail account and send an email as the first one.
var me = Session.getActiveUser().getEmail();
var aliases = GmailApp.getAliases();
Logger.log(aliases);
if (aliases.length > 0) {
GmailApp.sendEmail(me, 'From an alias', 'A message from an alias!', {'from': aliases[0]});
} else {
GmailApp.sendEmail(me, 'No aliases found', 'You have no aliases.');
}
I'm creating a script that reads specific emails and retrieves data from them to create responses on a google form.
It is working as a charm when I have a collaborator access to the form, but it throw me this error message when I'm not...
"No item with the given ID could be found, or you do not have permission to access it"
var logEnvironment = "TEST"; // TEST for Testing environment, PRODUCTION for production
// Get Ids for the Google Form and Response Spreadsheet
if (logEnvironment == "TEST") {
var premediaFormId = <MY TEST FORM ID>
var responseWorkbookId = *******;
} else {
var premediaFormId = <MY PRODUCTION FORM ID>;
var responseWorkbookId = *******;
}
// Google Form
var premediaForm = FormApp.openById(premediaFormId);
var premediaFormItems = premediaForm.getItems();
On the production form, I can't give access to everybody running the script as collaborators.. they should have only regular access to create responses.. but we don't want the users to be entering the responses manually if we already have an email from a system with all the information.
Integrating the system with the form is out of the question.
Is there a way to resolve this access issue....
I have a google spreadsheet that takes information from two different forms.
Manually triggering using the spreadsheet UI does not let you distinguish which form is specified when choosing "OnFormSubmit" in the triggers menu.
Thus, I am using the following code (for my sheet) to manage two different triggering events for two different form-submits:
function onOpen(e) {
var form = FormApp.openById('ID of Form 1');
ScriptApp.newTrigger('onForm1Submit')
.forForm(form)
.onFormSubmit()
.create();
var signup = FormApp.openById('[ID of Form 2]');
signup.setRequireLogin(true)
ScriptApp.newTrigger('SignUpEvent')
.forForm(signup)
.onFormSubmit()
.create();
}
function SignUpEvent(e) {
\\stuff
}
function onForm1Submit(e) {
\\stuff
}
But when I do it this way, I am recieving a failure notification on form submit:
Function: UpdateLadder
Error Message: "Authorization is required to perform that action."
Trigger: "formSubmit"
First off, how am I recieving these email notifications to begin with? I didn't manually ask for email notifications of error messages.
Secondly, what's the best way for me to get "authorization"?
For the onFormSubmit() function, you can write code to match the destination sheet to a certain function. For example, I have a spreadsheet that has 2 forms that both have sheets for destination within it. In the onFormSubmit() trigger code, here is what I did:
function onFormSubmit(e) {
Logger.log("[METHOD] onFormSubmit");
var sheet = e.range.getSheet();
var sheetId = sheet.getSheetId();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var student = ss.getSheetByName("Student Requests");
var studentId = student.getSheetId();
var teacher = ss.getSheetByName("Teacher Requests");
var teacherId = teacher.getSheetId();
if (sheetId == studentId){
sendStudentEmail(e.range);
}
if (sheetId == teacherId){
sendTeacherEmail(e.range)
}
}
So, in this example, a Teacher can request using a form, and if the onFormSubmit is related to the Teacher form, then send the Teacher email, etc..
I'm reasonably certain that onFormSubmit ties one spreadsheet to one form; it doesn't know what to do with two onFormSubmit triggers.
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;
}