Extracting permissions on a Shared Drive - google-apps-script

I'm curious if there is a way to extract all permissions within a Google Shared Drive. The current script below can do this except that it can only display one permission for each drive at a time. For example, if a shared drive was named X and had 3 users in the drive with the same permission (or different permissions), it will only display 1 out of the 3 users in Drive X. Any help will be greatly appreciated.
var GOOGLE_SHEET_URL = "https://docs.google.com/spreadsheets/";
var GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES = "Drive";
var GOOGLE_SHEET_RESULTS_TAB_NAME_PERMISSIONS = "Permissions";
const GOOGLEAPI_MAX_RESULTS_PER_PAGE = 5;
const NUMBER_OF_ROWS_TO_LOOKUP_PERMISSIONS_PER_LOOP = 4;
const USE_DOMAIN_ADMIN_ACCESS = true;
//const EMAIL_RECIPIENT_ADDRESS = 'test#gmail.com';
const EMAIL_SUBJECT_LINE = 'Appscript Google Shared Drive Audit Complete';
/*
* This is for the headers for Sheets
*/
function job_set_sheet_headers() {
var drives_sheet = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES);
drives_sheet.appendRow(["AUDIT_DATE", "ID", "NAME"]);
var permissions_sheet = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_PERMISSIONS);
permissions_sheet.appendRow(["AUDIT_DATE", "ID", "NAME", "EMAIL_ADDRESS", "TYPE", "ROLE"]);
}
/*
* This is the job that will get the full list of shared drives and put the results into the first tab in your target sheet
*/
function job_get_shared_drives_list() {
var startTime = new Date().getTime();
var audit_timestamp = Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var newRow = [];
var rowsToWrite = [];
let sharedDrives = Drive.Drives.list(
{
maxResults: GOOGLEAPI_MAX_RESULTS_PER_PAGE,
useDomainAdminAccess: USE_DOMAIN_ADMIN_ACCESS,
hidden: false,
}
);
let sharedDrivesItems = sharedDrives.items;
while(sharedDrives.nextPageToken){
sharedDrives = Drive.Drives.list(
{
pageToken:sharedDrives.nextPageToken,
maxResults: GOOGLEAPI_MAX_RESULTS_PER_PAGE,
useDomainAdminAccess: USE_DOMAIN_ADMIN_ACCESS,
hidden: false,
}
);
sharedDrivesItems = sharedDrivesItems.concat(sharedDrives.items)
//console.log(sharedDrives.items);
//console.log(sharedDrivesItems.length);
}
//console.log(sharedDrivesItems);
sharedDrivesItems.forEach(function(value) {
var newRow = [audit_timestamp, value.id, value.name];
rowsToWrite.push(newRow);
//console.log(newRow);
});
var ss = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES);
ss.getRange(ss.getLastRow() + 1, 1, rowsToWrite.length, rowsToWrite[0].length).setValues(rowsToWrite);
var endTime = new Date().getTime();
var elapsed = (endTime-startTime)/1000;
console.log('Elapsed Seconds: ' + elapsed);
}
/*
* This is the job that will loop through the first tab and look up the permissions for each shared drive.
*/
function job_get_permissions_for_drives() {
var startTime = new Date().getTime();
var drives_sheet = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES);
var rangeData = drives_sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = drives_sheet.getRange(2,1, NUMBER_OF_ROWS_TO_LOOKUP_PERMISSIONS_PER_LOOP, lastColumn);
// Get array of values in the search Range
var values = searchRange.getValues();
// console.log("values");
// console.log(values);
// Loop through array and if condition met, do relevant permissions lookup
for ( i = 0; i < NUMBER_OF_ROWS_TO_LOOKUP_PERMISSIONS_PER_LOOP; i++) {
if(values[0][0] == '') {
console.log('Source row empty. Script probably complete now.');
//send_email_simple_(EMAIL_RECIPIENT_ADDRESS,EMAIL_SUBJECT_LINE,'Source row empty for Shared Drive Audit process. The script probably complete now.');
return;
}
var newRow = [];
var rowsToWrite = [];
this_audit_date = values[i][0];
this_drive_id = values[i][1];
this_drive_name = values[i][2];
console.log('GETTING DRIVE NAME: ' + this_drive_name);
var thisDrivePermissions = Drive.Permissions.list(
this_drive_id,
{
useDomainAdminAccess: USE_DOMAIN_ADMIN_ACCESS,
supportsAllDrives: true
}
);
//loop through each permission item
var newRow = [];
if(thisDrivePermissions.items.length >= 1) {
//console.log('permissions were not empty');
thisDrivePermissions.items.forEach(function(value) {
//console.log(value);
newRow = [this_audit_date, this_drive_id, this_drive_name, value.emailAddress, value.type, value.role];
// add to row array instead of append because appending each one at a time is SLOOOOOWWWWW
});
} else {
console.log('permissions were empty');
newRow = [this_audit_date, this_drive_id, this_drive_name, 'ORPHAN', 'ORPHAN', 'ORPHAN'];
}
rowsToWrite.push(newRow);
//console.log(newRow);
// write the permissions items to the permissions tab
var ss = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_PERMISSIONS);
ss.getRange(ss.getLastRow() + 1, 1, rowsToWrite.length, rowsToWrite[0].length).setValues(rowsToWrite);
// now delete the row from the drives sheet so we dont have to process it again on the next loop
// this should always be row #2, because we've deleted the previous ROW 2 during the last loop iteration
var rowToDelete = 2;
drives_sheet.deleteRow(rowToDelete);
//console.log('DELETING DRIVE NAME: ' + this_drive_name);
//console.log('DELETING ROW: ' + rowToDelete);
};
var endTime = new Date().getTime();
var elapsed = (endTime-startTime)/1000;
console.log('Elapsed Seconds: ' + elapsed);
};
/*function send_email_simple_(recipient,subject,plain_text_body) {
MailApp.sendEmail(
recipient,
subject,
plain_text_body
);
}
*/

