Creating a Google Sheet in a specific folder with DriveApp - google-apps-script

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());
}

Related

Edit on Google Docs without converting

I'm integrating my system with Google Drive. Everything is working so far, but one thing. I cannot edit the uploaded Word documents without converting them to Google Docs first.
I've read here it's possible using a Chrome plugin:
https://support.google.com/docs/answer/6055139?hl=en
But that's not my goal. I'm storing the file's information on my database and then I just request the proper URL for editing and previewing. Previewing is working fine, but when I try the edit URL it says the file does not exist. If I convert the file (using Google Drive's interface) and pass the new ID it works. I don't want to convert the user's documents to Google Drive because they still use Word as their main editing software.
Is there a way to accomplish this?
This is how I'm doing right now:
public static File UploadFile(FileInfo fileInfo, Stream stream, string googleAccount)
{
var mimetype = GetValidMimetype(fileInfo.MimeType);
var parentFolder = GetParentFolder(fileInfo);
var file = new File { Title = fileInfo.Title, MimeType = mimetype, Parents = parentFolder };
var uploadRequest = _service.Files.Insert(file, stream, mimetype);
uploadRequest.Upload();
file = uploadRequest.ResponseBody;
ShareFileWith(file.Id, googleAccount);
return file;
}
This is the URL for editing (where {0} is the file ID):
https://docs.google.com/document/d/{0}/edit?usp=drivesdk
I know that in order to convert the file I just need to:
uploadRequest.Convert = true;
But again, that's not what I want. Is it possible?
Thanks!
EDIT
Just an update. Convert = true should've worked but it's not. I've raised an issue for that here https://github.com/google/google-api-dotnet-client/issues/712
Bottomline, it only works if I open the file on Google Docs and then use its Id...

How to delete a specific folder in Google Drive using Google AppScript

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());
};

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.

Opening specific MimeTypes with google picker

I am working with realtime API and I am making use of realtime-client-utils.
Using existing code, I am creating realtime files with this method:
createRealtimeFile = function(title, callback) {
gapi.client.load('drive', 'v2', function() {
gapi.client.drive.files.insert({
'resource': {
mimeType: rtclient.REALTIME_MIMETYPE,
title: title
}
}).execute(callback);
});
}
and then I am trying to open files with this picker:
var popupOpen = function () {
var token = gapi.auth.getToken().access_token;
var view = new google.picker.View(google.picker.ViewId.DOCS);
view.setMimeTypes(rtclient.REALTIME_MIMETYPE+ "."+realTimeOptions.appId);
var picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.setAppId(realTimeOptions.appId)
.setOAuthToken(token)
.addView(view)
.addView(new google.picker.DocsUploadView())
.setCallback(pickerCallback)
.build();
picker.setVisible(true);
};
Although if I use the setMimeTypes, no documents are found. If I remove that filter, my documents appear normally(along with every time of document in the drive). THe mime type I am using is the default one:
rtclient.REALTIME_MIMETYPE = 'application/vnd.google-apps.drive-sdk';
I am adding the appID as this is how its done on realtime-playground. I also tried without the "." or the appID but no documents are found.
Any suggestions about how to fix the filter ?
You should look for mimeType you created with.
You created your file with mimeType rtclient.REALTIME_MIMETYPE and you're looking for files with mimeType rtclient.REALTIME_MIMETYPE+ "."+realTimeOptions.appId That is the reason why you're not getting any files.
Change filepicker code to:
view.setMimeTypes(rtclient.REALTIME_MIMETYPE);
And make sure you change
rtclient.REALTIME_MIMETYPE = 'application/{{YOURE_CUSTOM_MIMETYPE}}';
to avoid collision with other apps.
Found the answer on an android forum. I had to create files with this mimeType:
REALTIME_MIMETYPE = 'application/vnd.google-apps.drive-sdk.'+appID;
and use same mimeType on view :
view.setMimeTypes(REALTIME_MIMETYPE);
Short answer: correct your appID. It is the first part of your CLIENT-ID. That is, if your client-id is 1088706429537-4oqhqr7o826ditbok23sll1rund1jim1.apps.googleusercontent.com, your APP_ID is 1088706429537. I initially confused it with the project-id and had your problem.
I started new answer because all accepted ones are wrong since they disregard the main issue. Might be they are right advising to change the mime-type to something completely custom. Yet, the most popular answer says that
You created your file with mimeType rtclient.REALTIME_MIMETYPE and
you're looking for files with mimeType rtclient.REALTIME_MIMETYPE+
"."+realTimeOptions.appId
This is seems natural but wrong fact. The topic author is right reproducing the demos. Google demos seem to do exactly that: they create application/vnd.google-apps.drive-sdk file and look for appid-expanded ones. This is correct actually because whenever you create a REALTIME_MIMETYPE=application/vnd.google-apps.drive-sdk file, google drive silently fixes file type! It expands it with .APP_ID. This also means that you do not need to customize your mime-type, as other answers propose. You can proceed with application/vnd.google-apps.drive-sdk, I suppose. I have checked: Google will even fix your mime type if some letters are misspelled, which makes the proposed customization even harder. Google API will take another mime type only if it is too different from REALTIME one. But I am not sure that this is a right thing to do, despite this is proposed by all other accepted answers as 'solution' because I do not know about the difference this implies. Nevertheless,
to to tie up, the reasons to use application/vnd.google-apps.drive-sdk instead of proposed custom types are:
This standard type is proposed by Google in realtime playground demos instead of custom type
Google calls this type 'REALTIME_MIMETYPE'. If your use something different, you use Realtime API to create non-realtime files. At least not that much realtime as proposed by google.
If you specify a different mime type, google still corrects it to REALTIME_MIMETYPE. This means that custom mime types are unwelcomed.
Proponents of customization do not understand anything of this. They even dare to laugh at Google official examples as 'ridiculous'. They basically say that we should not trust them.

How to add a doc to a folder

I am generating a document using Google App Script (specifically a document, not a spreadsheet) and I need to be able to add it to a folder I have called "Test Documents".
I have tried
doc.addToFolder("Test Documents");
However, in debug mode I get the error that the method addToFolder is not found. I'm trying to use this functionality: https://developers.google.com/apps-script/class_file#addToFolder
Could someone give me an example of how I might do this?
The method addToFolder is part of DocsList service, here is an example :
var Doc = DocsList.getFileById('1INkRIviwdjMC-PVT9io5LpiiLW8VwwIfgbq2E4xvKEo');
var gas = DocsList.getFolderById('0B3qSFd3iikE3NWY0dndsMTFZMDQ')
Doc.addToFolder(gas)