How to upload a subfolder in shared google drive using python? - google-drive-api

Need to know how to do the same in shared drive.
I was able to create a subfolder in my drive using:
file_metadata = {
'supportsAllDrives':True,
'parents': ['twLBZwLdxfBKTEX0VUUr6'],
'name': "new folderrrrr sub",
'mimeType': 'application/vnd.google-apps.folder',
}
file = DRIVE.files().create(body=file_metadata,
fields='id', supportsAllDrives=True, supportsTeamDrives=True).execute()```

I grasp that you want to upload a folder filled with files in a shared drive. If that isn't your question, please forgive me. To accomplish that you'll need editing permission to be able to write into that shared drive. With that on mind, your operation can be divided into two steps:
First you want to create a folder inside the shared drive in a way similar as this one. As the parents[] you have to use the id property of the shared drive (you can get it by using the LIST method).
After that you'll have to upload every file, one by one, with a code akin this one. You'll need to use the identifier of the folder created on the previous step and the appropriate MIME type.
For both operation you must use the CREATE method with the supportsAllDrives parameter set to true (this requirement is only needed until the first of June of 2020 as described on the linked documentation). You can read here how to set up a working Drive API environment for Python if you don't have one already. If you have any doubt or request additional help, please don't hesitate to comment on my answer.

Related

Providing the respondent with the file upload in the form

I continue to expand my project - the problem described at Selecting a subfolder to upload based on the response has been successfully resolved. Therefore, I am looking for a solution to the following problem. The respondent is to submit the file using the form. How to send him a link to this file (with download permissions but not editing) or send the file itself (e.g. by e-mail).
Folders are created based on the responses from the form, so at the stage of creating the form, I do not know the names and IDs of these folders.
I understand that you already have developed scripts for «emailing a file that is already on Google Drive or for sending a link to such a file», and your goal is to run such scripts when a file is uploaded to a Form. If my understanding of your scenario is correct, then you are very close to reaching your goals.
First of all let me clarify that the files uploaded on Form populate a Drive folder automatically. You can see in your main Drive folder (My Drive) that there is one folder for your form, and inside that folder there is one additional folder for every question in your form that requires a file upload. Please keep in mind that all respondent users will share the same folders.
Knowing the fundamentals discussed above, you only need one additional function to connect the new files in your Drive folder to your already existing scripts for mailing the files. To do so you can set up a form submit trigger that fires your scripts. To connect your scripts with the forms responses folder, you should copy the folder ID into your function. Let me know if you need further clarifications with this approach.

DriveApp.remove(folder) using REST API?

I want to 'hide' a folder from the users Drive root, as it contains mostly junk Google Docs. I don't want to actually trash it, or use the appData storage as I can't then convert things to Google Docs.
In Apps Script, I can call DriveApp.remove(folder) and the file is given no parents at all, not even the root. I've tried calling the create method with "parents": [] but the folder is still created in the root.
Is there a way to make this happen with the REST API?
Figured it out. Supplying an empty parents list means Google will assign the folder to the root of your Drive. The only way to change this is to supply an update request indicating that you want to remove the parents of the folder that fall under the alias root.
In Python, it's as simple as this:
service.files().update(fileId=id, removeParents='root').execute()

How does listing files/folders inside of google drive work?

I have been reading the reference and also used the API explorer, but I cannot for the life of me manage to get a full list of what's inside a specific folder of my google drive.
As far as I understand, the following code should list all files inside the folder that has the given folderID. That does work; but only on files that I created from my app. Folders/files created with or uploaded through the web UI will not show up in the response.
const folders = await drive.files.list({
q: "'<folderID>' in parents",
trashed: false,
spaces: ['drive']
});
In a different post I found that the scope might be the issue, but as far as I know I have already given my app the highest available scope:
const SCOPES = ['https://www.googleapis.com/auth/drive'];
I appreciate any help on this matter. :)
Your symptoms are consistent with using drive.file scope instead of drive. Is it possible that you granted access using drive.file and haven't yet repeated the grant procedure with drive?
The other possible explanation is that you are in two different Drive Accounts. Many people use a Service Account because the Google docs refer to this as server-to-server, without realising that the Service Account is a totally different account to their User Account, ie. the one with the Drive UI. So your app will be uploading and reading files in the Service Account, your UI-created files are in your User Account.

How can I use Google Apps Script to move files between personal Google Drives and Team Drives?

Recently, I gained access to Google Team Drive via Google's Team Drive early adopter program.
I created a Google Docs file called Hello, world!, and then wrote a short Google Apps Script function which uses an addFile() method to update which Google Drive folder the file is attached to:
function move_or_link_file() {
var source = DriveApp.getFolderById("<sourceID>");
var fileiter = source.getFilesByName("Hello, world!");
var dest = DriveApp.getFolderById("<destID>");
while (fileiter.hasNext()) {
var file = fileiter.next();
dest.addFile(file);
}
}
Typically, a Google Drive folder has a URL which matches the following pattern: https://drive.google.com/drive/folders/<alphanumericID>. Although it's perhaps a bit inelegant, I can test my code under various operating scenarios and conditions by simply opening different combinations of Google Drive folders in a web browser, selecting the <alphanumericID> portion of the folder URL, and then manually copy-and-pasting values for this string into <sourceID> and <destID>.
After testing, I am able to identify four different input conditions which result in three distinct behaviors:
Case 1: <sourceID> and <destID> are both folders in my personal Google Drive:
In this case, the script behaves in effect as if it's creating a symbolic link: the Hello, world! file now appears in both directories. Note that it really is the same file, not two identical copies: for example, if I open the document, then the document URL, like the folder URLs, also follows a pattern: https://docs.google.com/document/d/<documentID>/edit. I can tell the file is the same because when I open it, the URL for the document shares the same <documentID>, regardless of which parent folder I use to access it.
For case 1, I can also get the script to behave more like a mv command by simply appending an additional line, source.removeFile(file); to the end of the file iterator loop.
Case 2: <sourceID> is a folder in my personal Google Drive while <destID> is a folder in a Team Drive:
In this case, the script behaves like a mv command by default, rather than as a symbolic link, even without the additional call to the removeFile() method that I mentioned in case 1: i.e., the Hello, world! file simply disappears from my personal drive and reappears in the Team Drive.
Case 3: <sourceID> and <destID> are both folders in a Google Team Drive:
This results in an error message from Google Apps Script: Cannot use this operation on a Team Drive item. (line 7, file "move_or_link_file").
Case 4: <sourceID> is a folder in a Team Drive while <destID> is a folder in my personal Google Drive:
Same error as for Case 3.
Now here is the really weird part: the GSuite graphical user interface (i.e., what you are using when you access Google Drive files and folders via the web browser) offers a Move command via a popup window that appears when you right-click on a file. This GUI version of the Unix-like mv command behaves identically for all four of the above cases: it doesn't matter whether you are moving a folder back and forth between a personal drive or team drive, or internally within a drive, it works correctly and moves the file to where you would expect it to go, every time.
So, I presume it must be possible to implement a mv command via Google's API, somehow, given that they've evidently done it already for users of the GUI interface.
Thus my question: given that it's empirically possible to move files back and forth between arbitrary combinations of folders in personal drives and team drives, how would I actually do it, using only the API calls provided by Google Apps Script?
Also, a bonus question: suppose that, similar to Case 1, instead of moving a file between two different folders in the same Team Drive, I actually wanted to create a symbolic link attaching the file to both folders--how would I use the Google API to do that as well? (I.e., how can I get Case 3 to behave more like Case 1?)
The Google Team Drives only recently started allowing scripts in general. I would imagine the file move you were able to achieve in case 2 is not even intended. There are still several limitations on the team drive (for example you cannot move folders).
For Case 1 I can simply point out that your script is not actually a move command. You should actually imagine Google Drive folders as Gmail tags. A file can have no folders at all. Your script merely assigns a tag to the file and as such it can appear in many folders (just like an email can have many Gmail tags and appear in each tags "folder").
It works in case 2 because Team Drive is a seperate entity from your personal drive. In essence, when you added it to the team drive you had to give up the ownership of the file. As far as I have seen, team drive considers that adding a file to it means that it should be removed from all other parents. I would assume that is why in cases 3 and 4 you cannot move any items. The owner is the team drive itself, however the commands are being sent as a regular Gsuite user.
Drive REST api was recently (~begining of March) updated to work with team drives: https://developers.google.com/drive/v3/web/about-teamdrives so I believe that technically what you are looking for can be done, however considering there are still several limitations on team drives, I don't think it will be documented as well as it could be.

Set file permissions with Google App Script

I regularly need to change file share permissions on large numbers of files on Google Drive. I would rather not have to do this manually on a file by file basis. I have no issues extracting the File IDs, but can't seem to find any information associated with setting permissions for specific users.
Suppose I have a file with ID - 1132asdfasdf5sdf564sdf
Suppose I have a user named John Smith whose email address is jsmith#email.com and this user is a Google user.
Here's a sample script:
function myFunction() {
// this grabs the ID of the source file
var sourcefile=DriveApp.getFileById("1132asdfasdf5sdf564sdf");
}
Question. Once I grab the file by ID, is it possible to set either edit or view permissions for this specific user?
I haven't tried this, but I would imagine you could use addviewer(email) or addeditor(email). There is also an option for multiple emails.
source: https://developers.google.com/apps-script/reference/drive/file#methods
You can try to use the Drive Service. This service allows scripts to create, find, and modify files and folders in Google Drive.
// Log the name of every file in the user's Drive.
var files = DriveApp.getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName());
}
By using the enum permission, that represents the permissions granted to users who can access a file or folder, besides any individual users who have been explicitly given access. These properties can be accessed from DriveApp.Permission.
VIEW - Users who can access the file or folder are able only to view it or copy it. Passing this value to File.setSharing(accessType, permissionType) throws an exception if the type of file does not support it.
EDIT - Users who can access the file or folder are able to edit it. Unless File.setShareableByEditors(shareable) is set to false, users can also change the sharing settings. Passing this value to File.setSharing(accessType, permissionType) throws an exception if the type of file does not support it.
Try also to check this SO question for more information.
Are you replacing old users with new ones in the permission scheme? If you are, you need to invoke the File or Document class methods removeEditor(s) or removeViewer(s), and then follow up with addEditor(s) or addViewer(s).
The problem with using built in methods (as far as I can tell) is that it is difficult to stop a blizzard of email notifications going out to the users if you are doing bulk permission inserts. You can use the flexibility of the Drive API to specifically stop email notifications, but may experience with calling the Drive API is that it eats up a lot of time.