How to delete a specific folder in Google Drive using Google AppScript - google-apps-script

If you're just trying to delete a folder, nothing fancy, you can use this.
var folderIteratorName = DriveApp.getRootFolder().getFoldersByName("Folder Name"); // folder iterator
folderIteratorName.next().setTrashed(true);
Background info follows.
The DriveApp getFolder methods return a Folder Iterator, whose documentation gives you only three methods. What's not mentioned in there is that you must invoke the .next() method, even though the iterator has only one item in this case, to actually get the folder, which you can then invoke setTrashed(true) on to get rid of it.
I couldn't find an answer to this simple question for two days until I ran across a bit of code by ScampMichael in a tangentially related question.
function myFunction() {
var files = DriveApp.getFilesByName('data');
while (files.hasNext()) {
files.next().setTrashed(true);
}
}
The documentation for Folder lists removeFolder(child) as an option but it isn't made clear how to actually invoke this as there are few examples for any of the DriveApp, Folder, or FolderIterator classes provided in the documentation.
DriveApp.removeFolder("folder name");
Does not work either because removeFolder expects a 'child'. Conceptually most programmers know what a child is but finding documentation for what the 'child' data structure is or how precisely to specify it for that method isn't made clear in the documentation, nor are any examples provided.
If anyone has a more elegant solution please share. I've made this question and answer mainly for others with a similar problem to mine to find just so they have at least one method that works.

You don't need to use getRootFolder().
Other than that, there is no better, faster way to delete a folder with DriveApp. In Google Drive, you can name files and folders the exact same name. You can have 100 folders all named "My Folder". You can have 500 files named "Untitled spreadsheet". This is why there must be a folder iterator and file iterator.
function deleteFolder(folderName) {
if (folderName===undefined) {
folderName = "Test"
};
var folderToDelete = DriveApp.getFoldersByName(folderName).next();
var returnedFolder = folderToDelete.setTrashed(true);
Logger.log('returnedFolder is trashed: ' + returnedFolder.isTrashed());
};

Related

Issues reading shared folder using DriveApp when other users run the Apps Script

I've got a Google Sheet that uses App Script to read data from an external spreadsheet saved in a shared folder. There are multiple files in that folder (all named the same thing). I have one particular function that reads all files in a given folder and returns the id of the most recent one (which I then use to load the data from that file else where in the script).
The script was created using my corporate account and works perfectly when I execute it myself. However when other users try to run the same script they receive the follow error message:
TypeError: Cannot read property '1' of undefined
at get_latest_file_id(Code:380:21)
at get_timesheet_data(Code:19:12)
at unitTest(Code:78:3)
The error is obviously related to the fact that result[0][1] is undefined, however I'm not sure why. I suspect it has to do with permissions and my use of DriveApp. I've tested the following things:
I have tried using my personal Gmail account to run the script and read the file stored shared in a folder stored on my corporate account, this works fine.
I've tried moving the shared file to my personal account and run the script from my corporate account, this too works fine.
However if I use one of my colleagues accounts it doesn't work regardless if the file is stored on my corporate account (which is part of the same org) or on my personal account. When users run the script I get the usual "this developer isn't verified" and you have to choose to give access the first time the script is being run, but other than that I don't get any error messages that seems to point to permission so I'm not sure how to debug/proceed.
I'd appreciate any direction you could give me and suggestions for possible work arounds.
//RETREIVES THE FILE ID OF THE MOST RECENTLY CREATED FILE WITH A SPECIFIC NAME FROM A SPECIFIC FOLDER.
function get_latest_file_id(foldername,filename) {
var folder = DriveApp.getFolderById('XXXXXXXXXXXXXXXX');
var files = DriveApp.getFilesByName(filename);
var result = [];
while (files.hasNext()) {
var file = files.next();
result.push([file.getDateCreated(),file.getId()]);
}
result.sort(function(a,b){
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(b.date) - new Date(a.date);
});
var id = result[0][1];
// Logger.log('Most recent ID:', id);
return id;// return most recent file IDs
}//ENDS
Try This
function get_latest_file_id(foldername,filename) {
var folder = DriveApp.getFolderById('XXXXXXXXXXXXXXXX');
var files = DriveApp.getFilesByName(filename);
var result = [];
while (files.hasNext()) {
var file = files.next();
result.push([file.getDateCreated(),file.getId()]);
}
result.sort(function(x,y){
var xp = x[0];
var yp = y[0];
return xp == yp ? 0 : xp > yp ? 1 : -1;
var id = result[0][1];
});
}
Adding this answer for better visibility, specially for people that in the future may find this question.
The error TypeError: Cannot read property '1' of undefined means that result[0] is undefined. Because we are only populating the result using push, this means that the array is empty.
The reason for that is that the user doesn't see any files in the folder. Making sure that the users can access the folder and files will fix the issue. In this case, OP invited the users to the folder, which fixed the issue.

