Specifying nested folder directly? - google-apps-script

So I use this code to specify the folder in google drive that i want but I'm hoping there is a better way?
var folders = DriveApp.searchFolders("title contains '"+Values[3]+"' and 'jlfXPTjmRlS2dQf5SS3RaUWM....' in parents");
while (folders.hasNext()) {
var folder = folders.next();
var folder = folder.getId();
var subfolders = DriveApp.searchFolders("title contains '"+Values[4]+"' and '"+folder+"' in parents");
while (subfolders.hasNext()) {
var subfolder = subfolders.next(); // got My folder here!
// *Actions*
}}
The folder i want to specify is nested as such:
/shared folder(known by id)/subfolder(known by name)/subfolder(known by name)
My guess is this should be done with DocsList.getFolder(path) but I can't get it to work. Also I need this code to be compatible for collaborators that I have shared the base folder with.

Have a look at Mogsdad's answer in this post, I think it should help you: Migrating from DocsList to DriveApp?
Code is reproduced below, it does not need a lot of explanations : use the path as parameter and get a folder object as return.
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;
}

Related

how to save file to shourcut folder

I am trying to save attachment file from Gmail to Drive
I found a code sample here and its works BUT I need to save the file into the shared folder. at MyDrive I have a shortcut to the shared folder.
for example:
var GDRIVE_FILE = '_sheredFolder_/one/$name
function processThread(thread, label){
var attachments = message.getAttachments();
for(var i=0; i<attachments.length; i++) {
var attachment = attachments[i];
var info = {
'name': attachment.getName(),
'ext': getExtension(attachment.getName())}
var file = createFilename(GDRIVE_FILE, info);
saveAttachment(attachment, file);
}
If you have a folder shortcut in your "My Drive", then it's very likely that the folder is owned by someone else.
In order to be able to add files to a folder owned by someone else you should it should be shared with you as "editor". If it's shared with you as "viewer" you will not able to add the file to that folder.
Related
Moving Files In Google Drive Using Google Script
file.MoveTo doesn't move a file in a shared folder
Well after digging a while, I modify the core to support shortcuts or Shared folders
I add % at the begging of the initial path to identify when its a shared folder or local folder
that's the code:
var GDRIVE_FILE = '%TheSharedFolder/FOLDER/$name';
function getOrMakeFolder(path) {
var folder = DriveApp.getRootFolder();
var names = path.split('/');
while(names.length) {
var name = names.shift();
if(name.charAt(0)==='%')
{
var first = name.substring(1,name.length)
var folders = DriveApp.searchFolders('sharedWithMe');
while (folders.hasNext()) {
var folder = folders.next();
if(folder.getName()===first)
{
Logger.log("folder name is : "+folder.getName()+" folderID: " + folder.getId())
folder = DriveApp.getFolderById(folder.getId())
}
}
continue;
}
if(name === '') continue;
var folders = folder.getFoldersByName(name);
if(folders.hasNext()) {
folder = folders.next();
} else {
folder = folder.createFolder(name);
}
}
return folder;
}

duplicate folder and rename file structure

I'm trying to automate my duplicating folder process in google drive. As part of the process, I want to rename files and folders for each new client.
I've adapted some code found previously. It was previously working well, but for some reason now any folders/files that are more than 1 level deep of the root folder come back "undefined" in the replace section of the command.
function duplicatefolder(){
var newclientname = Browser.inputBox('Client Name')
var sourceFolder = "1. Master Client Folder";
var targetFolder = newclientname;
var source = DriveApp.getFoldersByName(sourceFolder);
var target = DriveApp.createFolder(targetFolder);
if (source.hasNext()) {
copyFolder(source.next(), target, newclientname);
}
}
function copyFolder(source, target,client) {
var folders = source.getFolders();
var files = source.getFiles();
while(files.hasNext()) {
var file = files.next();
var newname= file.getName().toString().replace("Master",client)
file.makeCopy(newname, target);
}
while(folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var newFolderName = subFolder.getName().replace("Master",client)
var targetFolder = target.createFolder(newFolderName);
copyFolder(subFolder, targetFolder);
}
}
The script also creates the folder in the root directory of google drive. Ideally, I'd like it to be created inside the folder "Clients". How would I add this to the script?
Appreciate the help.
Cheers
Please see attached link hope this is it what you are looking for
https://yagisanatode.com/2018/07/08/google-apps-script-how-to-create-folders-in-directories-with-driveapp/

List all files and folder in google drive

I've been trying to figure this out for a while now. I hope I can get some guidance on this. The purpose of the following script is to get a full list of folders and files with subfolders and their files included.
Here is what I currently have:
var counter = 0
var files = folder.getFiles();
var subfolders = folder.getFolders();
var folderPath = folder.getName();
while (subfolders.hasNext()){
subfolder = subfolders.next();
var row = [];
//row.push(subfolder.getName(),'',subfolder.getId(),subfolder.getUrl(),subfolder.getSize(),subfolder.getDateCreated(),subfolder.getLastUpdated());
//list.push(row);
if(counter > 0){
var files = subfolder.getFiles();
}
while (files.hasNext()){
file = files.next();
var vals = file.getUrl();
var row = [];
if(counter == 0){
row.push(folder.getName(),file.getName(),file.getId(),file.getUrl(),file.getSize(),file.getDateCreated(),file.getLastUpdated())
}else{
row.push(folderPath + '/' + subfolder.getName(),file.getName(),file.getId(),file.getUrl(),file.getSize(),file.getDateCreated(),file.getLastUpdated())
}
list.push(row);
}
counter = counter + 1
}
It currently gets the folder names and file names for the current folder and it's subfolder. It doesn't go any further than that. I'm stuck trying to figure out how to get a loop going to continue until there are no more sub-folders.
It isn't a very big drive. There are less than 10 levels but would like the flexibility to go further if needed.
Recursion is beneficial in this case. The code below calls the recursive method recurseFolder() which takes a Folder and Array as a parameter. It adds all the files in the folder to a list, then calls itself on any subfolders it finds.
function test(){
var root = DriveApp.getRootFolder();
var list = [];
var list = recurseFolder(root, list);
Logger.log(JSON.stringify(list));
//This is just how I am testing the outputed list. You can do what you need.
var sheet = SpreadsheetApp.getActiveSheet();
list.forEach(function (row){
sheet.appendRow(row);
});
}
function recurseFolder(folder, list){
var files = folder.getFiles();
var subfolders = folder.getFolders();
while (files.hasNext()){ //add all the files to our list first.
var file = files.next();
var row = [];
Logger.log("File: " + folder.getName());
row.push(folder.getName(),file.getName(),file.getId(),file.getUrl(),file.getSize(),file.getDateCreated(),file.getLastUpdated())
list.push(row);
}
while (subfolders.hasNext()){ //Recurse through child folders.
subfolder = subfolders.next();
Logger.log("Folder: " + subfolder.getName());
list = recurseFolder(subfolder, list); //Past the original list in so it stays a 2D Array suitible for inserting into a range.
}
return list;
}
I'm not sure if the output is formatted how you intended so you might need to play with it a little. Note: It will easily time out if run on a larger Drive.
You need a function that will navigate the folder structure recursively, meaning that if it runs into a subfolder within a folder, it will call itself again passing that folder as a new parent.
function listFolders(parentFolderId) {
var sourceFolder = DriveApp.getFolderById(parentFolderId) || DriveApp.getRootFolder();
var folders = sourceFolder.getFolders();
var files = sourceFolder.getFiles();
while (files.hasNext()) {
var file = files.next();
//Do something
}
while (folders.hasNext()) {
var folder = folders.next();
listFolders(folder.getId());
}
}
Note that this function will still time out if you have lots of files in your Drive, in which case you need to store the state of your app using PropertiesService and schedule the function to run again using triggers via the ScriptApp. You can achieve this by saving the continuation token for your Files Iterator between script executions
More on ContinuationToken

How do I create a Google Drive folder with only a path name

How do I create a folder in Google Drive from only a pathname? Preferably I need the code to not fail if either the folder or any of it's sub-folders already exist.
In the script I'm working in I only have the path name to a folder that I need to create in Google Drive:
newFolder = '/Path/To My/New Folder/';
If I use the following code:
DocsList.createFolder(newFolder);
It will create a folder at the root level called "/Path/To My/New Folder/"
My understanding is the correct syntax would be:
DocsList.createFolder('Path').createFolder('To My').createFolder('New Folder');
However if I only know the path, do not know how many sub-folders the path could contain, and do not know which of the folders/ sub folders exist what code should I use to create the folder from only a path?
Any suggestions much appreciated.
A far more compact, less error-prone way to find or create a folder:
function createFolderFromPathName(fullPathToFolder) {
var paths = fullPathToFolder.split("/");
var curFolder = DriveApp.getRootFolder();
for(var i = 0; i < paths.length; i++) {
var folders = curFolder.getFoldersByName(paths[i]);
curFolder = folders.hasNext() ? folders.next() : curFolder.createFolder(paths[i]);
}
return curFolder;
}
Maybe something like this?
function createFolderFromPathName(newFolder){
var paths = newFolder.split("/");
var curPath = "";
var folders = DriveApp.getRootFolder().getFolders();
var curFolder = DriveApp.getRootFolder();
var folder;
for(path in paths){
var hasFolder = false;
while(!hasFolder && folders.hasNext()){
folder = folders.next();
if(paths[path] == folder.getName()){
hasFolder = true;
}
if(hasFolder){
folders = DriveApp.getFolderById(DocsList.getFolder(curPath+paths[path]).getId()).getFolders();
curPath = curPath == "" ? paths[path] + "/" : curPath + paths[path] + "/";
curFolder = DocsList.getFolder(curPath);
}
}
if(!hasFolder){
curFolder = curFolder.createFolder(paths[path]);
}
}
return curFolder;
}
function test(){
createFolderFromPathName("Folder/Design/Test");
}
Thanks Serge, I figured something like it would be useful later on. I might end up creating a Folder/File library that does tasks similar to these when creating larger scale file/folder architectures.
This is only a comment on the other answer (user1475265), written here for format convenience
this code can be improved (a bit) by returning the folder object from the function so it can be used directly to create a document... test and (very little ) modification below :
function createFolderFromPathName(newFolder){
var paths = newFolder.split("/");
var curPath = "";
var folders = DriveApp.getRootFolder().getFolders();
var curFolder = DriveApp.getRootFolder();
var folder, folderName;
for(path in paths){
var hasFolder = false;
while(!hasFolder && folders.hasNext()){
folder = folders.next();
folderName = folder.getName();
if(paths[path] == folder.getName()){
hasFolder = true;
}
if(hasFolder){
folders = DriveApp.getFolderById(DocsList.getFolder(curPath+paths[path]).getId()).getFolders();
curPath = curPath == "" ? paths[path] + "/" : curPath + paths[path] + "/";
curFolder = DocsList.getFolder(curPath);
}
}
if(!hasFolder){
curFolder = curFolder.createFolder(paths[path]);
}
}
return curFolder;// by returning the folder object it can be used directly, see test function
}
function test(){
var curFolder = createFolderFromPathName("Folder/Design/Test");
var emptyTestDoc = curFolder.createFile('testDoc','empty');// creates an empty text document
}

Creating a zip file inside Google Drive with Apps Script

I have a folder in Google Drive folder containing few files. I want to make a Google Apps Script that will zip all files in that folder and create the zip file inside same folder.
I found a video that has Utilities.zip() function, but there is no API reference for that. How do I use it? Thanks in advance.
Actually it's even easier than that. Files are already Blobs (anything that has getBlob() can be passed in to any function that expects Blobs). So the code looks like this:
var folder = DocsList.getFolder('path/to/folder');
folder.createFile(Utilities.zip(folder.getFiles(), 'newFiles.zip'));
Additionally, it won't work if you have multiple files with the same name in the Folder... Google Drive folders support that, but Zip files do not.
To make this work with multiple files that have the same name:
var folder = DocsList.getFolder('path/to/folder');
var names = {};
folder.createFile(Utilities.zip(folder.getFiles().map(function(f){
var n = f.getName();
while (names[n]) { n = '_' + n }
names[n] = true;
return f.getBlob().setName(n);
}), 'newFiles.zip'));
As DocsList has been deprecated, You can use the following code to zip an entire folder containing files and sub-folders and also keep its structure:
var folder = DriveApp.getFolderById('<YOUR FOLDER ID>');
var zipped = Utilities.zip(getBlobs(folder, ''), folder.getName()+'.zip');
folder.getParents().next().createFile(zipped);
function getBlobs(rootFolder, path) {
var blobs = [];
var files = rootFolder.getFiles();
while (files.hasNext()) {
var file = files.next().getBlob();
file.setName(path+file.getName());
blobs.push(file);
}
var folders = rootFolder.getFolders();
while (folders.hasNext()) {
var folder = folders.next();
var fPath = path+folder.getName()+'/';
blobs.push(Utilities.newBlob([]).setName(fPath)); //comment/uncomment this line to skip/include empty folders
blobs = blobs.concat(getBlobs(folder, fPath));
}
return blobs;
}
getBlobs function makes an array of all files in the folder and changes each file name to it's relative path to keep structure when became zipped.
To zip a folder containing multiple items with the same name use this getBlob function:
function getBlobs(rootFolder, path) {
var blobs = [];
var names = {};
var files = rootFolder.getFiles();
while (files.hasNext()) {
var file = files.next().getBlob();
var n = file.getName();
while(names[n]) { n = '_' + n }
names[n] = true;
blobs.push(file.setName(path+n));
}
names = {};
var folders = rootFolder.getFolders();
while (folders.hasNext()) {
var folder = folders.next();
var n = folder.getName();
while(names[n]) { n = '_' + n }
names[n] = true;
var fPath = path+n+'/';
blobs.push(Utilities.newBlob([]).setName(fPath)); //comment/uncomment this line to skip/include empty folders
blobs = blobs.concat(getBlobs(folder, fPath));
}
return blobs;
}
I was able to use the code that #Hafez posted but I needed to modify it because It was not working for me. I added the first three lines because I needed the folder ID which is a string value and is not the name of the folder.
var folderName = DriveApp.getFoldersByName("<folderName>");
var theFolder = folderName.next();
var folderID =theFolder.getId();
var folder = DriveApp.getFolderById(folderID);
var zipped = Utilities.zip(getBlobs(folder, ''), folder.getName()+'.zip');
folder.getParents().next().createFile(zipped);
function getBlobs(rootFolder, path) {
var blobs = [];
var files = rootFolder.getFiles();
while (files.hasNext()) {
var file = files.next().getBlob();
file.setName(path+file.getName());
blobs.push(file);
}
var folders = rootFolder.getFolders();
while (folders.hasNext()) {
var folder = folders.next();
var fPath = path+folder.getName()+'/';
blobs.push(Utilities.newBlob([]).setName(fPath)); //comment/uncomment this line to skip/include empty folders
blobs = blobs.concat(getBlobs(folder, fPath));
}
return blobs;
}
The only weird thing that I'm experiencing is that when I run the script it says TypeError: Cannot call method "getFiles" of undefined. (line 10, file "Code"). When I happened to look at the place where this script lives there was also 5 zip files that were complete. It works but I still get that error. Weird...but this code works for me. Thanks to everyone on this thread. Cheers!
There's no API reference indeed. You could open an issue request regarding this on Apps Script issue tracker. But deducing from what the code-completion shows, here is my understanding:
var folder = DocsList.getFolder('path/to/folder');
var files = folder.getFiles();
var blobs = [];
for( var i in files )
blobs.push(files[i].getBlob());
var zip = Utilities.zip(blobs, 'newFiles.zip');
folder.createFile(zip);
But I have not tested this code, so I don't know if it will work. Also, it may work only for files not converted to Google's format, or maybe only for those or a subset of it. Well, if you try it out and find something, please share here with us. One limit that you'll sure face is the filesize, it will probably not work if the zip file gets "too" big... yeah, you'll have to test this limit too.
If Hafez solution didn't worked out, and you get this error
TypeError: Cannot read property 'getFiles' of undefined
Try doing this
/**
* Creates a zipFile of the mentioned document ID and store it in Drive. You can search the zip by folderName.zip
*/
function createAndSendDocument() {
var files = DriveApp.getFolderById("DOCUMENT ID CAN BE FIND IN THE URL WHEN A DOCUMENT IS OPENED");
var folder = files;
var zipped = Utilities.zip(getBlobs(folder, ''), folder.getName() + '.zip');
folder.getParents().next().createFile(zipped);
}
function getBlobs(rootFolder, path) {
var blobs = [];
var files = rootFolder.getFiles();
while (files.hasNext()) {
var file = files.next().getBlob();
file.setName(path+file.getName());
blobs.push(file);
}
var folders = rootFolder.getFolders();
while (folders.hasNext()) {
var folder = folders.next();
var fPath = path+folder.getName() + '/';
blobs.push(Utilities.newBlob([]).setName(fPath)); //comment/uncomment this line to skip/include empty folders
blobs = blobs.concat(getBlobs(folder, fPath));
}
return blobs;
}