Apparently the problem is caused by this statement:
thisDrivePermissions.items.forEach(function(value) {
//console.log(value);
newRow = [this_audit_date, this_drive_id, this_drive_name, value.emailAddress, value.type, value.role];
// add to row array instead of append because appending each one at a time is SLOOOOOWWWWW
});
It's overwriting the newRow variable.
To avoid this, instead of doing assigning an Array, push it:
thisDrivePermissions.items.forEach(function(value) {
//console.log(value);
newRow.push([this_audit_date, this_drive_id, this_drive_name, value.emailAddress, value.type, value.role]);
// add to row array instead of append because appending each one at a time is SLOOOOOWWWWW
});
Also modify
} else {
console.log('permissions were empty');
newRow = [this_audit_date, this_drive_id, this_drive_name, 'ORPHAN', 'ORPHAN', 'ORPHAN'];
}
as
} else {
console.log('permissions were empty');
newRow = [[this_audit_date, this_drive_id, this_drive_name, 'ORPHAN', 'ORPHAN', 'ORPHAN']];
}
and
rowsToWrite.push(newRow)
as
rowsToWrite.push(...newRow)

Related

split the sheet into different workbooks by column values with dynamic range in apps script

I want to split the google sheet into different workbooks, not tabs in the same workbook based on values in column A. Although I have got a script that splits the data into different workbooks but the data range in it is not dynamic like the number of columns to be added into each workbook are fixed. I want them to be dynamic like till the last column of the data range. I have tried a lot to make it dynamic by adding loops but it shows The number of columns in the data does not match the number of columns in the range. The data has 1 but the range has 12. this error. The data in the log has almost no difference for the fixed range (which is working fine) and for the dynamic range that I have tried to it But don't know why it is showing error. Have got stuck into it. any help will be highly appreciated.
This the function that I am trying.
function splitSheets() {
var theWorkbook = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = theWorkbook.getSheetByName("Master");
var slc = theSheet.getDataRange().getLastColumn()
var slcv = theSheet.getRange("B1:B" + slc).getValues()
var sheets = theWorkbook.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Master":
break;
default:
theWorkbook.deleteSheet(sheets[i]);
}
}
var key = theSheet.getRange("A:A").getValues();
var rows = theSheet.getDataRange().getValues();
var headerFormat = theSheet.getRange("2:2").getValues();
var folderId = '16XVypjB5_PWe2PaBIREpDGCNQlZuWL4k'
var completedSheets = [];
for (var i = 2; i < key.length; i++) {
// if(completedSheets.includes('Blank') && key[i][0] === ""){
// }else{
if(!completedSheets.includes(key[i][0]) ) {
if (key[i][0] === "") {
var name = 'Blank'
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var insertedFile = Drive.Files.insert(resource)
var csid = insertedFile.id
var currentSheet = SpreadsheetApp.openById(csid).getSheetByName("Sheet1")
// var currentSheet = theWorkbook.insertSheet("Blank");
} else {
var name = key[i][0]
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var insertedFile = Drive.Files.insert(resource)
var csid = insertedFile.id
var currentSheet = SpreadsheetApp.openById(csid).getSheetByName("Sheet1")
// var currentSheet = theWorkbook.insertSheet(key[i][0]);
}
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++) {
var rown = []
for(var c = 0; c < slcv.length; c++){
// some other trials
// if((rows[j][0] == key[i][0]) || (rows[j][0] === '' && currentSheet.getName() == "Blank")){
// theNewRows[b]=[];
// theNewRows[b].push (
// rows[j][c].toString()
// This although adds the data and range dynamically but also shows the mentioned error.
rown.push(rows[j][c])
// );
// b++;
// }
}
if((rows[j][0] == key[i][0]) || (rows[j][0] === '' && currentSheet.getName() == "Blank")){
theNewRows[b]=[];
theNewRows[b].push (
rown.toLocaleString()
// These are the fixed column for data rnage
// rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7],rows[j][8],rows[j][9],rows[j][10],rows[j][11]
);
b++;
}
Logger.log(rown)
}
Logger.log(theNewRows)
// Logger.log(theNewRows)
currentSheet.getRange("1:1").setValues(headerFormat)
var outrng = currentSheet.getRange(2,1,theNewRows.length, slc);//Make the output range the same size as the output array
outrng.setValues(theNewRows);
currentSheet.autoResizeColumns(1, slc);
if(currentSheet.getSheetName() == 'Blank') {
completedSheets.push('Blank');
last = "Blank";
}else{
completedSheets.push(key[i][0])
last = key[i][0]
// }
}
}
}
SpreadsheetApp.setActiveSheet(theWorkbook.getSheetByName('Master'));
}
I overhauled and improved your script to be more readable and use a lot less Spreadsheet calls by using array methods instead.
Script:
function splitSheets() {
var folderId = '*** FOLDER ID ***';
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheets = spreadsheet.getSheets();
var sheet = spreadsheet.getSheetByName('Master');
// delete sheets that are not named 'Master'
sheets.forEach(sheetIter => {
if(sheetIter.getSheetName() != 'Master')
spreadsheet.deleteSheet(sheetIter);
});
var data = sheet.getDataRange().getValues();
// remove 1st row (blank row)
data.shift();
// remove 2nd row from data and assign as headers
var headers = data.shift();
// get unique list of sheet names from column A
var sheetNames = data.map(row => row[0]).filter(onlyUnique);
// loop those unique sheetNames
sheetNames.map(sheetName => {
// filter data by getting only rows with same column A and sheetName
var outputData = data.filter(row => row[0] == sheetName);
// add header from data filtered
outputData.unshift(headers);
var resource = {
title: sheetName || 'Blank',
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var file = Drive.Files.insert(resource);
var currentSheet = SpreadsheetApp.openById(file.id).getSheetByName('Sheet1');
// write data filtered with the header
currentSheet.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
// resize the columns
currentSheet.autoResizeColumns(1, outputData[0].length);
});
}
// function to get unique values from array using filter
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
Sample Output:

How to build conditional formattings with script?

I'm using the google script to manage the google admin. Below is the script to list all google users in the domain.
If I create conditional formatting directly on the sheet, the rules are deleted whenever the script is launched. That's why I'd like to include the conditional formatting within the script itself, but it's not working?.
Basically what I want are :
if the value of the columns 'is Super Admin?' or 'is Delegated Admin?' are TRUE, then I'd like the rows to be colored in bold red.
And I'm trying to find out how to convert the results of lastLoginTime and creationTime (which are in string ISO8601 to datevalue, I know how to do it using formula, but I don't know how to using the script)
I'd really appreciate it if anyone can guide me.
Many many thanks in advance.
function listAllUser() {
var sheet11 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetNAME");
var sheet11range = sheet11.getRange("I3:Q")
sheet11range.clear()
var data = [];// array to store values
data.push(['Last Name','First Name','Email','orgUnitPath', 'is Suspended?','is Super Admin?','is Delegated Admin?', 'LastLoginTime','Creation']);// store headers
var pageToken, page;
do {
page = AdminDirectory.Users.list({
domain: 'DomainNAME',
pageToken: pageToken,
});
var users = page.users;
if (users) {
for (var i = 0; i < users.length; i++) {
try{
var user = users[i];
data.push([user.name.familyName,user.name.givenName,user.primaryEmail,user.orgUnitPath, user.suspended,user.isAdmin,user.isDelegatedAdmin, user.lastLoginTime , user.creationTime ]);//store in an array of arrays (one for each row)}
}catch (e){}
}}
pageToken = page.nextPageToken;
} while (pageToken);
sheet11.getRange(2,9,data.length,data[0].length).setValues(data).setVerticalAlignment("middle").setWrap(true);
var t1 = sheet11.getRange("I1:P1").merge();
var t1bis = t1.setValue("List of all USERS");
var dated = sheet11.getRange("Q1")
dated.setValue("Last updated: " + Utilities.formatDate(new Date(),Session.getScriptTimeZone(),'dd-MM-yy hh:mm'));
var range = sheet11.getRange("I1:Q2");
range.setFontWeight("Bold").setFontColor("Blue").setHorizontalAlignment("center").setVerticalAlignment("middle").setBackground("#beedda").setWrap(true);
var isSuperAdm = sheet11.getRange("N3:N").getValue();
var isDelegatAdm = sheet11.getRange("O3:O").getValue();
if(isSuperAdm==true || isDelegatAdm==true){
range.setFontWeight('bold').setFontColor('red')
}
}
After looking around for some tutorials, I finally succeeded to have what I wanted, formating the cell :
when the lastLoginTime was before the specific date
when the value of 'is Super Admin?' and 'is Delegated Admin?' are true
function cellsFormatting(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetName");
var range1 = sheet.getRange("A1:Q2");
range1.setFontWeight("Bold").setFontColor("Blue").setHorizontalAlignment("center").setVerticalAlignment("middle").setBackground("#beedda").setWrap(true);
var range2 = sheet.getRange ("A3:Q");
range2.setVerticalAlignment('middle').setWrap(true);
}
function setRangeToRedBoldFont(cellRange){
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(cellRange);
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenTextEqualTo("TRUE")
.setFontColor("#FF0000")
.setBold(true)
.setRanges([range])
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
}
function setRangeToGrayItalicFont(cellRange){
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(cellRange);
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied("=DATEVALUE(MID($P3;1;10)) + TIMEVALUE(MID($P3;12;8))<date(2018;8;31)")
.setFontColor("#A9A9A9")
.setItalic(true)
.setRanges([range])
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
}
function listAllUser() {
var sheet11 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetName");
var sheet11range = sheet11.getRange("I3:Q")
sheet11range.clear();
cellsFormatting();
var t1 = sheet11.getRange("I1:P1").merge();
var t1bis = t1.setValue("List of all USERS");
var dated = sheet11.getRange("Q1")
dated.setValue("Last updated: " + Utilities.formatDate(new Date(),Session.getScriptTimeZone(),'dd-MM-yy hh:mm')); //will input the date & time when the script was launched
var data = [];// array to store values
data.push(['Last Name','First Name','Email','orgUnitPath', 'is Suspended?','is Super Admin?','is Delegated Admin?', 'LastLoginTime','Creation']);// store headers
var pageToken, page;
do {
page = AdminDirectory.Users.list({
domain: 'DomainNAME',
pageToken: pageToken,
});
var users = page.users;
if (users) {
for (var i = 0; i < users.length; i++) {
try{
var user = users[i];
data.push([user.name.familyName,user.name.givenName,user.primaryEmail,user.orgUnitPath, user.suspended,user.isAdmin,user.isDelegatedAdmin, user.lastLoginTime , user.creationTime ]);//store in an array of arrays (one for each row)}
}catch (e){}
}}
pageToken = page.nextPageToken;
} while (pageToken);
sheet11.getRange(2,9,data.length,data[0].length).setValues(data);
var formatRange = "I3:Q";
setRangeToRedBoldFont(formatRange);
setRangeToGrayItalicFont (formatRange);
}
For the row color:
For user.isAdmin & user.isDelegatedAdmin
function colorLine() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('SheetName');
const rg=sh.getRange(3,9,sh.getLastRow(),9);
const vs=rg.getValues();
vs.forEach((r,i)=>{
if(r[5]||r[6]) {
sh.getRange(i+3,1,1,sh.getLastColumn()).setFontWeight('bold').setFontColor('red');
}
});
}

