Automatically find and permanently delete is;trashed items in google Drive - google-apps-script

I have thousands of files on google drive which i have deleted sent to trash. My problem is they are still showing up after emptying the trash.
Emptying the trash simply does not work. The only way i can delete items and reduce quota is by searching for items using the is:trashed search operator permanently deleting them that way except this takes forever and i have no idea how to automate this using drives api.
I have successfully ran this from another question but it doesn't help my cause.
I am essentially after a script that lists files tagged as trashed owned by me and then permanently deletes them.
Any help greatly appreciated

"I have thousands of files on google drive which i have deleted sent to trash. My problem is they are still showing up after emptying the trash."
Just picking up on that second sentence, it's worth bearing in that when using the Google API (eg with rclone) or even with Google's own web interface, there is always a delay between issuing any "Empty the Bin" instruction, to Google Drive actually actioning it. And the more files that are in the Bin, and the smaller they are, the longer it will take for the Bin to be fully cleared. Thousands of very small files can take quite a while. And you have no control over when Google Drive will actually start the deletion or how fast it will work its way through the Bin.
Anyone who's familiar with Garbage Collection will understand precisely what I mean - "Ye know not at what hour the master cometh", as my minister Dad would have [annoyingly] put it :D
You can demonstrate this behavior for yourself by "removing" lots of files to the Bin, have the Bin displayed in a web browser and then issue (for example), "rclone cleanup mydrive:" which is the rclone command to empty the Google Drive Bin (where mydrive: has been configured to point to your Google Drive).
Firstly, you can observe that although the instruction has been issued, it can take a while before the Bin's list of files begins changing and at times, it can chug quite slowly through the list.
The other thing is that if you instruct the web interface to empty the bin it will immediately replace the list of files and folders with a graphic saying the Bin is empty. In fact, it's not, as you will see if you click on the "My Drive" folder and then immediately click back on the Bin again - the entire list will be redisplayed as before, minus anything that Google Drive has deleted in the interim. In the background, however, Google Drive will be revving up to delete your files.
The other, other thing is also that any further files and folders that are removed to the Bin after the initial "Empty the Bin" instruction has been issued, will need a subsequent command for them to be deleted permanently - they will not be covered by the first command. Again, you can demonstrate this to yourself by removing items to the Bin while the first "Empty the Bin" is in progress - you will ultimately be left with a Bin containing the second lot of removals.
I just thought it was worth pointing this out (you may disagree :D ), as I too have tried to empty my Bin in the past and thought that Google Drive wasn't doing anything.
Edit: apologies for the edit. I meant to say that once you've issued the "rclone cleanup mydrive: " command, if you then use "rclone about mydrive:", one of the stats it reports is the total space used by the files in the Bin. By periodically issuing "rclone about..." you can see the amount of space used by Bin decreasing each time. This is often the better way of checking how your "Delete Forever" command is progressing since Google Drive already considers those files gone, albeit in theory rather than practice.

Cooper's method failed for me.
files.hasNext()
does not iterate through trash items. Cooper did point me at the Advanced Google Services API:
//This method requires adding the Advanced Google Services Drive API under Resources -> Advanced Google Services
//And the Google API Console[https://console.cloud.google.com/apis/library/drive.googleapis.com/]
function DeleteTrashedFiles(){
Drive.Files.emptyTrash();
};
If you would like to see how much of your quota you deleted you can do this.
function DeleteTrashedFilesLogged(){
// Enable Drive.About.get() fields here:
// https://developers.google.com/apis-explorer/?hl=en_US#p/drive/v3/
var lngBytesInTrash = Drive.About.get().quotaBytesUsedInTrash;
var filesDrive = Drive.Files;
Logger.log('Attempting to clean up ' + lngBytesInTrash + ' bytes');
filesDrive.emptyTrash();
Logger.log('Trash now contains ' + Drive.About.get().quotaBytesUsedInTrash + ' bytes');
filesDrive = Drive.Files;
};

Try this:
function delTrashedFiles() {
var files=DriveApp.getFiles();
while(files.hasNext()) {
var file=files.next();
if(file.isTrashed()) {
Drive.Files.remove(file.getId())
}
}
}
You will need to enable Drive API

Related

Can one iterate through bound scripts and edit their manifest files via a script?

