folder.revokePermissions fails to revoke permissions - google-apps-script

Can anyone help me figure out why this revokeAdminPermissions function is not working? It executes, and the Execution Transcript states that it's successful, but the permissions are not revoked; the user still has access to the folders in question.
AddAdminPermissions and findFolder included for reference only, those are working fine.
Thanks in advance to anyone who can help!
function findFolder(folderName){
var folders = DriveApp.getFolders();
while (folders.hasNext()) {
var folder = folders.next();
if(folder.getName()==folderName){
return folder
}
}
}
function addAdminPermissions(email){
findFolder("Admin").addEditor(email)
findFolder("Project Sheets").addEditor(email)
findFolder("Team Members").addEditor(email)
}
function revokeAdminPermissions(email){
var admin = findFolder("Admin")
admin.revokePermissions(email)
var projects = findFolder("Project Sheets")
projects.revokePermissions(email)
var tmFolder = findFolder("Team Members")
tmFolder.revokePermissions(email)
}

Looking at the documentation from revokePermissions(user), it says that:
This method does not block users from accessing the File if they
belong to a class of users who have general access — for example, if
the File is shared with the user's entire domain.

I was able to work around this by calling folder.removeEditor(email) and folder.removeViewer(email). Doesn't explain the failure, but at least it works

Related

DriveApp.getFolders() not working as expected

I have a Google Apps Script for Sheets (running as a script under Sheets) that is supposed to return all the folders in my Google drive, however, the following code is stuck in an endless loop and only shows 1 of 3 folders in the drive - over and over again.
//this doesn't work -only shows 1 folder and repeats indefinitely
function getMyFolders() {
var f;
while (DriveApp.getFolders().hasNext()) {
f = DriveApp.getFolders().next();
console.log("f: " +f.getName());
}
}
The code returns the name of only one of my folders (hasNext() is clearly not working or there is a bug in Google Apps Scripts - since the while condition is never false (I ran it for several minutes and it never stopped!)
Could this be a security issue? This is just one of the problems I've run into. The other is that the 3 folders are subfolders of the same parent folder. No sure why getFolders() is not returning just the parent (that would make more sense).
Get All My Files
function getAllMyFiles(folder=DriveApp.getRootFolder()) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
let files = folder.getFiles();
while(files.hasNext()) {
let file = files.next()
sh.appendRow([file.getName(),file.getId()]);
}
let sfldrs = folder.getFolders();
while(sfldrs.hasNext()) {
let sfldr = sfldrs.next();
getAllMyFiles(sfldr)
}
}
Try this as your function instead to return just the parent folders!
function getMyFolders() {
let folders = DriveApp.getFolders();
while (folders.hasNext()) {
console.log("f: " +folders.next().getName());
}
}
If you want child folders as well, you would need to use recursion. Are you familiar with the concept? Happy to update my answer with that information if it's helpful.
Explanation:
You were calling DriveApp.getFolders(); on every iteration, so hasNext() was always retrieving the same item (the first folder in the iterator). Hence, you had an infinite loop.
I rewrote Cooper's answer to get all folders underneath a given parent.
In my case the parent folder is called "Gill".
function getAllMyFolders(folder=DriveApp.getFoldersByName("Gill").next()) {
let sfldrs = folder.getFolders();
while(sfldrs.hasNext()) {
let sfldr = sfldrs.next();
console.log("folder name: " +sfldr.getName());
getAllMyFolders(sfldr)
}
}
Ok - I played around with this code and discovered recursion is completely unnecessary, so here's code that will return all folders at all levels, then you can test the level with getParents()
function getMyFolders() {
let folders = DriveApp.getFolders();
while (folders.hasNext()) {
let folder = folders.next();
Logger.log(folder.getName() + ", Parent Folder: " + folder.getParents().next().getName());
}
}
I had a hunch that using a recursive function was the wrong way to go (and it looks terrible plus adds unnecessary overhead) but was confused why, my first attempt in this question did not work. The answer was the way my code was written - apparently, you need to assign DriveApp.getFolders() to a variable only once. Simply putting it in more than once, seems to reset it. That is, checking for DriveApp.getFolders().hasNext() and followed by DriveApp.getFolders().next() will cause the endless loop! Lesson here: assign it to a variable and then check for hasNext as calling it changes the state of the iterator which is reset again if getFolders is called again.
That was my actual bug here.

