0

I'm trying to delete files and folders older than 7 days in a specific Team Drive (not in the whole Google Drive).

To accomplish this I tried to merge what I read here: Apps Script - Automatically Delete Files from Google Drive Older than 3 Days - Get List of Files

I don't have enough rep to add a comment there so that's why I'm opening a new thread. Here you can find my ripoff from user1588938:

function getOldFileIDs() {
  var fileIDs = [];
  // Old date is 30 days
  var oldDate = new Date().getTime() - 3600*1000*24*30;
  var cutOffDate = Utilities.formatDate(new Date(oldDate), "GMT", "yyyy-MM-dd");

  // Get folderID using the URL on google drive
  var folder = DriveApp.getFolderById('XXXXXXX');
  var files = folder.searchFiles('modifiedDate < "' + cutOffDate + '"');

  while (files.hasNext()) {
    var file = files.next();
    fileIDs.push(file.getId());
    Logger.log('ID: ' + file.getId() + ', Name: ' + file.getName());
  }
  return fileIDs;
};

function deleteFiles() {
  var fileIDs = getOldFileIDs();
  fileIDs.forEach(function(fileID) {
    DriveApp.getFileById(fileID).setTrashed(true);
  });
};

I'm stuck with 'getFolderById' function because I suppose it doesn't apply to a Team Drive root but only works for folders inside of it. Indeed, when I look at the logs I can see that the output for:

  var folder = DriveApp.getFolderById('this-is-my-team-drive-id');  

is a generic: [18-07-30 06:34:49:146 PDT] Team Drive and not the name of the Team Drive I chose.

I can't go any further with the script because of this.

Any hint on how to list every file and subfolder in a Team Drive using searchFiles?

This solution might apply but it works for a folder inside a Team Drive and not on the root of the Team Drive: browse files in google team drive

Thanks!

Daniele INeDiA
  • 327
  • 1
  • 3
  • 9
  • Team Drives have different permissions than Google Drive. So users may have access to some subfolders but not the root, etc. To get the name of a Team Drive, you need to use the Drive REST API's TeamDrives resource. See my answer [here](https://stackoverflow.com/q/49542984/9337071) – tehhowch Jul 30 '18 at 15:17
  • Thanks for the hint, I can now understand the difference. Unfortunately I'm not skilled enough to evolve the script with this new knowledge, thanks anyway. – Daniele INeDiA Jul 31 '18 at 15:51
  • if you include code in your question, you can get answers that will resolve the specific error that is hindering your script. But no code / no error means your question is too broad to answer. – tehhowch Jul 31 '18 at 16:21
  • question modified, thanks – Daniele INeDiA Aug 01 '18 at 09:41
  • so what's missing from the script you provide? Per your question title, you want to delete files older than 7 days (30 in your code) - and the code does this. (Albeit trashing instead of deleting) – tehhowch Aug 01 '18 at 10:53
  • It doesn't work because the code is intended to work for a folder, but I want to do this in a File Drive root. Also, I'm not sure this is recursive, it would be nice if it could search in subfolders. – Daniele INeDiA Aug 01 '18 at 12:33
  • a root is a folder. Do you have permission to trash those team drive files? You may need to use the Drive advanced service to guarantee compatibility with team drive items. Review the Drive REST API v2 – tehhowch Aug 01 '18 at 16:48

3 Answers3

0

If someone out there is trying to achieve the same result, this is how you do it.