I am building an application that will have many users, each of whom will have many Google documents. Each doc will have a custom menu and that custom menu will invoke a library script. I may need or want to change the coding in that library script from time to time.
As changes to a library script must be "saved" as a new version in order for the changed version to be passed on to client scripts (in my case, the scripts bound to Google Docs), I need a way that users can "batch" update the version number in their docs' bound script appsscript.json
file.
I have researched this issue and there seems to be two general alternatives: set the client scripts' library mode to "Developmental" or use an add-on.
The problem with the former is that it won't work unless the users are all granted edit mode access to the library script (which seems particularly a bad idea as the users may well not even be known to me).
The problem with the later is essentially complication and cost. If I make the add-on private, it only works for users in the same domain which means I have to create a G-Suite domain (and pay at least (as of this writing) $72 per year per user—a non-starter for this project).
If I make the add-on public, in addition to the complication, I have to sign up to the Google Cloud Platform and the costs for that require one to navigate a veritable maze of choices and alternatives such that at this point, I really have no idea what the cost per service or user would be.
Below I present some "mock-up" code that should at least indicate the direction I am trying to go.
function upDate() {
var version = 23
var scripts = "https://script.google.com/u/0/home"
//while (scripts.hasNext()) {
//var script = files.next();
//Note: All of the script's have the same name as they commence life bound to a template, which template is duplicated to create the rest of the user's docs
if( scriptName = ScriptName){
//set.dependencies.enabledAdvancedServices[].version
}
}
I don't even know if it's possible to step through bound scripts the way one step's through files in a Google Drive, so that is the first question. Then, the second question is whether, assuming you can step through the scripts one by one, you can change a manifest value—in this case, the version number.
One cannot step through container-bound scripts as they are (no longer) located in one's Google Drive. Moreover, despite Google's documentation about using a "stable" value in the version section of the manifest, that documentation appears erroneous. Finally, one cannot programmatically edit standalone scripts.
However, there is a workaround. What I ended up doing was writing a script that steps through all of the involved Google Docs and copies them to a blank template (i.e., in effect, duplicates them all). That blank template has the bound script installed in it with the new version number of the library. Then, delete original docs (via the same script) and voilà, batch update to all of the target docs is accomplished. (One drawback of this is: if Google Doc revision history is important to you, be advised this gambit jettisons that (unless you keep the original versions).

apps script google drive auto remove editors

I am sharing the folder / file in my Google drive to 'A'.
But now I'm trying to stop sharing to 'A'.
I tried to stop sharing all folders / files using apps script, but apps script could not execute for more than 5 minutes.
apps script Is there a better solution?
Are you exceeding the 5 minutes because you are iterating over a lot of files?
By the way, probably it is better to use the advanced API to perform this operation quicker (this is what comes from my personal experience, it maybe not true, but avoiding FileIterators tends to speed things up). Also, if you are iterating using DriveApp.getFiles() instead by searching directly the right ones, you are iterating over all files that you have in your drive, and it takes quite a lot.
Using the advanced API you may only iterate over the files that user A can modify/read (and even if you reach the 5 minutes, if you run again the function, the files/folders that you have already modified will be not taken into account by the query).
The file list can be obtained via (hypothesis: user A has mail address a.mail#gmail.com):
var files = Drive.Files.list({
q: '"a.mail#gmail.com" in readers or "a.mail#gmail.com" in writers'
});
files.items.forEach(file => {
var file_app = DriveApp.getFileById(file.id);
// Do here what you need to stop sharing.
});
The listing gets both files and folders. You can check this by using the property file.mimeType (it is application/vnd.google-apps.folder for folders, tha actual mimetype for the files, like application/pdf for a PDF).
DISCLAIMER: you have to enable the advanced API in both AppScript and Google Console. It is easy, just read carefully the instructions.
Test: I tested this solution against the file shared with one of my colleague. We have in shared folders with more than 10000 files, and I succeed to iterate on all the files without reaching the limit. The DriveApp callback reached the limit. But again, this is personal tests, take it with cautions.

How can I use Google Apps Script to move files between personal Google Drives and Team Drives?

Recently, I gained access to Google Team Drive via Google's Team Drive early adopter program.
I created a Google Docs file called Hello, world!, and then wrote a short Google Apps Script function which uses an addFile() method to update which Google Drive folder the file is attached to:
function move_or_link_file() {
var source = DriveApp.getFolderById("<sourceID>");
var fileiter = source.getFilesByName("Hello, world!");
var dest = DriveApp.getFolderById("<destID>");
while (fileiter.hasNext()) {
var file = fileiter.next();
dest.addFile(file);
}
}
Typically, a Google Drive folder has a URL which matches the following pattern: https://drive.google.com/drive/folders/<alphanumericID>. Although it's perhaps a bit inelegant, I can test my code under various operating scenarios and conditions by simply opening different combinations of Google Drive folders in a web browser, selecting the <alphanumericID> portion of the folder URL, and then manually copy-and-pasting values for this string into <sourceID> and <destID>.
After testing, I am able to identify four different input conditions which result in three distinct behaviors:
Case 1: <sourceID> and <destID> are both folders in my personal Google Drive:
In this case, the script behaves in effect as if it's creating a symbolic link: the Hello, world! file now appears in both directories. Note that it really is the same file, not two identical copies: for example, if I open the document, then the document URL, like the folder URLs, also follows a pattern: https://docs.google.com/document/d/<documentID>/edit. I can tell the file is the same because when I open it, the URL for the document shares the same <documentID>, regardless of which parent folder I use to access it.
For case 1, I can also get the script to behave more like a mv command by simply appending an additional line, source.removeFile(file); to the end of the file iterator loop.
Case 2: <sourceID> is a folder in my personal Google Drive while <destID> is a folder in a Team Drive:
In this case, the script behaves like a mv command by default, rather than as a symbolic link, even without the additional call to the removeFile() method that I mentioned in case 1: i.e., the Hello, world! file simply disappears from my personal drive and reappears in the Team Drive.
Case 3: <sourceID> and <destID> are both folders in a Google Team Drive:
This results in an error message from Google Apps Script: Cannot use this operation on a Team Drive item. (line 7, file "move_or_link_file").
Case 4: <sourceID> is a folder in a Team Drive while <destID> is a folder in my personal Google Drive:
Same error as for Case 3.
Now here is the really weird part: the GSuite graphical user interface (i.e., what you are using when you access Google Drive files and folders via the web browser) offers a Move command via a popup window that appears when you right-click on a file. This GUI version of the Unix-like mv command behaves identically for all four of the above cases: it doesn't matter whether you are moving a folder back and forth between a personal drive or team drive, or internally within a drive, it works correctly and moves the file to where you would expect it to go, every time.
So, I presume it must be possible to implement a mv command via Google's API, somehow, given that they've evidently done it already for users of the GUI interface.
Thus my question: given that it's empirically possible to move files back and forth between arbitrary combinations of folders in personal drives and team drives, how would I actually do it, using only the API calls provided by Google Apps Script?
Also, a bonus question: suppose that, similar to Case 1, instead of moving a file between two different folders in the same Team Drive, I actually wanted to create a symbolic link attaching the file to both folders--how would I use the Google API to do that as well? (I.e., how can I get Case 3 to behave more like Case 1?)
The Google Team Drives only recently started allowing scripts in general. I would imagine the file move you were able to achieve in case 2 is not even intended. There are still several limitations on the team drive (for example you cannot move folders).
For Case 1 I can simply point out that your script is not actually a move command. You should actually imagine Google Drive folders as Gmail tags. A file can have no folders at all. Your script merely assigns a tag to the file and as such it can appear in many folders (just like an email can have many Gmail tags and appear in each tags "folder").
It works in case 2 because Team Drive is a seperate entity from your personal drive. In essence, when you added it to the team drive you had to give up the ownership of the file. As far as I have seen, team drive considers that adding a file to it means that it should be removed from all other parents. I would assume that is why in cases 3 and 4 you cannot move any items. The owner is the team drive itself, however the commands are being sent as a regular Gsuite user.
Drive REST api was recently (~begining of March) updated to work with team drives: https://developers.google.com/drive/v3/web/about-teamdrives so I believe that technically what you are looking for can be done, however considering there are still several limitations on team drives, I don't think it will be documented as well as it could be.

Publish and distribute a Google Sheets with App Scripts so user gets private copy of sheet

Usually addons are the way to distribute a Google Sheets script, but I do not know if that is a fit for my case:
The user will manually fill out a list of their contacts / friends and answer custom questions about them. So they will, in some sense, definitely need their own copy.
This should scale reasonably well. i.e. O(users) copies of invisible tabs in a sheet is not going to work.
The user should be good to go from clicking a link with minimal work. i.e. a dialog saying "Please make your own copy of this sheet!" and forcing them to fork is not ideal (although I guess it could work.)
Idea #1: manual fork
Very much not ideal to make the user go through that workflow and hard to implement. i.e. the dialog to copy-and-fork will of course be present in the copy so additional logic will be needed to (somehow) de-wire that.
Idea #2: bootstrap + copy
The next idea I have is two apps: 1) an installer that needs access to their Google Drive and 2) the sheet it will copy into their Google Drive.
This is... okay. But I don't like that the user has to authorize an unknown script to have arbitrary access to their Google Drive (!!). Even a more specific question like "add a file to your Drive" would get out of this bind.
The other problem is that the user seeing the example sheet makes it clear what the heck is going on. This should feel as safe as copying a sheet (and be that safe), not like the relative hassle of "installing an app," much less one needing sensitive data.
Idea #3: Obscure addon
I'm not sure if an addon that lives inside of a spreadsheet can be self-discoverable.
But, from there, I believe the addon would unpack the entire structure of the sheet from script. This is something I can handle, even though it seems kludgey still. And this may even require the user having a blank sheet ready first?
Initially I asked about tracking forks. Now I am much more ambitious in that I am looking for an actual proper distribution mechanism that is not asking users to just manually fork everything.