Creating a Google Sheet in a specific folder with DriveApp

I want to create a Google Sheet in a specific folder on Google Drive using Google Apps Script. According to the documentation of the File class:
createFile(name, content, mimeType)
Creates a file in the current folder with the given name, contents, and MIME type. Throws an exception if content is larger than 10MB.
// Create an HTML file with the content "Hello, world!"
DriveApp.getRootFolder().createFile('New HTML File', '<b>Hello, world!</b>', MimeType.HTML);
And according to the MimeType documentation:
Enum MimeType
An enumeration that provides access to MIME-type declarations without typing the strings explicitly. Methods that expect a MIME type rendered as a string (for example, 'image/png') also accept any of the values below, so long as the method supports the underlying MIME type.
There is no mention of createFile supporting only a subset of available MIME types. The page includes a table, which includes this MIME type:
GOOGLE_SHEETS Enum Representation of MIME type for a Google Sheets file.
My issue lies in implementing this method of creating a file as described in the File documentation.
var folder = DriveApp.createFolder('new folder');
var ss = folder.createFile('new sheet', '', MimeType.GOOGLE_SHEETS);
This will throw an error, stating
Invalid argument: file.contentType (line 2, file "Code")
Replacing MimeType.GOOGLE_SHEETS with the string application/vnd.google-apps.spreadsheet obviously did not help. I am aware of this question, but the accepted answer simply admits defeat and uses a common messy workaround by creating the file in SpreadsheetApp, copying it, and deleting the original. This question has a similarly disappointing accepted answer to the same problem, except for Google Docs. The SpreadsheetApp workaround is fine, but I'm hoping 3 years on that someone holds the answer as to how to use createFile properly.
The Google issue tracker has a long, long history of this question being asked. Way back in 2014, a Google staff member declared that it was a resolved issue, and they had decided that MimeType.GOOGLE_-type files could not be created by the createFile method.
Marked as fixed.
After extensive consideration, we have determined that DriveApp.createFile() should not be used to create MimeType.GOOGLE_* files.
Shortly, attempting to do so will fail and result in a more descriptive error message.
Unfortunately, despite promising otherwise, the error message was never changed and the documentation was never updated to reflect this "extensive consideration".
The correct solution to add a MimeType.GOOGLE_-type file to a specific folder with 1 server call (no copying, removing, or updating) is
var folder = DriveApp.createFolder('folder');
var file = {
title: 'New Sheet',
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{id: folder.getId()}]
}
ssId = Drive.Files.insert(file).id;
Drive.Files.insert does require the Advanced Drive Service, which you can enable on your script with this guide.
Try this:
function createFolderAndFile() {
var folder = DriveApp.createFolder('RootSub');
var file = SpreadsheetApp.create('RootSubSheet');
Drive.Files.update({"parents": [{"id": folder.getId()}]}, file.getId());
}

Inserting Image into Google Sheets from Google Drive using Apps Script

