Implement a folder move function in Google Dirve - google-apps-script

I am very new to writing any types of scripts and am trying to write a Google Apps Script to search through folders for a particular string, and if found move the folder to another folder.
I can copy a folder, but not move it since the copied folder has the same ID as the original, and deleting one deletes the other. From what I have been able to figure out I can implement this by deleting the right child from the folder. I've tried this in the APIs explorer and it does what I expect.
The problem is that I can't for the life of me figure out how to implement the example given in the Google Drive SDK page in script.google.com. If I paste in the example code I get a Missing name after . operator error. None of the scripting commands let me delete a child.
Likely this is a simple issue and arises from my trying to learn some JavaScript by doing this project, but I would appreciate any help or advice on how to implement a move function in Google Script.
The specific question is: How do I implement a move folder script? This is what I am trying, but it removes both folders:
while (contents.hasNext()) {
folder = contents.next();
name = folder.getName();
if (name.search("XYZ") != -1) {
moveFolder = folder;
targetFolder.addFolder(moveFolder) moveFolder.setName('DEL_' +
moveFolder.getName());
removeFolder(moveFolder);
}
}

First, let's clear up some confusion. Two Drive-related "Services" are provided for use within Google Apps Script, Drive Services, and Docslist Services. The documentation you've referenced is for the Google Drive SDK, which is not the same thing. If you're programming in Google Apps Script, ignore the SDK documentation.
Next, let's talk about what a folder or file is on Drive, and how that affects the concepts of "copy", "move" and "delete". A file or folder may have a folder as a parent. In fact, it may have multiple parents. If we want to have the same file or folder "copied" to another folder, we just add it as a child to the target folder. At this point, it may look like we have two items with the same ID - but what we actually have is one item with two parents. If we then remove the file or folder from the original folder (disown it, in a way), it will be left with one parent, and this will appear like a "move". The file or folder will not have been "deleted". On the other hand, if instead of removing the parent / child relationship, we DO delete the file or folder, it will appear that we have "deleted both folders", as you have described.
So, your "move" script needs to add a new parent / child relationship, and remove the other.
The Drive starter script that is presented when you open a new script in the editor contains a function called moveFileToFolder().
This function does what you are looking for, except that the thing it's moving is a file. However, the methods that are used for a file have cousins for folders, so you can just do some replacement to end up with moveFolderToFolder():
/**
* This script moves a specific folder into a given folder, and removes the folder
* from all other folders that previously contained it. For more information on
* interacting with files, see
* https://developers.google.com/apps-script/drive/file
*/
function moveFolderToFolder(sourceFolderId, targetFolderId) {
var targetFolder = DriveApp.getFolderById(targetFolderId);
var sourceFolder = DriveApp.getFolderById(sourceFolderId);
var currentFolders = sourceFolder.getParents();
while (currentFolders.hasNext()) {
var currentFolder = currentFolders.next();
currentFolder.removeFolder(sourceFolder);
}
targetFolder.addFolder(sourceFolder);
};
If you want to deal with folder Names, then you need to use getFoldersByName() to collect all matching folders, then iterate using next() into the list. Here's how that function could be modified to use folder names. To simplify things, it assumes that there is just ONE folder with the given name, and throws an exception if that's not the case. Once we have a handle on the folders we're interested in, the balance of the function remains the same as the previous example.
function moveNamedFolderToFolder(sourceFolderName, targetFolderName) {
var matchedFolders = DriveApp.getFoldersByName(sourceFolderName);
if (matchedFolders.hasNext()) {
var sourceFolder = matchedFolders.next();
if (matchedFolders.hasNext()) throw new Error( "Source Folder Name not unique" );
}
matchedFolders= DriveApp.getFoldersByName(targetFolderName);
if (matchedFolders.hasNext()) {
var targetFolder = matchedFolders.next();
if (matchedFolders.hasNext()) throw new Error( "Target Folder Name not unique" );
}
var currentFolders = sourceFolder.getParents();
while (currentFolders.hasNext()) {
var currentFolder = currentFolders.next();
currentFolder.removeFolder(sourceFolder);
}
targetFolder.addFolder(sourceFolder);
};