Google Apps Script access Drive SDK API

Is there a way to use the Google Drive API's from Google Apps Script. I am aware of the DocsList Service, which allows you to look at folders and files , however what about all the other API's in Google Drive (Files,About,Changes,Children,Parents,Permissions,Revisions,Apps,Comments,Replies). For example, is there API access to add Comments to files from Google Apps Script.
Apps Script has the ability to access Google API's, but you need to explicitly enable them before they can be used.
In the code editor, choose RESOURCES, ADVANCED GOOGLE SERVICES
Click the OFF button, to turn the service ON.
Before you close the dialog box, click the link at the bottom to open up the API Manager.
Once you've completed those two steps, the Drive API is available inside of Apps Script. Type the key word Drive then type a period, and available methods will show up in a list.
Methods
get - Gets a file's metadata by ID.
insert - Insert a new file.
patch - Updates file metadata. This method supports patch semantics.
update - Updates file metadata and/or content.
copy - Creates a copy of the specified file.
delete - Permanently deletes a file by ID. Skips the trash.
list - Lists the user's files.
touch - Set the file's updated time to the current server time.
trash - Moves a file to the trash
untrash - Restores a file from the trash.
watch - Start watching for changes to a file.
emptyTrash - Permanently deletes all of the user's trashed files.
https://developers.google.com/drive/v2/reference/files#methods
(Google-Apps-Script=GAS) Drive Services added 2013.05.13 (to be announced at the 2013 Google I/O 2 days later) is apparently-exactly designed to replace the prior API(DocsList) and allow GAS to access the Google Drive SDK, though that functionality is currently not mentioned (why?) from those official docs but it is most certainly suggested by the new new API name "Drive" and is stated at as the purpose by Google's great demo video "Integrate Google Drive with Google Apps Script — Google I/O 2013" and "Drive SDK" is mentioned in by the search functions as searchFiles(String).
And Drive Services works (I'm using it; and though I could think of many improvements, haven't found any bugs of memory) including it works for useful apps (see the the video above for one of the most impressive ones I've seen) but it isn't complete (including doesn't expose the full Drive SDK) including:
it doesn't (yet?) allow one to access prior versions of content (as that vide plus (the enhancement request: Google Search for "Issue 2811: Access the document revisions using DriveApp"),
control the indexing status & method of content,
one can't get a list of a file or folder's accessors (apparently deliberately says the video; but using the prior DocsList or Library mentioned seems like it would be a workaround).
I'd have included more links to help out but I'm a new poster here so the editor's telling me "You need at least 10 reputation to post more than 2 links.".
Yes, it's possible.
Take a look at the documentation for Class File, which includes functions for obtaining and/or changing most of the attributes you've listed.
For an example of code that gets some of this info for files and folders, see this answer.
You should also have a look at this Library written by Romain Vialard, one of the GAS TC.
It provides functions that are not available directly in gas or - at least - not as simply.