Fetch the files of suspended users - google-apps-script

Is there a method in google apps script to get the files of a suspended owner based on his/her email id? I am literally trying to implement transfer drive files to a new owner using google apps script, I am using this code to find the details of all the suspended users:
/**
* Fetches the suspended user details from the AdminDirectory.
*/
function fetchUser() {
// Set the constant options only once.
const options = {
domain: 'xyz.com',
orderBy: 'email',
query: 'isSuspended=true',
maxResults: 500,
fields: "nextPageToken,users"
};
// Could log the options here to ensure they are valid and in the right format.
const results = [];
do {
var search = AdminDirectory.Users.list(options);
// Update the page token in case we have more than 1 page of results.
options.pageToken = search.nextPageToken;
// Append this page of results to our collected results.
if(search.users && search.users.length)
Array.prototype.push.apply(results, search.users);
} while (options.pageToken);
//Logger.log(results);
for(var k = 0; k < results.length; k++){
var fullEmail = results[k].primaryEmail;
Logger.log(fullEmail);
fetchFiles(fullEmail);
}
}
/**
* Fetches the files of the suspended users based on their email.
*/
function fetchFiles(email){
var pageToken;
var filesList = Drive.Files.list({ // Invalid value error is thrown here, I am not sure if this the right way to use Drive API in google script
domain: 'xyz.com',
orderBy: 'email',
q: "'email' in owners",
maxResults: 500,
pageToken: pageToken
});
Logger.log('filesList:' +filesList);
}
I am trying to implement something like Transfer ownership of a file to another user in Google Apps Script, but is there some way by which I can fetch the files of the user from the details obtained from the above code which basically returns the following output:
[
{
orgUnitPath = /,
ipWhitelisted = false,
gender = {type = other},
creationTime = 2017-06-13T14:38:44.000Z,
isMailboxSetup = true,
isEnrolledIn2Sv = false,
kind = admin#directory#user,
suspensionReason = ADMIN,
isAdmin = false,
suspended = true,
emails = [{
address = john.seb#xyz.com,
primary = true
}],
lastLoginTime = 2017-09-12T00:27:00.000Z,
isDelegatedAdmin = false,
isEnforcedIn2Sv = false,
changePasswordAtNextLogin = false,
customerId = v12idsa,
name = {
givenName = John,
familyName = Seb,
fullName = John Seb
},
etag = "npJcgeAc7Xbfksfwer22344/sfasdfaffUDsfdsjfhsfhsdfw-ec",
id = 1033392392142423424,
primaryEmail = john.seb#xyz.com,
agreedToTerms = true,
includeInGlobalAddressList = true
},
...
]
I am trying to use the Drive API in the google apps script in order to access the files of suspended users using their email, but its throwing
"Invalid Value" error
for the line Drive.Files.list, I am not sure if this is the right way to use the DRIVE API in google script, is there any other way to use this api in google script?

In your function fetchFiles, you never use the input argument email. Instead, you query the Drive REST API for the literal text 'email' in owners. Since the text string 'email' is not a valid email address, you correctly receive the error message "Invalid value".
Rather than this code:
/**
* Fetches the files of the suspended users based on their email.
*/
function fetchFiles(email){
var pageToken;
var filesList = Drive.Files.list({
domain: 'jerseystem.org',
orderBy: 'email',
q: "'email' in owners",
maxResults: 500,
pageToken: pageToken
});
Logger.log('filesList:' +filesList);
}
You should perform the substitution first, to set up all constant options (as I mentioned and demonstrated in your other question), and then repeatedly query the Drive API for the files owned by the user with that email address:
function fetchAllFilesOwnedByEmail(email) {
const searchParams = {
corpora: 'some corpora',
orderBy: 'some ordering criteria',
q: "'" + email + "' in owners",
fields: 'nextPageToken,items(id,title,mimeType,userPermission)'
};
const results = [];
do {
var search = Drive.Files.list(searchParams);
if (search.items)
Array.prototype.push.apply(results, search.items);
searchParams.pageToken = search.nextPageToken;
} while (searchParams.pageToken);
return results;
}
You need to review the Drive REST API documentation, see Drive.Files.list and Search for Files at minimum. Don't forget to enable the Drive advanced service if you haven't.
Also note that while the above code will resolve the "Invalid Value" error you get, it won't necessarily make the user's files show up, even if you're executing the code as the G-Suite admin. Your research should have turned up at least these two related questions:
How can an Admin access the Google Drive contents of all the users in a particular domain?
Most efficient process for transferring ownership of all of a user's files using the Google Drive API