function deleteOldFiles() {
  var Folders = new Array(
    'YOUR-TEAM-DRIVE-ID' //you can find this in the team drive url
  );
  var DaysRetentionNumber = 15; //how many days old your files and folders must be before getting deleted?
  var RetentionPeriod = DaysRetentionNumber * 24 * 60 * 60 * 1000;

  Logger.clear();

  for each (var FolderID in Folders) {
    folder = DriveApp.getFolderById(FolderID);
    processFolder(folder);
  }

  function processFolder(folder){
    Logger.log('Folder: ' + folder.getName());
    var files = folder.getFiles();
    while (files.hasNext()) {
      var file = files.next();
      Logger.log('File: ' + file.getName());
      if (new Date() - file.getLastUpdated() > RetentionPeriod) {
        //file.setTrashed(true); //uncomment this line to put them in the trash
        //Drive.Files.remove(file.getId()); //uncomment this line to delete them immediately; CAREFUL!
        Logger.log('File '+ file.getName() + ' trashed');
      }
    }
    var subfolders = folder.getFolders();
    while (subfolders.hasNext()) {
      subfolder = subfolders.next();
      processFolder(subfolder);
    }
    checkEmptyFolder(folder);
  }

  function checkEmptyFolder(folder){
    if(!folder.getFiles().hasNext() && !folder.getFolders().hasNext()){
      Logger.log('Empty folder: '+ folder.getName());
      folder.setTrashed(true); // put them in the trash
    }
  }

  if(Logger.getLog() != '')
     MailApp.sendEmail('youremailaddresshere', 'Team Drive weekly cleanup report', Logger.getLog()); //get a log in your email so that you can see what will be deleted; try this before uncommenting the trash/delete lines!
}
Daniele INeDiA
  • 327
  • 1
  • 3
  • 9
  • `for each (... in ...)` is [deprecated](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for_each...in). Use a more appropriate iteration pattern, especially for an `Array` object. https://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-a-bad-idea – tehhowch Oct 22 '18 at 11:12
  • Does anyone have a better alternative to be edited into the script? – LWC Jan 11 '19 at 12:08
0

@Daniele INeDiA's script is broken due to various reasons:

  1. The script fails withReferenceError: "Drive" is not defined. (line 24, file "Code"), which refers to //uncomment this line to delete them immediately;
  2. The script also fails because of Access denied: DriveApp. (line 39, file "Code"), in case the root folder is empty. It's because it doesn't differentiate between the root folder and its sub folders.

I've fixed #1 by simply leaving it commented out, but here's a fixed version for #2.

While at it, I've also added a feature to always delete (always_delete on/off). That way you can schedule it to run every night to make sure you start the day with an empty folder.
Also while at it, I've added an on/off switch to send a log by e-mail.

P.S. I did see tehhowch's comment that "for each (... in ...) is deprecated", so feel free to add an alternative and I'll use it instead.

