GAS - Change Folder Permissions - javascript

I've been working on a script that lists the name and folder ids of the all the folders in a specific folder in my drive. Once the list is created, I run the function to get the id and change the folder permissions to private (only specific people can view), remove editors who do not have the company domain and switches them to viewers, and then it should change the permissions for any files in the folder as well. The initial step of creating the ids works fine. Unfortunately, the updatePermissions() function only seems to infinitely loop through the first folder and I'm not sure what next steps to take to ensure that the script pulls the next folder id from the list in the spreadsheet. Any help would be greatly appreciated.
function listSchoolFolders(){
var folderId = 'enter folder id here';
var myspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var parentFolder = DriveApp.getFolderById(folderId);
var childFolders = parentFolder.getFolders();
// List folders inside the folder
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
var data = [
childFolder.getName(),
childFolder.getId()
];
//Write
myspreadsheet.appendRow(data)
}
}
//------------------------- New Function -------------------------//
function updatePermissions() {
var myspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var schoolRows = myspreadsheet.getDataRange();
var schoolNumRows = schoolRows.getNumRows();
var schoolValues = schoolRows.getValues();
//Loop through List of Schools
var row_num = schoolValues[0][2];
while(row_num<schoolNumRows){
//Retrieve folder id and go to folder
var folderId = schoolValues[row_num][1];
var folderName = schoolValues[row_num][0];
Logger.log(folderName);
try {
var schoolFolder = DriveApp.getFolderById(folderId);
}
catch (err){
Logger.log(folderName + ": " + err.message);
row_num = row_num+1;
myspreadsheet.getRange(1,3).setValue(row_num);
continue;
};
};
//Loop through folders and set permissions
var childFolders = DriveApp.getFolderById(folderId).getFolders();
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
var childFolderPermissions = childFolder.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
var files = DriveApp.getFolderById(folderId).getFiles();
while (files.hasNext()) {
Logger.log(files.next().getName());
var fileFolderPermissions = files.next().setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
//check for rogue editors
var viewEditors = schoolFolder.getEditors();
for (i in viewEditors) {
var email = viewEditors[i].getEmail();
var emailSource = email.split("#")[1]
if (emailSource != "tester.com") {
// add as a viewer or remove completely?
addViewer(email)
};
};
};
};
// Recursive call for any sub-folders
getChildFolders(childFolder);

The error was in the logic checking for child folders. If there was no child folder, the script completed. I've added two conditional checks on each top-level folder, one for child folders and one for files. This script functions with one-level depth.
function updatePermissions() {
// define several variables to use throughout the script
var editors, domain, email;
var myspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var schoolValues = myspreadsheet.getDataRange().getValues();
// Loop through List of folders
// Skip row 1
for(var i=1; i<schoolValues.length; i++) {
//Retrieve folder id and go to folder
var folderId = schoolValues[i][1];
var folderName = schoolValues[i][0];
Logger.log(folderName);
var schoolFolder = DriveApp.getFolderById(folderId);
// Get the children
var childFolders = schoolFolder.getFolders();
// test for child folders.
if(!childFolders.hasNext()) {
// There is no child folder, so test for files
if(!schoolFolder.getFiles().hasNext()) {
// There are no files, so get the folder editors and loop
editors = schoolFolder.getEditors();
for(var j=0; j<editors.length; j++) {
email = editors[j].getEmail();
domain = editors[j].getDomain(); // easier than using a split function
// Check the domain. Remove if no match, add as viewer if there is a match
if(domain !== "testdomain.com") {
schoolFolder.removeEditor(email)
} else {
schoolFolder.removeEditor(email).addViewer(email);
}
}
}
// There are child folders, loop through and change permissions
} else {
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
// Set permissions on the folder
childFolder.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
// Get the files in the child folder and loop
var files = childFolder.getFiles();
while (files.hasNext()) {
files.next().setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
var viewEditors = schoolFolder.getEditors();
// Loop the array of editors
for (var j=0; j<viewEditors.length; j++) {
email = viewEditors[j].getEmail();
domain = viewEditors[j].getDomain();
if (domain !== "testdomain.com") {
// add as a viewer or remove completely?
Logger.log("add " + email + " as a viewer");
files.next().addViewer(email);
} else {
// Remove the editor
Logger.log("removed " + email + " from folder");
files.next().removeEditor(email);
}
};
}
}
};
}
// Recursive call for any sub-folders
//getChildFolders(childFolder);
};