Related

Remove files from GDrive Trash

I have backup scripts that are copying my files into GDrive and it's also deleting files that are older than X days. But this is filling my bin with lot of these files. I want to remove them from trash bin.
I wrote this code:
function getTrashedFiles() {
//find every file in bin
var trashedSearch = DriveApp.searchFiles('trashed=true');
while (trashedSearch.hasNext()) {
var file = trashedSearch.next();
if (file.getName().indexOf("DBKLP") >= 0) {
//this is correctly printing files that I want to remove
Logger.log(file.getName());
//this should remove from bin
DriveApp.removeFile(file);
}
}
}
This code correctly finds every file, that starts at "DBKLP". But when it runs, it's not removing files from bin. It's just removing them 'once again', so their date of remove is basically "now".
I can't find a way to "remove from bin".
The AppScript DriveApp does not support it, but you can do it with the Drive API v3. As the documentation states, you'd need to use the DELETE HTTP method; alike this you could even skip trashing these files altogether. Here's an example (mine) of how to use Google API with AppScript (that's another API, but at least it's a good starting point). The documentation combined with a rough example should hopefully push you into the right direction.
From the documentation: removeFile(child) does not delete the file, but if a file is removed from all of its parents, it cannot be seen in Drive except by searching for it or using the "All items" view.
Have you tried Drive.Files.remove() instead of DriveApp.removeFile()?
To enable it, in the script editor, you have to click on the + button at "Services". Check here: imgur.com/cVFCAXQ (or, in the old editor navigate to Resources > Advanced Google Services > Drive API).
I have a similar script that seems to work. Notice that it has to find all files and folders because they are treated separately.
// Delete all files from trash older than 1 month
function cleanOldTrash() {
var files = DriveApp.getTrashedFiles();
var folders = DriveApp.getTrashedFolders();
var lastMonth = new Date();
lastMonth.setDate(lastMonth.getDate() - 30);
while (files.hasNext()) {
var file = files.next();
if (file.getLastUpdated() < lastMonth ) {
Drive.Files.remove(file.getId());
}
}
while (folders.hasNext()) {
var folder = folders.next();
if (folder.getLastUpdated() < lastMonth) {
Drive.Files.remove(folder.getId());
}
}
}
I actually do have a problem with this script when files are stored on the trash of Team Drives that I don't have permission to access, but I'll try to get more help with that in a separate question. I'll update this answer if needed.

How to access to a subFolder with google app script?

Im using the script editor in a Google spreadsheet, and I want to create a folder inside a root folder , already created. I've tried to follow the official notice of the API DriveApp, but without success.
Here is my code :
function createSubFolder(rootFolder,name){
DriveApp.getFoldersByName(rootFolder);
DriveApp.createFolder(name);
}
I've tried also ( following this old post working with the old version of DriveApp : i.e DocsList) :
function createSubFolder(rootFolder,name){
DriveApp.getFoldersByName(rootFolder).createFolder(name);
}
It seems to be very easy but I don't see where am I wrong.
Does anyone know the way to do it?
Thanks
You need to assign values to a variable.
function createSubFolder(rootFolder,name) {
var AllFoldersWithThisName = DriveApp.getFoldersByName(rootFolder);
var theFirstFolderWithThisName = AllFoldersWithThisName.next();
theFirstFolderWithThisName.createFolder(name);
};
In Google Drive, you could have 1,000 folders all with the same name. Same thing with file names.
The getFoldersByName() method returns:
FolderIterator — a collection of all folders in the user's Drive that have the given name
You need to get one of the folders out of the collection of folders. That is done with the next() method.

Delete or Trash specific file in Drive