Google Apps Script trigger - run whenever a new file is added to a folder

I want to execute a google apps script whenever a new file is added to a specific folder.
Currently I'm using a run-every-x-minutes clock trigger, but I only need to run the script whenever I add a file to a folder. Is there a way to do this?
The same as this question - which is now almost 3 years old. The comment below the question states that:
There's not a trigger for that, if that's what you're hoping. How are
things getting into the folder, and do you have any control over that?
– Jesse Scherer Apr 8 '18 at 3:02
I wonder if this comment is still valid, and if it is, then if there's a workaround.
Issue:
Unfortunately, the comment you read is still true. Here is a list of all the available triggers and a trigger for a new file added to a folder is not one of them.
Workaround / Explanation:
I can offer you a workaround which is usually used by developers when they built their add-ons. You can take advantage of the PropertiesService class. The logic is quite simple.
You will store key-value pairs scoped to the script:
In your case, the key will be the folder id, and the value will be the number of files under this folder.
You will setup a time-driven trigger to execute mainFunction for example every one minute.
The script will count the current number of files within the selected folder. The function responsible for that is countFiles.
The checkProperty function is responsible for checking if the current number of files under this folder matches the old number of files. If there is a match, meaning no files were added, then checkProperty returns false, otherwise return true and update the property for the current folder ID, so when the script runs after 1 minute, it will compare with the fresh value.
If checkProperty returns true, then execute the desired code.
Code snippet:
Set up a time-driven trigger for mainFunction. Whatever code you put inside the brackets of the if(runCode) statement will be executed if the number of files under the folderID has changed.
function mainFunction(){
const folderID = 'folderID'; //provide here the ID of the folder
const newCounter = countFiles(folderID);
const runCode = checkProperty(folderID, newCounter);
if(runCode){
// here execute your main code
//
console.log("I am executed!");
//
}
}
And here are the helper functions which need to be in the same project (you can put them in the same script or different scripts but in the same "script editor").
function countFiles(folderID) {
const theFolder = DriveApp.getFolderById(folderID);
const files = theFolder.getFiles();
let count = 0;
while (files.hasNext()) {
let file = files.next();
count++;
};
return count;
}
function checkProperty(folderID, newC){
const scriptProperties = PropertiesService.getScriptProperties();
const oldCounter = scriptProperties.getProperty(folderID);
const newCounter = newC.toString();
if(oldCounter){
if(oldCounter==newCounter){
return false;
}
else{
scriptProperties.setProperty(folderID, newCounter);
return true;
}
}
else{
scriptProperties.setProperty(folderID, newCounter);
return true;
}
}

Google App Script Timed Out after 30 mins

I'm running below script but it errors out with a Error Message of "Timed Out" because the folder Id I'm giving have lots of lots folders under.
Can someone please help me out to optimize it or alternative approach.
function listFolderContents() {
var currentFolder = DriveApp.getFolderById("0B89Y-hAfWt_HVkhSbWprOVhPM00");
traverseFolder(currentFolder);
}
function traverseFolder(folder) {
DriveApp.getRootFolder().removeFolder(folder);
var subFolders = folder.getFolders();
while (subFolders.hasNext()) {
traverseFolder(subFolders.next());
}
}
It seems like you are doing a recursive delete of a folder. Try moving the removeFolder call to after the recursive calls. Otherwise, you delete the folder before you call getFolders on it, which is why I think it's timing out on you.
Like this:
function deleteFolder(folder) {
var subFolders = folder.getFolders();
while (subFolders.hasNext()) {
deleteFolder(subFolders.next());
}
DriveApp.getRootFolder().removeFolder(folder);
}
The termination condition for the resursive function is that there are no sub-folders, that is you are in a leaf folder.
See this related answer to a question about recursive deletion of a binary tree
I think that solution in this case could be using of getContinuationToken()
and example of iteration with continuationToken is available in another answer on stackoverflow.