Related

Script does not restart after it fails once it does not start up again. I can not find the reason

Need help on simplifying this script. I still need to add more clusters in while loops.
function Files() {
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("AME");
var SupplierName = names.getRange(names.getLastRow(),5).getValue();
var Cluster = names.getRange(names.getLastRow(),3).getValue();
if (Cluster == 'US'){
var ClusterID = DriveApp.getFolderById("1z2R");
var newFolderID = ClusterID.createFolder(SupplierName);
var sourceFolder = DriveApp.getFoldersByName("US").next();
var files = sourceFolder.getFiles();
var destFolder = DriveApp.getFoldersByName(SupplierName).next();
while(files.hasNext()){
var file = files.next();
file.moveTo(destFolder);
}
return newFolderID.getId();
SUGGESTION:
We are still kind of lurking in the dark here but here are some opportunities I have identified on the provided script above.
/** Can be a global variable */
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("AME");
var SupplierName = names.getRange(names.getLastRow(),5).getValue();
var Cluster = names.getRange(names.getLastRow(),3).getValue();
function Files() {
if(Cluster == 'US') {
var ClusterID = DriveApp.getFolderById("SET FOLDER ID");
var folders = ClusterID.getFoldersByName(SupplierName).hasNext();
if(folders !== true){
ClusterID.createFolder(SupplierName);
moveFolders(SupplierName);
}
else {
moveFolders(SupplierName);
}
}
else {
Logger.log('Cluster is ' + Cluster);
}
}
function moveFolders() {
var sourceFolder = DriveApp.getFoldersByName("US").next();
var files = sourceFolder.getFiles();
Logger.log(files);
var destFolder = DriveApp.getFoldersByName(SupplierName).next();
while(files.hasNext()){
var file = files.next();
file.moveTo(destFolder);
}
}
Created a separate function moveFolders() to move files to the created folder ClusterID.createFolder(SupplierName); if Cluster 'US' was checked on the last row of the sheet. I assumed that your data is from a response that came from a Google Form that is why the last row is only being checked.
Sample Data:
Source parent folder for Cluster Folder and US Folder:
Files inside the "US" Folder:
After the script is run, created the SupplierName folder containing the files moved from the US Folder:
Now I haven't worked on dynamically adjusting the script to manually define the cluster name as of the moment due to lack of clarity of the question, but I'll modify my answer once questions are addressed.

Trying to get folder path through file id using appscript

I am pulling a drive activity report using GCP/GAMadv which gives me file IDs of various users in our workspace domain on a google sheet. My goal is to find the folder path of these files using file IDs. I am using appscript to get to that. Here is the code that I am running so far.
function getFolderPath(fileID, folderPath =""){
var sheetID = "1YfZgkLvAnPj7kOIQOVkcXeJgnh-KTecMn6er1a0elkk"
var sheet = SpreadsheetApp.openById(sheetID)
// var file = sheet.Files.get(fileID)
var parent = Drive.Files.get(fileID);
console.log(parent)
// console.log(file)
var parentElement = parent.items[0]
console.log(parentElement)
// var parentElement = parent[0]
var parentFile = Drive.Files.get(parent.id);
var parentPath = parentFile.title;
if (parent.isRoot)
return "/" + folderPath;
else {
return getFolderPath(
parentFile.id,
parentPath + "/" + folderPath
);
}
}
Looks like this is returning ALL the files we have in our drive rather than the ones on the sheet.
Help would be greatly appreciated! Thanks.
Folder Path from Id
function getFolderPathFromId(id="fileid") {
try {
var file = DriveApp.getFileById(id)
var pA = [];
pA.push(file.getName());
var folder = file.getParents();
while (folder.hasNext()) {
var f = folder.next();
pA.push(f.getName());
folder = f.getParents()
}
var r = pA.reverse().slice(0,-1).join(' / ');
}
catch (e) {
return e;
}
Logger.log(r);
return r;
}

How to change name new creating folder with the subfolders

i want creating new folder with new name from cell in the sheet, and i want copy to this folder files and folders with the sourcedestiny.
Could you help me to solve my problem.
It is possible change name newFolder witout change name folders and subfolders- after his creating?
function copyFolderass() {
const sourceFolder = DriveApp.getFolderById('');
const destinyFolder = DriveApp.getFolderById('');
copyFolder(sourceFolder, destinyFolder);
}
function copyFolderContents(source, target) {
const filesIterator = source.getFiles();
while (filesIterator.hasNext()) {
const file = filesIterator.next();
file.makeCopy(file.getName(), target);
}
}
function copyFolder(sourceFolder, destinyFolder) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("nameFile");
var name = ss.getRange("F4").getValue();
const newFolder = destinyFolder.createFolder(sourceFolder.getName());
copyFolderContents(sourceFolder, newFolder);
const foldersIterator = sourceFolder.getFolders();
while (foldersIterator.hasNext()) {
const folder = foldersIterator.next();
copyFolder(folder, newFolder);
}
}
Create Folders and Copy Files
Reads a sheet to obtain FilesFolder Id FolderFolder Id and new FolderName
It creates the new folder and copies all of the files from the Files Folder to the newly created folder.
It records the new folder id in FolderId Column.
It records all of the file ids in the FileIds column.
It put the date in the created column.
It doesn't do anything for rows that have content in Created
//FilesFolder FolderFolder FolderName FolderId FileIds Created
function createFolderAndCopyFiles() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const shsr = 2;
let [hA, ...vs] = sh.getDataRange().getValues();
let idx = {};
let col = {};
hA.forEach((h, i) => { idx[h] = i; col[h] = i + 1; });
vs.forEach((r, i) => {
if (!r[idx["Created"]]) {
let dfldr = DriveApp.getFolderById(r[idx["FolderFolder"]]);
let fldr = dfldr.createFolder(r[idx["FolderName"]]);
let fldrid = fldr.getId();
sh.getRange(i + shsr, col["FolderId"]).setValue(fldrid);
let files = DriveApp.getFolderById(r[idx["FilesFolder"]]).getFiles();
let ids = [];
while (files.hasNext()) {
let file = files.next();
let fid = file.getId();
file.makeCopy(file.getName(), fldr);
ids.push(fid);
}
sh.getRange(i + shsr, col["FileIds"]).setValue(ids.join('\n'));
sh.getRange(i + shsr, col["Created"]).setValue(new Date());
}
});
}
Spreadsheet
You fill in the stuff in yellow the program fills in the rest and it won't make folders again if the Created Column not empty.
Thx Cooper for the answer,
but this is not that what i want to do.
on google drive i have a template of structure od catalogs.
my code is one of the part code of my main code. main code working like this
i click send email and then run my code with creating folder from temtlace
when is creating a have name of the template folder but i want just change one part of this line of my code.
instead of this line
const newFolder = destinyFolder.createFolder(sourceFolder.getName());
i want to just like this - the name of the creating folder is from cell F4.
the remaining names of the folders may stays like from template.
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("nameFile");
var name = ss.getRange("F4").getValue();
const newFolder = destinyFolder.createFolder(sourceFolder.setName(name));
but this line change ale name in subfolders i all structure catalog when is creating.