Updated File List

I am using a script to show me new files in google drive so people can see that a file has been updated. The script worked fine with one or two users using the system but after migrating a few more we started getting errors about exceeding maximum execution time. Currently the script deletes the current list and recreates it each and every hour
function listFilesInFolder() {
//try {
// If you want a tree of any sub folder
//var parent = DriveApp.getFoldersByName("FOLDER_NAME").next();
// If you want to search from the top (root) folder
var files = DriveApp.getFiles();
var ss = SpreadsheetApp.openById("1XbAaoNTiRXKSUAovY-QUvnc8V2q68g7Q3z4NIH_ioTc");
var sheet = ss.getSheetByName("Sheet1");
var file,folder,folders,folderTop,newFiles,lastUpdated,difference;
var j = 0;
var data = []
var parentFolders = []
var newDate = new Date();
while (files.hasNext()) {
file = files.next();
lastUpdated = file.getLastUpdated();
difference = newDate.getTime() - lastUpdated.getTime();
if ((difference/86400000) < 100) {
folders = file.getParents();
while (folders.hasNext()) {
folder = folders.next();
folders = folder.getParents();
parentFolders.push(folder.getName())
}
folderTop = parentFolders[parentFolders.length -2]
data.push (new Array());
data[j].push(file.getName());
data[j].push(file.getLastUpdated());
data[j].push("open_in_new");
data[j].push("https://drive.google.com/file/d/"+file.getId()+"/edit");
data[j].push("folder_open");
if (file.getParents().hasNext())
{
data[j].push("https://drive.google.com/drive/u/0/folders/" + file.getParents().next().getId());
}
else
{
data[j].push("https://drive.google.com/drive/");
}
data[j].push(folderTop+"#xxx.net");
j = j+1;
}
}
sheet.deleteRows(2, sheet.getMaxRows()-1)
sheet.deleteColumns(2, sheet.getMaxColumns()-1)
sheet.clear()
sheet.appendRow(["File Name","Date Updated","View File","File Link", "Open Folder","Folder Link", "Cell" ]);
sheet.appendRow(["","","HyperlinkType(D)-iconType","", "HyperlinkType(F)-iconType","", "Permissions-Hidden"] );
var destinationRange = sheet.getRange(3, 1,j , 7);
destinationRange.setValues(data);
}
// catch (e) {
// Logger.log(e.toString());
// }
// }
function getFilesFromFolder(parent,targetSheet,topLevelName) {
var files = parent.getFiles();
var data
var file
while (files.hasNext()) {
file =files.next()
data = [
file.getName(),
file.getDateCreated(),
file.getSize(),
file.getUrl(),
file.getDescription(),
file.getLastUpdated(),
file.getMimeType(),
topLevelName+"#xxx.net"
];
targetSheet.appendRow(data);
row.push([file.getId(),
file.getName(),
file.getDateCreated(),
file.getSize(),
file.getUrl(),
file.getDescription(),
file.getLastUpdated(),
file.getMimeType()
]);
}
}
function getChildFolders(parent,targetSheet,topLevelName) {
var childFolders = parent.getFolders();
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
getFilesFromFolder(childFolder,targetSheet,topLevelName);
getChildFolders(childFolder,targetSheet,topLevelName);
}
// Recursive call for any sub-folders
}
Thank you.
Here's a script that uses DriveActivity API to monitor changes to Google Drive. The Qwikstart is here and I took that script and modified it.
I hardwired the timezone into the query filter so you'll need to address that and there's also the variable n and nl that I used to limit the number of calls so that I would hit quota limits during development.
It generates the html for the results and displays everything in a dialog.
Code.gs:
/**
* Lists activity for a Drive user.
*/
function listDriveActivity() {
const dt=new Date();
const dt0=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1);
const dt1=Utilities.formatDate(dt0, Session.getScriptTimeZone(),"yyyy-MM-dd");
const dt2=Utilities.formatDate(dt0, Session.getScriptTimeZone(), "HH:mm:ss")
const dt3=dt1+"T"+dt2+"-07:00";
const dt4=Utilities.formatDate(new Date(dt3), Session.getScriptTimeZone(), "E MM/dd/yyyy HH:mm:ss")
var html='Recent Activities ' + dt4 + ' ' + Session.getScriptTimeZone();
html+='<style>td,th{padding:2px;border:1px solid black;overflow-wrap: break-word;\} table{table-layout:fixed;width:100%;}</style>';
html+='<table>';
html+='<tbody>';
html+='<tr><th>Time</th><th>Actors</th><th>Actions</th><th>Targets</th></tr>';
var token="";
var n=0;
var nl=10;//I needed to reduce the number of calls to not exceed quota during development
do{
var request = {pageSize: 10,filter:Utilities.formatString('time>=\"%s\"',dt3)};
var response = DriveActivity.Activity.query(request);
var activities = response.activities;
if (activities && activities.length > 0) {
for (var i = 0; i < activities.length; i++) {
var activity = activities[i];
var time = getTimeInfo(activity);
var actions = getActionInfo(activity.actions);
var actors = activity.actors.map(getActorInfo);
var targets = getTargetInfo(activity.targets);
//html+=Utilities.formatString('<tr><td style="width:15%;">%s</td><td style="width:15%;">%s</td><td style="width:25%;">%s</td><td style="width:35%;">%s</td></tr>',time,truncated(actors),actions,targets);
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',time,actors,actions,targets);
Logger.log(JSON.stringify(activities[i]));
if(++n > nl)break;;
}
} else {
html+='<br />No Further Activity';
}
token=response.nextPageToken;
}while(token!='' && n<=nl);
html+='</tbody></table><br />n=' + n;
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html).setWidth(1200), "Drive Activity")
}
/** Returns a string representation of the first elements in a list. */
function truncated(array, opt_limit) {
var limit = opt_limit || 2;
var contents = array.slice(0, limit).join(', ');
var more = array.length > limit ? ', ...' : '';
return '[' + contents + more + ']';
}
/** Returns the name of a set property in an object, or else "unknown". */
function getOneOf(object) {
for (var key in object) {
return key;
}
return 'unknown';
}
function getTimeInfo(activity) {
if ('timestamp' in activity) {
const dt=new Date(activity.timestamp);
return Utilities.formatDate(dt, Session.getScriptTimeZone(), "E MM/dd/yyyy HH:mm:ss") + '<br />' + activity.timestamp;
}
return 'unknown';
}
function getActionInfo(acts) {
let html='';
acts.forEach(function(d,i){
if(i>0)html+='<br /><br />';
html+=JSON.stringify(d)
});
return html;
}
/** Returns user information, or the type of user if not a known user. */
function getUserInfo(user) {
if ('knownUser' in user) {
var knownUser = user.knownUser;
var isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
/** Returns actor information, or the type of actor if not a user. */
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user)
}
return getOneOf(actor);
}
/** Returns the type of a target and an associated title. */
function getTargetInfo(targets) {
let html='';
targets.forEach(function(t,i){
if(i>0)html+='<br /><br />';
//html+=JSON.stringify(t);
html+='target: ' + i;
html+=Utilities.formatString('<br />title: %s, type: %s, personName: %s, domainName: %s, id: %s',t.driveItem.title,t.driveItem.mimeType,getUserInfo(t.driveItem.owner.user),t.driveItem.owner.domain.name,t.driveItem.name.slice(6));
});
return html;
}