I have been trying for 9 days to add an image that is uploaded to my drive (via the use of a google form) into my Google sheet using Apps Script, but it isn't working and I have no idea why, this is my code below:
function getImage(){
var folderImage = DriveApp.getFolderById("0B7gxdApLS0TYfm1pRHpHSG4yTm96bm1PbTZQc1VmdGpxajY4N1J4M1gtR1BiZ0lOSl9NMjQ");
Logger.log(folderImage.getFiles().next().setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW));
Logger.log(folderImage.getFiles().next().getSharingAccess());
Logger.log(folderImage.getFiles().next().getSharingPermission());
var imageFile = folderImage.getFiles().next().getBlob();
detailSheet.insertImage(imageFile, 1, 13);
}
I have even tried making the sharing and access permissions of the to be as open as possible but I keep getting this error message:
"We're sorry, a server error occurred. Please wait a bit and try again"
I find the error message ambiguous which leads me at a dead end. Usually the message gives me a good idea of where I have gone wrong.
I believe my code is correct, and during my research I have found no definitive reason this shouldn't work. Does anybody know where I am going wrong?
A solution would be great but preferably a critique on my code so I can learn :)
Couple of issues with your script:
You never bind to a specific file, so to work with the same file you have to reinitialize the iterator each time.
You don't verify its mimetype prior to using it as an image
An example that resolves those issues:
function addFolderPNGs_(sheet, folderId) {
const folder = folderId ? DriveApp.getFolderById(folderId) : DriveApp.getRootFolder(); // scope only to the root or given folder.
const imgs = folder.getFilesByType(MimeType.PNG);
var targetRow = sheet.getLastRow();
while (imgs.hasNext()) {
var img = imgs.next();
Logger.log(img.getName())
sheet.insertImage(img.getBlob(), 1, ++targetRow);
}
}
References
getFilesByType is either folder-specific (above) or operates on all of Google Drive
DriveApp
Folder
MimeTypes
Sheet#insertImage

Open a file from Google Sheets script

I've had a Google Sheets script running for some time (a year) that needs to read an HTML file from it's Google Drive directory. The code to open the file looks like this:
var myHtmlFile = UrlFetchApp.fetch("https://googledrive.com/host/0B1m........JtZzQ/myfile.htm");
... and I could use the HTM file for further parsing.
Suddenly, the code above is throwing an error 404.
Has anything changed recently, stopping me from opening the file?
After a discussion with 'azawaza' (thanks for all the tips), I have finally solved this, so I'm posting the resolution in case others fall into this.
It looks like the construct
https://googledrive.com/host/{public_folder_id}/myfile.htm
in UrlFetchApp.fetch(url, true) can no longer be used. It gives error 404.
I was getting it from the following construct (for simplicity, assuming there is only one parent folder of my spreadsheet):
...
var myId = DocsList.getFileById(SpreadsheetApp.getActive().getId());
var folderId = myId.getParents()[0].getId();
var url = "https://googledrive.com/host/" + folderId + "/myfile.htm";
// url looks like: https://googledrive.com/host/0B1m....JtZzQ/myfile.htm"
var httpResp = UrlFetchApp.fetch(url,true); //throws 404 !!!
// now, parse 'httpResp'
The solution that worked for me, is to find the file directly using this construct (again, assuming there is only one file of given name) :
var htmlCont = DocsList.find("myfile.htm")[0].getContentAsString();
// now, parse htmlCont
I don't know why the 'old' solution no longer works. As I mentioned it worked for a year.
UPDATE (May 2015)
The 'DocsList' has been deprecated, a new construct:
var files = DriveApp.getFilesByName(myURL);
if (files.hasNext()) {
var htmlCont = files.next().getBlob().getDataAsString()
}
has to be used instead
I find it strange that it ever worked before! If it did, it was probably a bug - pretty sure it was never intended to work like that with "local" files... I have never seen it mentioned anywhere that UrlFetchApp.fetch() can fetch "local" files like that.
A simple fix would be to just use proper full url of the file:
var myHtmlFile = UrlFetchApp.fetch("https://googledrive.com/host/{public_folder_id}/myfile.htm");
That will ensure your code complies with the API and does not break next time Google changes something.

Script to automatically make a copy of a Google Document for editing