How to copy a folder from Google Drive to create a new folder in Google drive using JS

I want to copy folders into a Master Folder within Google Drive. My main issue is creating a function when one column is not blank and another column is blank.
Once, a new folder is created it has to paste in the folder URL in one of the columns.
This is what I have so far:
function addData(){
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
var lr = activeSheet.getLastRow();
var lc = activeSheet.getLastColumn();
var sourceData = sourceSheet.getRange(1, 1, lr, lc).getValues();
for (row in sourceData) {
if (sourceData[row][0] !== "" && sourceData[row][19] !== "") {
var rowNum = parseInt(row);
//code will go here for Folder copying and renaming and getting the new folder url
activeSheet.getRange(rowNum+1,lc).setValue("Completed"); //we will eventually change Completed with the new Folder URL
}
}
}
The main idea would be to iterate files using Folder.getFiles() and folders with Folder.getFolders(). Make new folders with Folder.createFolder(name) and copy files using File.makeCopy(name, destination):
/**
* Copies all the files in a folder to another one.
*/
function copyFolderContents_(source, target) {
// Iterate files in source folder
const filesIterator = source.getFiles()
while (filesIterator.hasNext()) {
const file = filesIterator.next()
// Make a copy of the file keeping the same name
file.makeCopy(file.getName(), target)
}
}
/**
* Recursivelly copies a folder and all its subfolders with all the files
*/
function copyFolder_(toCopy, copyInto) {
// Makes the new folder (with the same name) into the `copyInto`
const newFolder = copyInto.createFolder(toCopy.getName())
// Copy the contents
copyFolderContents_(toCopy, newFolder)
// Iterate any subfolder
const foldersIterator = toCopy.getFolders()
while (foldersIterator.hasNext()) {
const folder = foldersIterator.next()
// Copy the folder and it's contents (recursive call)
copyFolder_(folder, newFolder)
}
}
/**
* Entry point to execute with the Google Apps Script UI
*/
function copyFolder() {
// Get the folders (by ID in this case)
const toCopy = DriveApp.getFolderById('')
const copyInto = DriveApp.getFolderById('')
// Call the function that copies the folder
copyFolder_(toCopy, copyInto)
}
You need to add the ids of the folder to be copied and the folder to copy it from on copyFolder().
foldersIterator and filesIterator are Google Apps Script Iterators. They have a method hasNext() that returns if there is another item, and a next() that retrieves it.
Note that copyFolder_(toCopy, copyInto) uses itself to copy its subfolders.
References
Folder.getFiles() (Google Apps Script reference)
Folder.getFolders() (Google Apps Script reference)
Folder.createFolder(name) (Google Apps Script reference)
File.makeCopy(name, destination) (Google Apps Script reference)
Iterators (Google Ads Script concepts) (same implementation as Google Apps Script)
This solution is a variation that checks to see if the file or folder has already been copied.
Useful if you have to restart the execution.
It uses includes to check if a file/folder has been copied:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
It goes only one level deep, so won't copy subfolders of subfolders.
I've added a sort using Intl.Collator because it helps me read the console output. Neither sort nor console are needed but may be useful.
references:
https://stackoverflow.com/a/54427214/16465606
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator
https://tc39.es/ecma402/#sec-collator-comparestrings
/** Use with copyFolderContents */
function copyFolder() {
const topFolderFrom = DriveApp.getFolderById(''); // enter folder ID
const topFolderTo = DriveApp.getFolderById(''); // enter folder ID
// Get the subfolder names and IDs
const ffTopFolder = topFolderFrom.getFolders();
const subfolders = [];
while (ffTopFolder.hasNext()) {
let folder = ffTopFolder.next();
let ffnameid = [];
ffnameid.push(folder.getName(), folder.getId());
subfolders.push(ffnameid);
}
subfolders.sort((a, b) => collator.compare(a[0], b[0]));
// Get target subfolder names
const folderstarget = topFolderTo.getFolders();
const listtarget = [];
while (folderstarget.hasNext()) {
let foldertarget = folderstarget.next();
let ffname = [];
ffname.push(foldertarget.getName());
listtarget.push(ffname);
}
listtarget.sort(collator.compare);
// Check if subfolder has already been created
const alreadycreated = listtarget.flat();
const stilltocreate = subfolders.filter(e => !alreadycreated.includes(e[0]));
console.log('subfolders already created:\n', alreadycreated);
console.log('subfolders still to create:\n', stilltocreate.map(e => e[0]));
// Create subfolders
for (let i = 0, len = stilltocreate.length; i < len; i++) {
topFolderTo.createFolder(stilltocreate[i][0]);
}
// Get the name and ID of subfolders in target folder (needed in case subfolders have been created)
const ffTopFolderTo = topFolderTo.getFolders();
const subfoldersTo = [];
// Get the subfolders
while (ffTopFolderTo.hasNext()) {
let folder = ffTopFolderTo.next();
let ffnameid = [];
ffnameid.push(folder.getName(), folder.getId());
subfoldersTo.push(ffnameid);
}
subfoldersTo.sort((a, b) => collator.compare(a[0], b[0]));
// Add the top level folders to arrays to copy files in top level
subfolders.push([topFolderFrom.getName(), topFolderFrom.getId()]);
subfoldersTo.push([topFolderTo.getName(), topFolderTo.getId()]);
console.log('subfolders and top folder:\n', subfolders);
console.log('subfolders and top folder (target):\n', subfoldersTo);
for (let i = 0, len = subfolders.length; i < len; i++) {
let copyFrom = DriveApp.getFolderById(subfolders[i][1]);
let indx = subfoldersTo.map(e => e[0]).indexOf(subfolders[i][0]);
let copyTo = DriveApp.getFolderById(subfoldersTo[indx][1]);
// Call the function that copies the folder
copyFolderContents_(copyFrom, copyTo);
}
}
/** Copies all the files in a folder to another one, checking for duplicates */
function copyFolderContents_(source, target) {
Logger.log('copy from: ' + source);
Logger.log('copy to: ' + target);
// Get source filenames and IDs
const files = source.getFiles();
const listsource = [];
while (files.hasNext()) {
let file = files.next();
let ssnameid = [];
ssnameid.push(file.getName(), file.getId());
listsource.push(ssnameid);
}
listsource.sort((a, b) => collator.compare(a[0], b[0]));
// Get target filenames
const filestarget = target.getFiles();
const listtarget = [];
while (filestarget.hasNext()) {
let filetarget = filestarget.next();
let ssname = [];
ssname.push(filetarget.getName());
listtarget.push(ssname);
}
listtarget.sort(collator.compare);
// Check if file has already been copied
const alreadycopied = listtarget.flat();
const stilltocopy = listsource.filter(e => !alreadycopied.includes(e[0]));
console.log('files already copied:\n', alreadycopied);
console.log('files still to copy:\n', stilltocopy.map(e => e[0]));
// Copy files still to copy
for (let i = 0, len = stilltocopy.length; i < len; i++) {
let fileid = stilltocopy[i][1];
let ss = DriveApp.getFileById(fileid);
Logger.log(i + ' - ' + ss);
ss.makeCopy(stilltocopy[i][0], target);
}
}
/** Used for sorting alpha/numeric arrays */
const collator = new Intl.Collator('en', {
numeric: true,
sensitivity: 'base'
})