Related

Google Apps Script Google People API get contacts from contact group

I have a script (using Google Apps Script) that uses the Contacts API to pull the emails from a contactgroup, then sends an email to the group of contacts. Attempting to convert to the PeopleAPI and cannot seem to replicate the functionality. Here's the relevant script sample with the working ContactsAPI code included but commented out:
function sharereport() {
//var CGroup = ContactsApp.getContactGroup('group_name');
//var Dist = CGroup.getContacts();
var CGroup = People.ContactGroups.get('contactGroups/498ba6e40f63a476')
var Dist = People.People.getBatchGet('CGroup','people.email_addresses');
.
.
.
for (var i = 0; i < Dist.length; i++) {
var nextemail = Dist[i].getEmails();
var usethisemail = nextemail[0].getAddress();
Drive.Permissions.insert(
// #ts-ignore
{
'role': 'writer',
'type': 'user',
'value': usethisemail,
},
MyID,
{
'sendNotificationEmails': 'false',
});
MailString = MailString + ',' + usethisemail;
};
I'm sure I'm missing something really simple here to get the PeopleAPI to return an array of contacts that I can get the email addresses out of so I can populate the drive permissions and email to: field.
Since I did not found any methods getting them directly, I did use People.People.Connections.list and filtered the data till I get the emails. This should what your code will look like.
function sharereport() {
var CGroup = ContactsApp.getContactGroup('label1');
var Emails = CGroup.getContacts().map(function (contact) {
return contact.getEmailAddresses();
});
// Show emails of people belonging to the group label1
Logger.log(Emails);
var PGroup = People.People.Connections.list('people/me', {
personFields: 'emailAddresses,memberships'
});
// resource name of label1
var resourceName = 'contactGroups/7086c0fa8e7b006b';
var Emails2 = [];
PGroup.connections.forEach(function (person) {
person.memberships.forEach(function (membership) {
if (resourceName == membership.contactGroupMembership.contactGroupResourceName) {
var addresses = [];
person.emailAddresses.forEach(function (emailAddress){
// people can have multiple email addresses, add them all
addresses.push(emailAddress.value);
});
Emails2.push(addresses);
}
});
});
Logger.log(Emails2);
}
Behavior:
get all the people connections under you.
get all their memberships
check if their memberships resource name is equal to the one you want. (they can belong to multiple memberships)
loop all emails of that certain person (they can have multiple emails), then push into one array.
push that array into final array containing all emails of all people belonging to the resource name.
Output:
Logged both ContactsApp and People API results, below shows that they were the same.
Resources:
people.connections/list
advanced/people
Here's a more efficient way of doing what you want:
var group = People.ContactGroups.get('contactGroups/50e3b0650db163cc', {
maxMembers: 25000
});
Logger.log("group: " + group);
var group_contacts = People.People.getBatchGet({
resourceNames: group.memberResourceNames,
personFields: "emailAddresses"
});
Logger.log("emails: " + group_contacts.responses.map(x => {
var emailObjects = x.person.emailAddresses;
if (emailObjects != null) {
return emailObjects.map(eo => eo.value);
}
}));
First call gets all the group's members (resourcesNames AKA contactIds)
Second call gets all the members' email-addresses.
Then we just get the actual value of the email from the response (via map)

Is it possible to load google photos metadata into google sheets?

I have a project where I have scanned 10,000 family pictures from as far back as the 1900's and I am organizing them in Google Photos. I have a spreadsheet where I was keeping track of the proper dates and captions for the entire collection. I would organize a few at a time but then recently found out about the google photos API.
I would like to use something like the methods Method: mediaItems.list or Method: mediaItems.search to get the data from my photos into the spreadsheet to manage.
The output from these examples is exactly what I'm looking for and would want to load that into a spreadsheet.
It would be super awesome if there was a way to update back from the sheet again as well.
I found this article but the code provided does not work for me.
I have this function now in my sheet
function photoAPI() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length);
albums_sh.clear();
var narray = [];
var api = "https://photoslibrary.googleapis.com/v1/albums";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
var param= "", nexttoken;
do {
if (nexttoken)
param = "?pageToken=" + nexttoken;
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
json.albums.forEach(function (album) {
var data = [
album.title,
album.mediaItemsCount,
album.productUrl
];
narray.push(data);
});
nexttoken = json.nextPageToken;
} while (nexttoken);
albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
When I run it in debug mode, I get the following error
({error:{code:403, message:"Request had insufficient authentication scopes.", status:"PERMISSION_DENIED"}})
I know this means I need to authenticate but don't know how to make that happen.
I have an API key and a secret from the Google photos API pages.
Edit
I used the links from #Tanaike to figure out how to add scopes to my project.
I added these three.
spreadsheets.currentonly
photoslibrary
script.external_request
Now when I run in debug mode, I get a 403 error indicating I need to set up my API. Summary of the error is below:
error:
code:403
Photos Library API has not been used in project 130931490217 before or it is disabled. Enable it by visiting
https://console.developers.google.com/apis/api/photoslibrary.googleapis.com/overview?project=130931490217
Google developers console API activation
type.googleapis.com/google.rpc.Help
"PERMISSION_DENIED"
When I try to go to the listed URL though, I just get a message that says "Failed to load."
I got my code working with the help of #Tanaike in my comments above. I had two issues.
1) I needed to specify the oauthScopes in appsscript.json which is hidden by default in google scripts. It can be revealed by going to the menu and selecting View > Show Manifest File.
2) I was using a default GCP project which did not have authorization to use the photos API and could not be enabled. I needed to switch to a standard GCP project which I had created earlier and had enabled the photos API.
Here is my original posted function with additional comments after I got it working:
function photoAPI_ListAlbums() {
// Modified from code by Stackoverflow user Frç Ju at https://stackoverflow.com/questions/54063937/0auth2-problem-to-get-my-google-photos-libraries-in-a-google-sheet-of-mine
// which was originally Modified from http://ctrlq.org/code/20068-blogger-api-with-google-apps-script
/*
This function retrieves all albums from your personal google photos account and lists each one with the name of album, count of photos, and URL in a new sheet.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length);
//Make sure the target sheet is empty
albums_sh.clear();
var narray = [];
//Build the request string. Default page size is 20, max 50. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/albums?pageSize=50";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
var param= "", nexttoken;
//Make the first row a title row
var data = [
"Title",
"Item Count",
"ID",
"URL"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Loop through the JSON object adding desired data to the spreadsheet.
json.albums.forEach(function (album) {
var data = [
"'"+album.title, //The prepended apostrophe makes albums with a name such as "June 2007" to show up as that text rather than parse as a date in the sheet.
album.mediaItemsCount,
album.id,
album.productUrl
];
narray.push(data);
});
//Get the nextPageToken
nexttoken = json.nextPageToken;
//Continue if the nextPageToaken is not null
} while (nexttoken);
//Save all the data to the spreadsheet.
albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
And here is another function which I created in the same style to pull photo metadata directly. This is what I was originally trying to accomplish.
function photoAPI_ListPhotos() {
//Modified from above function photoAPI_ListAlbums
/*
This function retrieves all photos from your personal google photos account and lists each one with the Filename, Caption, Create time (formatted for Sheet), Width, Height, and URL in a new sheet.
it will not include archived photos which can be confusing if you happen to have a large chunk of archived photos some pages may return only a next page token with no media items.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
//Make sure the target sheet is empty
photos_sh.clear();
var narray = [];
//Build the request string. Max page size is 100. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
//This variable is used if you want to resume the scrape at some page other than the start. This is needed if you have more than 40,000 photos.
//Uncomment the line below and add the next page token for where you want to start in the quotes.
//var nexttoken="";
var param= "", nexttoken;
//Start counting how many pages have been processed.
var pagecount=0;
//Make the first row a title row
var data = [
"Filename",
"description",
"Create Time",
"Width",
"Height",
"ID",
"URL",
"NextPage"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Check if there are mediaItems to process.
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
//Loop through the JSON object adding desired data to the spreadsheet.
json.mediaItems.forEach(function (MediaItem) {
//Check if the mediaitem has a description (caption) and make that cell blank if it is not present.
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
//Format the create date as appropriate for spreadsheets.
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description, //The prepended apostrophe makes captions that are dates or numbers save in the sheet as a string.
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
//Get the nextPageToken
nexttoken = json.nextPageToken;
pagecount++;
//Continue if the nextPageToaken is not null
//Also stop if you reach 400 pages processed, this prevents the script from timing out. You will need to resume manually using the nexttoken variable above.
} while (pagecount<400 && nexttoken);
//Continue if the nextPageToaken is not null (This is commented out as an alternative and can be used if you have a small enough collection it will not time out.)
//} while (nexttoken);
//Save all the data to the spreadsheet.
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
Because of the limitations of the ListPhotos function and the fact that my library is so enormous, I am still working on a third function to pull photo metadata from all the photos in specific albums. I'll edit this answer once I pull that off.

List of user from the Secondary Domain in Google Apps

I create a script to get all the users in one SpreadSheet, Name and Email, but I'm not able to get the users that are created under the secondary domain.
This is my code:
function writeToSpreadsheet(){
var values = [],
users = [],
userListQuery = {},
nextPageToken = '',
listObject = {
domain:'the domain name',
maxResults: 500,
},
i = 0,
activeSpreadsheet;
do {
if (nextPageToken && nextPageToken !== '') {
listObject.pageToken = nextPageToken;
}
userListQuery = AdminDirectory.Users.list(listObject);
// if there are more users than fit in the query a nextPageToken is returned
nextPageToken = userListQuery.nextPageToken;
// Add the query results to the users array
users = users.concat(userListQuery.users);
} while (nextPageToken);
for (i = 0; i < users.length; i += 1) {
values.push([users[i].name.fullName, users[i].primaryEmail]);
}
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(2, 1, values.length, values[0].length).setValues(values);
}
Use the Google Admin Settings API, it allows administrators of Google Apps domains to retrieve and change settings of their domains in the form of Google Data API feeds.
To retrieve from secondary domain, you may send HTTP GET to the account information administrator secondary email address feed URL and include an Authorization header as described in Authenticating to the Admin Settings service
A successful response returns an HTTP 200 OK, along with the administrator's secondary email address.
Sample HTTP GET request:
https://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/accountInformation/adminSecondaryEmai

403 error when executing Google Apps Script form a different google account

I have this javascript to extract html table and then pass the arrays to google apps script as parameters.
var CLIENT_ID = 'some ID';
var SCRIPT_ID = 'some ID';
var SCOPES = ['https://www.googleapis.com/auth/drive.file'];
function handleAuthClick(event) {
gapi.auth.authorize(
{'client_id': CLIENT_ID, 'scope': SCOPES, 'immediate': true},
handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult) {
// Access token has been successfully retrieved, requests can be sent to the API
} else {
// No access token could be retrieved, force the authorization flow.
gapi.auth.authorize(
{'client_id': CLIENT_ID, 'scope': SCOPES, 'immediate': false},
handleAuthResult);
}
}
function exportGsheet() {
var myTableArray = [];
$("table#fin tr").each(function() {
var arrayOfThisRow = [];
var tableData = $(this).find('td');
if (tableData.length > 0) {
tableData.each(function() { arrayOfThisRow.push($(this).text()); });
myTableArray.push(arrayOfThisRow);
}
});
var params = JSON.stringify(myTableArray);
var request = {
'function': 'setData',
'parameters': params,
'devMode': true // Optional.
};
var op = gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + SCRIPT_ID + ':run',
'method': 'POST',
'body': request
});
op.execute(function(resp){opensheet(resp)});
}
Below is the apps script. This uses Drive API and Executable API.
var DOC_ID = 'some id';
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var folder = DriveApp.getFolderById('some id');
function setData(parameters) {
var getFile = DriveApp.getFileById(DOC_ID);
var file = getFile.makeCopy(formattedDate, folder);
var ss = SpreadsheetApp.open(file);
var ssId = ss.getId();
ss.getSheets()[0].getRange(5,1,50,24).clear();
var e = JSON.parse(parameters);
var outerArray = [];
for(var i = 0; i < e.length; i++) {
outerArray.push(e[i]);
}
ss.getSheets()[0].getRange(5, 2, outerArray.length, outerArray[0].length).setValues(outerArray);
return {"url":ssId};
Logger.log(ssId);
}
Everything works fine when I authorize using the gmail ID that owns the apps script and project (my own gmail account). But when I authenticate using a different gmail account I get the below error:
error: {code: 403, message: "The caller does not have permission", status: "PERMISSION_DENIED"}
code: 403
message: "The caller does not have permission"
status: "PERMISSION_DENIED"
I intend to make this application public and anyone should be able to authenticate using their gmail account and execute the script. How do I do that? Please help.
Folks, I figured out the problem later. It happened to be permission issue n Developer console. We have to assign permission under Developer Console Project for the project which the apps-script is associated. So follow these steps:
Open your apps script Go to Resources-Developers Console Project
Click on the project name appearing in blue under "This script is
currently associated with project:" It will redirect you to Developer
Console Project.
Click on Menu on the left hand side upper corner and click on
Permissions
Under Permissions click on Add members
In the member type the email ID or domain you want to provide
permission and desired permission level. Click on 'Add'
You are done.
However, I wasnt able to add the entire gmail domain, nor able to add allAuthenticatedUsers. I have raised an issue with google support

Get all of domain's email addresses by a Google Apps Script

Is it possible to get all domain email addresses (#example.com) to a variable? (the domain is of course connected with Gmail). I was searching and have found just ContactsApp class, which can give contacts from address books, but I need all company's email addresses.
To get user information like this for your entire domain, you need to use the Admin SDK Directory API. You will need to query Google Apps for your domain's users.
var optionalArgs = {"customer":"my_customer"},
response,
users = []
;
response = AdminDirectory.Users.list(optionalArgs);
users = response.users;
This will get you your first page of users, so you may need to create a loop to fetch all of them. You will need to understand the User resource, which is returned for each of your users. Both their primary domain email and their aliases can be found in this resource:
var email,
aliases
;
email = users[0].emails[0]; // if email.primary is true, email.address is the primary domain email for this user
aliases = users[0].aliases; // list of user's alias email addresses
More information can be found in the documentation
This is the function to get all the users email address list in appscript .
function listAllUsers1() {
var pageToken;
var page;
do {
page = AdminDirectory.Users.list({
domain: 'abc.com',
orderBy: 'givenName',
maxResults: 100,
pageToken: pageToken,
});
var users = page.users;
Logger.log(users);
if (users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
Logger.log(user);
Logger.log('%s (%s)', user.name.fullName, user.primaryEmail);
}
} else {
Logger.log('No users found.');
}
pageToken = page.nextPageToken;
} while (pageToken);
}