How to optimize fileiterator (to avoid timeout) in Google Scripts

I am hitting the Google Scripts 6-minute timeout. The script is running through quite a few folders and subfolders. I am hoping that there is either (or both):
A way to optimize the script to more efficiently/intelligently run through the dataset/iterators?
A way to use a getContinuationToken()
Any help would be much appreciated!
I have tried to limit the number of folders/subfolders the script runs through. Even on a 'minimal' run, it times out. And FYI, my goal is to:
make a copy of files that are NOT owned by 'me'...
change the name of the original file (that was not owned by me) to be 'delete'
Here is the code:
FOLDERS = ["0B4FiuEqe8ftGb2lSbjJzakJDNWs"]; /*, "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "1D_1u_KwcLOsBuKyQ7NCsKDd8DB5JwOn6"];*/
function copyNonOwnedFiles() {
var arr = FOLDERS
for (var j = 0; j < arr.length; j++) {
var folderTOP = DriveApp.getFolderById(arr[j]);
var folders = folderTOP.getFolders();
var me = Session.getActiveUser().getEmail();
//RUN #1 = this runs through all of the subfolders
//iterator 1
while (folders.hasNext()) {
var folder = folders.next();
//get all files within these folders
var files = folder.getFiles();
//iterator 2
while (files.hasNext()) {
var file = files.next();
var Owner = file.getOwner().getEmail();
if( Owner !== me ){
var name = file.getName();
file.makeCopy(name);
file.setName('delete');
}
}
}
//RUN #2 = this runs through the parent folder
var files2 = folderTOP.getFiles();
//iterator 2
while (files2.hasNext()) {
var file2 = files2.next();
var Owner = file2.getOwner().getEmail();
if( Owner !== me ){
var name2 = file2.getName();
file2.makeCopy(name2);
file2.setName('delete');
}
}
}
}
Try this:
This script will find the files owned by the active user, in the folders with the listed ids and display them on a modeless dialog. This script will probably need to utilize lock service for multiple simultaneous users.
var myFiles=[];
var ids=["0B4FiuEqe8ftGb2lSbjJzakJDNWs", "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "1D_1u_KwcLOsBuKyQ7NCsKDd8DB5JwOn6"];
var me='';
function getMyFiles() {
myFiles=[];
me=Session.getActiveUser().getEmail();
for(var i=0;i<ids.length;i++) {
getFnF(DriveApp.getFolderById(ids[i]));
}
var html="<style>th,td{border:1px solid black;}</style><table><tr><th>Item</th><th>Name</th><th>Url</th></tr>";
for(var i=0;i<myFiles.length;i++) {
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>Link</td></tr>',i+1,myFiles[i].name,myFiles[i].url);
}
html+='</table><input type="button" value="Close" onClick="google.script.host.close();" />';
var userInterface=HtmlService.createHtmlOutput(html);
var title=Utilities.formatString('Files Owned by: %s',me);
SpreadsheetApp.getUi().showModelessDialog(userInterface, title)
}
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
var files=folder.getFiles();
while(files.hasNext()) {
var file=files.next();
if(file.getOwner().getEmail()==me) {
myFiles.push({name:file.getName(),url:file.getUrl()});
}
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
getFnF(subfolder);
}
}
for anyone who has this same question or a similar one... I found that it was much faster/easier/less resource intensive to use a search than a file iterator
dataSheetName = "List";
function onOpen(){
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('List Files', 'ListFiles')
.addSeparator()
.addToUi();
}
function ListFiles() {
var sheet = SpreadsheetApp.getActive();
var dataSheet = sheet.getSheetByName(dataSheetName);
// Clear Sheet
var lastRow = dataSheet.getLastRow();
if (lastRow > 0) {
dataSheet.getRange(2, 1, lastRow, dataSheet.getLastColumn()).clearContent();
}
dataSheet.getRange("E2").setValue("Process Started at: " + new Date());
Logger.log("Start: " + new Date());
// var row = ['File Name', 'File Id'];
var row = ['File Name', 'File Id'];
var data = [];
data.push(row);
// Find files modified in the last 24 hours
var totalDays = 30;
var today = new Date();
var oneDayAgo = new Date(today.getTime() - totalDays * 24 * 60 * 60 * 1000);
var startTime = oneDayAgo.toISOString();
// The magic search expression
var search = '(trashed = false) and (modifiedDate > "' + startTime + '") and not (title contains \'DELETE\') and not (\'me\' in owners) ';
// var search = "(mimeType='image/jpeg') and not (title contains 'DELETE') ";
// var search = " not ('me' in owners) and not (title contains 'DELETE') ";
// var search = " not ('me' in owners) ";
//var search = 'sharedWithMe';
// 'me' in owners
var i = 1;
var files = DriveApp.searchFiles(search);
Logger.log("2: " + new Date());
dataSheet.getRange("E3").setValue("Search Completed at: " + new Date());
// while (files.hasNext() && (i < 10000) {
while (files.hasNext() ) {
var file = files.next();
try {
// data.push([file.getName(),file.getId()]);
data.push([file.getName(), file.getId()]);
}
catch (e) {
Logger.log(file.getName());
}
// i += i;
}
// Write data
var dataRange = dataSheet.getRange(1, 1, data.length, row.length);
dataRange.setValues(data);
Logger.log("3: " + new Date());
dataSheet.getRange("E4").setValue("List Completed at: " + new Date());
}