Execution Timeout for Google Apps Script

I'm currently writing a script that copies a folder, and all of its sub-folders and files of a very large directory from one location in Google Drive to another. This is being used for archiving purposes for the company which I am employed by.
My issue is that the size and quantity of the file that I am trying to archive is WAY too large to handle in the 5 minute execution time given by Google. I am trying to keep this script as a standalone web app, however I am happy to extend on it as needed.
My second issue is that I need the script to run over and over again until the folders have been copied but once it is finished I need it to stop. I was originally going to use triggers, however time-based triggers are inappropriate for the task I am trying to fulfil.
In short, I need my script to run until the task is completed, automatically restarting itself, and avoiding execution time errors.
The full code is included below.
//Global Variables
var classroomFolderName = "Classroom";
var archiveFolderName = "Archive";
function doGet(){
var classroomFolder = DriveApp.getFoldersByName(classroomFolderName).next();
var archiveFolder = DriveApp.getFoldersByName(archiveFolderName).next();
start(archiveFolder);
}
function getFolder(folderName){
var foldercount = 0;
//Selects all folders named exactally as parameter is given
var folder = DriveApp.getFoldersByName(folderName);
while (folder.hasNext()) {
var folders = folder.next();
foldercount++;
}
//Throws errors if number of folders != 1
if (foldercount < 1){
throw 1;
}
else if (foldercount > 1){
throw 2;
}
else{
return folder;
}
}
function start(archiveFolder) {
var sourceFolder = classroomFolderName;
var targetFolder = "New Archive";
var source = DriveApp.getFoldersByName(sourceFolder);
var target = archiveFolder.createFolder(targetFolder);
if (source.hasNext()) {
copyFolder(source.next(), target);
}
}
function copyFolder(source, target) {
var folders = source.getFolders();
var files = source.getFiles();
while(files.hasNext()) {
var file = files.next();
file.makeCopy(file.getName(), target);
}
while(folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var targetFolder = target.createFolder(folderName);
copyFolder(subFolder, targetFolder);
}
}
This is something I came across- you may find some value in it-
https://ctrlq.org/code/20016-maximum-execution-time-limit

Categories