I had a script that ran every day at 5 am, that would move a specific file (data.xls) to the trash. However, since DocsList has been retired, the script no longer functions, and I'm having trouble updating it.
I've seen a couple of delete/setTrashed scripts posted here, but they all seem to function for an array of files, and i only want one specific file deleted.
I'm not a coder, and self-taught myself most of the small amount i have, so i need it as simple as possible (sorry.)
Any and all help or guidance is very appreciated. Thank you.
function myFunction() {
var files = DriveApp.getFilesByName('data');
while (files.hasNext()) {
var file = files.next();
ID = file.getId(file)
Drive.Files.remove(ID);
}
}
I've seen a couple of delete/setTrashed scripts posted here, but they
all seem to function for an array of files, and i only want one
specific file deleted.
Simply put, to delete a single file you delete the first item in the list, what you are calling an array and what Google calls a file iterator.
Retrieving a file by name is going to return a list(iterator), even if it has only one file by that name, so you must treat the single item as the first item in the iterator and set that first item to trash.
Edit:
function myFunction() {
var files = DriveApp.getFilesByName('data');
while (files.hasNext()) {
files.next().setTrashed(true);
}
}
if you know that there is one and only one file by that name you could do something as simple as:
function myFunction() {
DriveApp.getFilesByName('data').next().setTrashed(true);
}
Since this is the first hit for Googling "apps script move file to trash", I found the following easy solution:
let file = DriveApp.getRootFolder().createFile('RIP file.txt', 'Good-bye, world ㅜㅜ');
file.setTrashed(true); // So the file is deleted after 30 days
File documentation
I have workaround using DriveApp removeFile. Note this does not delete or trash the file in the user archive, but is no longer visible in the named folder.
removeFile(child)
Removes the given file from the root of the user's Drive. This method
does not delete the file, but if a file is removed from all of its
parents, it cannot be seen in Drive except by searching for it or
using the "All items" view.
DriveApp.getFolderById(DriveApp.getFolderById(folderId)).removeFile(DriveApp.getFileById(fileId))

Google apps script Permissions on created file in shared folder

I have a script which creates a file in a shared Google Drive folder, this is the script:
var spr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Klantenlijst');
var data = spr.getDataRange().getValues();
var klanNumbers = data; //some var declared before this piece of code
var file = DriveApp.createFile(fileName, JSON.stringify(klanNumbers));
This file needs to be updated frequently, to do so I remove the existing file and create a new one to replace it (with the new data). The problem is that when I try to perform the setTrashed action as a user other than the file owner, this error pops up:
You do not have authorization to perform that action.
Any ideas on how to fix that? :)
Thanks!
EDIT: I can delete the file in drive manually with the other users.
I have seen this article but I totally disagree with the conclusion that the problem is "too localized". Look around on Google and you will find cases with the same problem without a decent solution.
Workaround for this moment:
Rename the file
Move it to another folder
Create the new file in the old folder
I will not delete this post so people can add other ideas here.
You can only trash files that you own. When you delete the file manually (using the GUI to trash the file), it appears that you've trashed the file, but you are not actually setting the trashed flag on it. Rather, you are removing it from view in your own Google Drive, without affecting anyone else. The owner still sees it shared with you, and any other collaborators are unaffected. In fact, you can still see the file if you search for it by its full name, or use one of the alternate views such as the "Recent" file list, or use the file's URL.
To get the same effect from a script, use removeFile().
Here's a utility that will treat a file differently for owners than collaborators, to either trash or remove it.
/**
* Remove the given file from view in the user's Drive.
* If the user is the owner of the file, it will be trashed,
* otherwise it will be removed from all of the users' folders
* and their root. Refer to the comments on the removeFile()
* method:
*
* https://developers.google.com/apps-script/reference/drive/drive-app#removeFile(File)
*
* #param {File} file File object to be trashed or removed.
*/
function deleteOrRemove( file ) {
var myAccess = file.getAccess(Session.getActiveUser());
if (myAccess == DriveApp.Permission.OWNER) {
// If I own the file, trash it.
file.setTrashed(true);
}
else {
// If I don't own the file, remove it.
var parents = file.getParents();
while (parents.hasNext()) {
// Remove the file from the current folder.
parents.next().removeFile(file);
}
// Remove the given file from the root of the user's Drive.
DriveApp.removeFile(file);
}
}
Example:
function test_deleteOrRemove() {
var files = DriveApp.getFilesByName('536998589.mp3');
while (files.hasNext()) {
var file = files.next();
deleteOrRemove( file );
}
}