I feel like a total noob posting here. I know CSS, HTML, and XML pretty well but have always avoided JS. I know very little javascript and recently started a Lynda.com course to catch up. Sorry for my ignorance. As such, I am really struggling learning Google Apps Script. Obviously, I need to learn JS before I can make sense of any of it.
The school I work for (5000 students) has set up an online curriculum. I created the curriculum in the form of thousands of google document worksheets. These worksheets are linked on various websites.
The problem we are facing is that when students open the documents, they have to make a copy of them before they can edit them (I of course don't want them to be able to edit the originals). This really sucks for students using mobile browsers on their tablets as making a copy in Google Docs doesn't really work well when using the desktop UI on mobile devices.
I know this kind of thing can be automated with script. I've looked here, and low and behold, it works! I'm pissing my pants with joy as I've been searching for such functionality for three years. (Yes, I know that's sad).
So, what I'm asking is, would anyone be willing to help a noob figure out how to adapt this code so that students click a button on a website lesson and it automatically makes and opens a copy of the worksheet in a new tab?
/**
* Copy an existing file.
*
* #param {String} originFileId ID of the origin file to copy.
* #param {String} copyTitle Title of the copy.
*/
function copyFile(originFileId, copyTitle) {
var body = {'title': copyTitle};
var request = gapi.client.drive.files.copy({
'fileId': originFileId,
'resource': body
});
request.execute(function(resp) {
console.log('Copy ID: ' + resp.id);
});
}
Spending all day yesterday learning Javascript, I've still got a long way to go. Not sure how long it'll take for me to be able to figure this out on my own.
You can certainly do this with Apps Script. Only takes a couple of lines. In fact, you can use just the version I wrote below.
Here is how I would do it -
Ensure you original document is at least read enabled for the folks that will be accessing it.
Grab the fileId from the URL -
Write a web app in Apps Script with the following code -
function doGet(e) {
//file has to be at least readable by the person running the script
var fileId = e.parameters.fileId;
if(!fileId){
//have a default fileId for testing.
fileId = '1K7OA1lnzphJRuJ7ZjCfLu83MSwOXoEKWY6BuqYitTQQ';
}
var newUrl = DocsList.getFileById(fileId).makeCopy('File copied to my drive').getUrl();
return HtmlService.createHtmlOutput('<h1>Open Document</h1>');
}
Deploy it to run as the person accessing the app.
One key thing to remember is that a web app built by Apps Script cannot force open a new window automatically. Instead we can show a link which is clickable into the document in edit mode.
You can see it in action here (will create some dummy file) -
https://script.google.com/macros/s/AKfycbyvxkYqgPQEb3ICieywqWrQ2-2KWb-V0MghR2xayQyExFgVT2h3/exec?fileId=0AkJNj_IM2wiPdGhsNEJzZ2RtZU9NaHc4QXdvbHhSM0E
You can test this by putting in your own fileId.
Since DocsList is deprecated, currently you can make a copy of a file using the following code:
File file=DriveApp.getFileById(fileId).makeCopy(fileName, folder);
where fileId can be obtained as explained in the answer by Arun Nagarajan.
Update as of 2015, Google Script removed the fileId for reasons unknown. The previous method of appending "/copy" to the URL of the Google doc has been re-enabled. Ex) https://docs.google.com/document/d/1GTGuLqahAKS3ptjrfLSYCjKz4FBecv4dITPuKfdnrmY/copy
Here is the code to do it properly (works in 2019,2020,2021):
/**
* Create custom menu when document is opened.
*/
function onOpen() {
DocumentApp.getUi()
.createMenu('For Students')
.addItem('Make a copy', 'makeACopy')
.addToUi();
}
function makeACopy() {
var templateId = DocumentApp.getActiveDocument().getId();
DriveApp.getFileById(templateId).makeCopy();
}
Here is the code I use to make an auto copy of my google Docs.
function makeCopy() {
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");
// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate
var name = DocumentApp.getActiveDocument().getName() + " Copy " + formattedDate;
// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar
var destination = DriveApp.getFolderById("16S8Gp4NiPaEqZ0Xzz6q_qhAbl_thcDBF");
// gets the current Google Docs file
var file = DriveApp.getFileById(DocumentApp.getActiveDocument().getId())
Add a trigger and enjoy!
The same code works for google Sheets. Only you need to replace
DocumentApp.getActiveDocument().getName()
with
SpreadsheetApp.getActiveSpreadsheet()
You can find more details here.