function deleteOldFiles() {
  var sendlog = false, Folders = new Array(
    'YOUR-TEAM-DRIVE-ID' //you can find this in the team drive url
  );
  var DaysRetentionNumber = 15; //how many days old your files and folders must be before getting deleted?
  var RetentionPeriod = DaysRetentionNumber * 24 * 60 * 60 * 1000;
  var always_delete = true;

  Logger.clear();

  for each (var FolderID in Folders) {
    folder = DriveApp.getFolderById(FolderID);
    processFolder(folder, FolderID);
  }

  function processFolder(folder, FolderID){
    Logger.log('Folder: ' + folder.getName());
    var files = folder.getFiles();
    while (files.hasNext()) {
      var file = files.next();
      if (!always_delete)
        Logger.log('File: ' + file.getName());
      if (always_delete || new Date() - file.getLastUpdated() > RetentionPeriod) {
        file.setTrashed(true); //uncomment this line to put them in the trash
        // Don't uncomment the following because it breaks the script!
        //Drive.Files.remove(file.getId()); //uncomment this line to delete them immediately; CAREFUL!
        Logger.log('File '+ file.getName() + ' trashed');
      }
    }
    var subfolders = folder.getFolders();
    while (subfolders.hasNext()) {
      subfolder = subfolders.next();
      processFolder(subfolder);
    }
    if(Folders.indexOf(FolderID) == -1)
      checkEmptyFolder(folder);
  }

  function checkEmptyFolder(folder){
    if(!folder.getFiles().hasNext() && !folder.getFolders().hasNext()){
      Logger.log('Empty folder: '+ folder.getName());
      folder.setTrashed(true); // put them in the trash
    }
  }

  if(sendlog && Logger.getLog() != '')
     MailApp.sendEmail('youremailaddresshere', 'Team Drive cleanup report', Logger.getLog()); //get a log in your email so that you can see what will be deleted; try this before uncommenting the trash/delete lines!
}
LWC
  • 1,084
  • 1
  • 10
  • 28
  • Daniele's script is not broken--you must enable the `Drive` advanced service before trying to use it. If you want to actually delete files instead of just trash them, you'll need to do that. It should be trivial to replace the deprecated loop syntax with non-deprecated alternatives (if it isn't, you need to review the various loop options and the object types being iterated). Your answer would probably be better served as an edit to Daniele's answer, highlighting the fix for #2 and linking to official documentation on how to enable the`Drive` advanced service. – tehhowch Jan 11 '19 at 18:07
  • Edits are meant to to fix a word or two, not to add whole new lines of codes. I invite anyone to provide a better loop option and I promise to edit it in. – LWC Jan 11 '19 at 20:22
0

In my case I needed variability of how long to keep files based on what top folder they are in. Some folders have thousands of generated imgs that need to be cleaned up often and other folders have reports that should be kept around for longer. There are a lot of nested folders as well. This script has to be triggered a lot to keep up. Any tips on optimization would be great!

I have not been able to find an example quite like this, so I hope it can be useful to someone searching as I had searched.

/**
 * Clean up Google Drive folders sending all files 
 * last modified older than daysToKeep to the trash
 */
function cleanUpFolders() {
  let folders = [
    { id: "folderId1", daysToKeep: 14 },
    { id: "folderId2", daysToKeep: 30 },
    { id: "folderId3", daysToKeep: 90 }
  ];
  for (let f = 0; f < folders.length; f++) {
    let folder = DriveApp.getFolderByIdAndResourceKey(folders[f].id, "");
    let daysAgo = getDaysAgo(folders[f].daysToKeep);
    cleanUpFolder(folder, daysAgo);
  }
}

/**
 * Cleans up a folder sending all files last modified more than filterDate to the trash
 *
 * @param {DriveApp.Folder} folder to proccess
 * @param {String} filterDate date to search files older than in format YYYY-MM-DD
 */
function cleanUpFolder(folder, filterDate) {
  console.log("searching in " + folder.getName());
  let trashCount = 0;

  // Send files last modified before filterDate to the trash
  let query = 'modifiedDate < "' + filterDate + '"';
  let files = folder.searchFiles(query);
  while (files.hasNext()) {
    let file = files.next();
    file.setTrashed(true);
    // console.log(file.getName() + " modified " + formatDate(file.getLastUpdated()));
    trashCount++;
  }

  // Recursively clean up all sub folders
  let folders = folder.getFolders();
  while (folders.hasNext()) {
    trashCount += cleanUpFolder(folders.next(), filterDate);
  }
  
  // Check if the folder is now empty and trash it as well
  if (isEmptyFolder(folder)) {
    folder.setTrashed(true);
    console.log(folder.getName() + " is empty and has been trashed");
  }

  console.log(trashCount + " files found last modified before " + filterDate + " in " + folder.getName() + " have been trashed");
  return trashCount;
}

/**
 * Check if a folder has any files or folders
 *
 * @param {DriveApp.Folder} folder to check
 */
function isEmptyFolder(folder) {
  return !folder.getFiles().hasNext() && !folder.getFolders().hasNext();
}

/**
 * get date string "YYYY-MM-DD" days ago from now
 *
 * @param {number} days ago from now
 */
function getDaysAgo(days) {
  let date = new Date(Date.now());
  let nowString = formatDate(date);
  date.setDate(date.getDate() - days);
  let daysAgoString = formatDate(date);
  console.log("now: " + nowString + ", " + days + " days ago: " + daysAgoString);
  return daysAgoString;
}

/**
 * Format date as "YYYY-MM-DD"
 *
 * @param {Date} date to format
 */
function formatDate(date) {
  return date.getFullYear() + '-' + 
        (date.getMonth()+1).toString().padStart(2,'0') + '-' + 
         date.getDate().toString().padStart(2, '0');
}