How to check particular value in google spreadsheet

I picked this code online and I am trying to check if the value in the column B is 'Done' then the value will be be copied otherwise not. Here is the code I am using:
copy sheet function below will copy the datat from source sheet to destination sheet but what I want that it will only pick the row if the col B value contains Done
function copySheet() {
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Source");
var destSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Destination");
var columns_to_be_copied =['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U'];
var columns_to_be_pasted =['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U'];
for (column in columns_to_be_copied) {
var copy_range_string = columns_to_be_copied[column] + ':' + columns_to_be_copied[column];
var paste_range_string = columns_to_be_pasted[column] + ':' + columns_to_be_pasted[column];
var source = sourceSheet.getRange(copy_range_string);
var destination = destSheet.getRange(paste_range_string);
if(findInColumn('A','Done') !== -1) {
copyTo(source,destination );
}
}
}
function copyTo(source,destination) {
var sourceSheet = source.getSheet();
var destSheet = destination.getSheet();
var sourceData = source.getValues();
var dest = destSheet.getRange(
destination.getRow(), // Top row of destination
destination.getColumn(), // left col of destination
sourceData.length, // # rows in source
sourceData[0].length); // # cols in source (elements in first row)
dest.setValues(sourceData);
SpreadsheetApp.flush();
}
function findInColumn(column, data) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sprint");
var column = sheet.getRange(column + ":" + column); // like A:A
var values = column.getValues();
var row = 0;
while (values[row] && values[row][0] !== data) {
row++;
}
if (values[row][0] === data)
return row+1;
else
return -1;
}
As I am a fan of simple and easy to read (even after long time) solutions I would suggest the following script:
function main() {
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Source');
var destinationSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Destination');
copyFromTo(sourceSheet, destinationSheet);
}
function copyFromTo(sourceSheet, destinationSheet) {
const ColumnB = 1; //Array indexing starts from 0
const FilterValue = 'Done';
var sourceValues = sourceSheet.getSheetValues(1, 1, 100, 28); //startRow, startColumn, numRows, numColumns
var filteredValues = sourceValues.filter(function(row) {
return row[ColumnB] === FilterValue;
});
destinationSheet.getRange(1, 1, filteredValues.length, filteredValues[0].length).setValues(filteredValues);
}
It's about the same function. I just modified it to facilitate my debugging process. It copies the columns from source to destination if Sprint has "Done" in that column.
function copySheet() {
var srcsh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Source");
var dessh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Destination");
var from = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U'];
var to = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U'];
for(var i=0;i<from.length;i++) {
var fromrg = from[i] + ':' + from[i];
var torg = to[i] + ':' + to[i];
var src = srcsh.getRange(fromrg);
var des = dessh.getRange(torg);
if(findInColumn(from[i],'Done')!== -1){
src.copyTo(des);
}
}
}
function findInColumn(col, data) {
var col=col || 'A';//This is here for initial testing so I could run the function without parameters.
var data=data || 'Done';
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sprint");
var rg = sh.getRange(col + "1:" + col + sh.getMaxRows());//MaxRows is kind of big but I was not sure what else you'd like to do and I dont know what your Sprint sheet looks like.
var vA = rg.getValues();
var rv=-1;
for(var i=0;i<vA.length;i++){
if(vA[i][0]==data){
rv=i+1;
break;
}
}
return rv;
}
I see that you changed the question a bit. This function looks in your sprint sheet as you show in your answer and copies from source to destination only those columns that have the word "Done" on any row of that column. But it checks every column in your "columns_to_be_copied" which I called "from". Originally, that's what your function was trying to do. So I just wanted to be clear what this function is doing. If it's not what you want then leave a comment and I'll delete it.