How do I copy & move a file to a folder in Google Apps Script?

Is there a way to copy and rename a file and move that copy to a particular folder without having a second copy in the root folder? I have used combinations of copy, rename, move in different order, but each time, I still end up with a copy of the renamed file the root drive. Is this by default? It is annoying to say the least.
In the new version of google apps script all you need to acheive your task is the following:
file.makeCopy("new name", folder);
EDIT : I had a look at your original question (before edit) and from there I understood the confusion you made about how files and folders work in Google Drive.
When you make a copy of a file you get a new file in the root folder, when you add this new file to another folder you have to consider that as sticking a label on this file without creating any new copy of it.
You can actually see the file in both the root and the other folder but it is the very same file with 2 different labels! (and you did notice that since you tried to delete it and saw it was deleted in both places)
The only thing you have to do to get the new file in its folder and not shown in the root it to remove the "root label".
That's how Google drive works. And when you think about it and compare to a local hard disk storage it gets logical : when you move a file from one folder to another on the same logical drive you don't move data (ie the bytes on the disk) but simply tell the disk operating system that this file is somewhere else on the map.
So consider Google Drive as a (very) large disk unit with billions on files that you can't move but on which you can stick as many labels as you want ;-) (the most important label being your Google account ID !)
And to play with these labels in the case you describe just try this simple function :
function copyAndMove(file,folder){
var newfile=file.makeCopy('copy of '+file.getName());// here you can define the copy name the way you want...
newfile.addToFolder(folder);// add the copy to the folder
newfile.removeFromFolder(DocsList.getRootFolder());// and remove it from your root folder
}
to test it just use the 2 required parameters : the file object anf the folder object that you can get from many different methods, see the DocsList documentation for details on how to get it if you need to but I guess you already know that ;-)
Yeah, it is a bit odd that Google does not provide a move method. But, considering how drive works and that a file can belong to multiple folders, it makes some sense that you have write your own moves. Here is a simple move function I wrote:
// Moves a file to a new folder by removing all current
// parent folders and then adding the new one.
function moveFileTo(fileObj, folderObj) {
// Attempt the move only if folderObj has a value.
// Otherwise, the file will be left without a folder.
// (Lost files can be found in the "All items" area.)
if (folderObj) {
var folders = fileObj.getParents();
for (var i = 0; i < folders.length; i++) {
fileObj.removeFromFolder(folders[i]);
}
fileObj.addToFolder(folderObj);
return true;
}
return false;
}
Basically, use it like this:
var file = DocsList.getFileById(fileID);
var folder = DocsList.getFolder('Some folder name');
// Make a backup copy.
var file2 = file.makeCopy('BACKUP ' + Utilities.formatDate(new Date(), Session.getTimeZone(), 'yyyy-MM-dd') + '.' + file.getName());
// Move the backup file.
if (moveFileTo(file2, folder)) {
...
I should note that this simple function is exactly that... simple. It assumes you are okay with whacking all parent folders -- owned or shared. This can have unexpected consequences with shared files in various shared folders of various users if you are not careful. It does not appear, however, to remove parent folders that are not shared with the user of this script -- which is good. Anyhow, one obvious alternative to control things better would be to specify the "from" folder as well as the "to" folder.