Google apps script: How to create a downloadable html file?

Starting with 'google apps script'. I've created a simple script that logs some information with 'Logger.log("message");' with this lines being in a loop, so the log message is quite large.
I'd like now to create an html file with that same information and either being able to download that file locally or have it downloaded to my google drive.
Any idea how that could be achieved? I've tried looking examples for a while but I cannot have anything working...
Many thanks!
Yes you can, you should read first. Have a look this documentation
function createMyHTMLlog()
{
var folders = DriveApp.getFoldersByName('ff');// ff:your folder name on drive
if (folders.hasNext())
{
var folder = folders.next(); //select the folder
folder.createFile('Log','<b> your message here </b>', MimeType.HTML); //create the file
}
}
Update: Add more content later
function logUpdate()
{
var children = folder.getFilesByName('Log');// log is the created file name
if (children.hasNext())
{
var file = children.next();
var logContent = file.getAs('text/plain').getDataAsString();// get available msg
logContent = logContent.concat('<br>your<b> updated message </b> here'); // combine with new msg
file.setContent(logContent);
}
}

Migrating from DocsList to DriveApp?

I've been using DocsList for a big project and it was working perfectly. Lately, bugs have been popping up and they mostly have roots with getting a folder or file. When I did research, I found that DriveApp had been updated. The problem is that DriveApp doesn't have search parameters like DocsList had.
For example, if I had a folder structure like this:
Root
-Main Folder 1
--Folder 1
--Folder 2
-Main Folder 2
--Folder 1
--Folder 2
To get folder "Folder 1" in "Main Folder 2," I could put in the search parameter like so: DocsList.getFolder('Main Folder 2/Folder 1')
With DriveApp, I just can't understand how to work with it. From what I understand, I have to do something like this for DriveApp:
var mainFolders = DriveApp.getFoldersByName('Main Folder 2');
while (mainFolders.hasNext()) {
var mainFolder = termFolders.next();
var subFolders = termFolder.getFoldersByName('Folder 1');
// Something like this...
}
So if I had a folder that is more "deep" I would have to expand this even further..?
I feel like instead of making things easier, they made it more complicated with all the FileIterators and FolderIterators. And just making it hard to "get" a file or folder in code terms.
So basically, the point of this thread is to find out how a person who is use to DocsList to navigate and edit Drive files/folders can migrate to DriveApp and achieve the same things.
Small/Discrete examples of different scenarios would be really helpful. I can take it from there. I'll edit this more, if you guys think I'm not being clear about what I need help on.
The discussions from wchiquito's comment are an interesting read, but following all the links is time-consuming.
Bottom line: There will not be DriveApp version of getFolderByPath(), so you will need to roll your own. In the Google+ group, Faustino proposed a work-around and Eric improved it. Here it is, with an added check to allow paths that start with "/".
function getFolderByPath(path) {
var parts = path.split("/");
if (parts[0] == '') parts.shift(); // Did path start at root, '/'?
var folder = DriveApp.getRootFolder();
for (var i = 0; i < parts.length; i++) {
var result = folder.getFoldersByName(parts[i]);
if (result.hasNext()) {
folder = result.next();
} else {
return null;
}
}
return folder;
}
With that, you can simply do myFolder = getFolderByPath('Main Folder 2/Folder 1');. You will end up with a DriveApp Folder instance.
The code below works at mine.
It is based on having the same Id
function convertFileFromDocsListToDriveApp(file)
{ // Because of difference between DocsList and DriveApp
return (file === null) ? null : DriveApp.getFileById(file.getId());
}
function convertFolderFromDocsListToDriveApp(folder)
{ // Because of difference between DocsList and DriveApp
return (folder === null) ? null : DriveApp.getFolderById(folder.getId());
}
I call this in a few 'strategic' positions in my code.
I didn't test conversion from DriveApp to DocsList, but I expect this to work as well.