Google Drive Folders/Files Created Using API Not Visible on Google Interface - google-drive-api

This is rather strange. I used Google Drive API to create a folder in Google Drive and then uploaded a file there. I can retrieve the folder and file using the same API (the code is working fine in all respect). However, when I go to Google Drive Web interface, I can't seem to find the folder or file. The file also doesn't sync to my local drive. Is there a setting in API or elsewhere to set the "visibility" ON?
Thank you in advance.

I had the same issue. Turned out to be permissions. When the file is uploaded by the service account, the service account is set as the owner, and then you can't see the files from the Drive UI. I found the solution online (but can't seem to find it again...)
This is what I did...
It's C#, your question didn't specify. The code you're interested in is the permission stuff after you get the response body after the upload...
FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, "text/plain");
request.Upload();
//Start here...
Google.Apis.Drive.v2.Data.File file = request.ResponseBody;
Permission newPermission = new Permission();
newPermission.Value = "yourdriveaccount#domain.com";
newPermission.Type = "user";
newPermission.Role = "reader";
service.Permissions.Insert(newPermission, file.Id).Execute();
The file was visible on the Drive UI after this. I tried specifying "owner" for the role, like the api suggests, but I got and error saying that they're working on it. I haven't played around with the other setting yet, (I literary did this last night). Let me know if you have any luck with any other combinations on permissions.
Hope that helps

I had the same issue, but this got solved my using a list data type for parents parameter, eg: If one wants to create a folder under a folder("1TBymLMZXPGkouw-lTQ0EccN0CMb_yxUB") then the python code would look something like
drive_service = build('drive', 'v3', credentials=creds)
body={
'name':'generated_folder',
'parents':['1TBymLMZXPGkouw-lTQ0EccN0CMb_yxUB'],
'mimeType':'application/vnd.google-apps.folder'
}
doc = drive_service.files().create(body=body).execute()

While permission issue is the main cause of this problem. What I did to make the folders or files appear after I uploaded it with service account was to specify the parent folder. If you upload / create folder / files without parent folder ID, that object's owner will be the service account that you are using.
By specifying parent ID, it will use the inherited permissions.
Here's the code I use in php (google/apiclient)
$driveFile = new Google\Service\Drive\DriveFile();
$driveFile->name = $req->name;
$driveFile->mimeType = 'application/vnd.google-apps.folder';
$driveFile->parents = ['17SqMne7a27sKVviHcwPn87epV7vOwLko'];
$result = $service->files->create($driveFile);

When you create the folder, you should ensure you set a parent, such as 'root'. Without this, it will be not appear in 'My Drive' and only in Search (Have you tried searching in the UI?)
Since you have already created the folder, you can update the file and give it the parent root as well.
You can test it out using the Parents insert 'Try it now' example.
Put your Folders ID in the fileId box, then in the request body, add root in the ID field.

private void SetFilePermission(string fileId)
{
Permission adminPermission = new Permission
{
EmailAddress = "test#gmail.com", // email address of drive where
//you want to see files
Type = "user",
Role = "owner"
};
var permissionRequest = _driveService.Permissions.Create(adminPermission, fileId);
permissionRequest.TransferOwnership = true; // to make owner (important)
permissionRequest.Execute();
Permission globalPermission = new Permission
{
Type = "anyone",
Role = "reader"
};
var globalpermissionRequest = _driveService.Permissions.Create(globalPermission, fileId);
globalpermissionRequest.Execute();
}

Related

Using addEditor(s) with users outside your workspace

I used appscript to create copies of a document and share them with a particular user, this works fine in that the code creates the document and the user receives an invitation to edit, but I have found that at least some users are not able to edit the document. When I view the sharing options on the individual docs, I can see that they are restricted to my workspace only.
I had the same issue when doing this process manually, but thought addEditor would be able to override this, as the shared Gdrive is not restricted in this way overall (i.e, we can share docs with individuals by adding them as editor, from outside our organisation.) . I do not want to have to individually share >100 docs per week manually, so any fixes/workarounds would be very appreciated.
Some limitations: the doc must be shared to one user exactly, but still be accessible to everyone in my workspace.
All of the users I am sharing the document are external to the organisation.
I tried making the master copy of the file accessible to anyone who had the link, hoping that this would mean they could access the file without manually approving the request, but it looks like this doesn't transfer to the copies. I'm not sure enough of the actual issue to know that this would solve it.
This is how the code looks:
function createCopy() {
let file = DriveApp.getFileById("ID of file to copy");
let sheet = SpreadsheetApp.openByUrl('Sheet with names and emails').getSheetByName("Sheet1");
let range = sheet.getRange('D2:D5');
let email = sheet.getRange('C2:C5').getValues().flat();
let values = range.getValues();
let folder = DriveApp.getFolderById("Destination Folder");
for (let i = 0; i < values.length; i++) {
Logger.log(values[i]);
file.makeCopy(values[i].toString(), folder).addEditor(email[i]);
}
}
From the question
Some limitations: the doc must be shared to one user exactly, but still be accessible to everyone in my workspace.
Try this
file.makeCopy(values[i].toString(), folder).addEditor(email[i]);
by
file.makeCopy(values[i].toString(), folder)
.addEditor(email[i])
.setSharing(DriveApp.Access.DOMAIN, DriveApp.Permission.EDIT)
References
https://developers.google.com/apps-script/reference/drive/file#setsharingaccesstype,-permissiontype

Previously working Google App Script now produces permission error

I cannot find anything about Google having updated policies on April 2022. This script was working without any issues before, then in the middle of the day, permissions needed to be re-added. Even with said permissions granted, Google still says access is denied. It was later discovered that the macro has not ran successfully since March 31st.
var newJobFile = DriveApp.getFileById(newJobID);
newJobFile.AddEditors(['group#gmail.com','admin#gmail.com');
newJobFile.setOwner('admin#gmail.com');
The last line is what generates the error. When debugging, I can see as the variable gets created, I cannot see any information about it. For example, if I add a "Filecreated.getName();" before the line with the error, the variable menu remains blank. I am not sure if this is normal. The "newid" variable is confirmed to have the spreadsheet ID. I can copy it into the URL and it takes me to the page. Why is this suddenly an issue and how can I fix it? I did not have a appscripts.json file before with OAuthScopes and it worked fine then. I've added it with the proper permissions, but it does not change anything. I've added several permission scopes to try to resolve this but none of them do. Any advice?
"oauthScopes": [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/spreadsheets"
]
EDIT: I am still having this issue. Even with everything controlled under one account, I cannot transfer ownership to 'Central_account#gmail.com' due to 'Exception : Access Denied : DriveApp'. I have found just one other person experiencing this same issue on the Google App Script group page. Is anyone else not having issues with transferring ownership starting in April on a regular, non-work account?
EDIT 2: Sorry for not uploading my code earlier, I had to clean it up first.
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Add') //creates toolbar entry to the right of 'Help'
.addItem('Capital project','newProject')
.addToUi();
}
function newProject(){
var dashboard = SpreadsheetApp.getActiveSpreadsheet(); //Stores dashboard into a variable for later
var template = SpreadsheetApp.openById('xxxxxx') //Opens the project sheet template in the backend
var nameEntry = getName(); //Prompts user for the name of the new job file
if (nameEntry == null){
return;
}
SpreadsheetApp.setActiveSpreadsheet(template); //Template is now the active sheet
var newJobID = copySheet(nameEntry); //Creates a copy of the New Project template
var newJobFile = DriveApp.getFileById(newJobID);
newJobFile.AddEditors(['group#gmail.com','admin#gmail.com');
newJobFile.setOwner('admin#gmail.com');
}
function getName(){
var ui = SpreadsheetApp.getUi();
var name = ui.prompt( //Prompts the user for an input name
'',
'?????-? Project Description',
ui.ButtonSet.OK_CANCEL);
var cancelCheck = name.getSelectedButton();
if (cancelCheck == ui.Button.CANCEL || cancelCheck == ui.Button.CLOSE) {
return null;
}
var sheetName = name.getResponseText();
return sheetName;
}
function copySheet(name) {
var activeSS = SpreadsheetApp.getActive();
var newFile = activeSS.copy(name) //Creates a copy of the New Project template
SpreadsheetApp.setActiveSpreadsheet(newFile); //Resets active spreadsheet to the recent copy
activeSS = SpreadsheetApp.getActive();
activeSS.getRange('A1').activateAsCurrentCell();
activeSS.getCurrentCell().setValue(name); //Set cell A1 to the name of the file
var newHyperlink = '=HYPERLINK("' + activeSS.getUrl() + '#gid=15580246",A1)';
activeSS.getRange('A2').activateAsCurrentCell();
activeSS.getCurrentCell().setValue(newHyperlink);
return activeSS.getId();
}
With logs, I can see that everything works as intended except for the .setOwner() method. It returns the Access Denied error. I have checked all google accounts and each has enabled CustomScripts to access their drive. Has .setOwner() been deprecated for non workspace accounts?
I've updated my original snippets to match my code.
Apparently the process to transfer file ownership between consumer accounts has changed. According to this guide, the prospective new owner needs to accept the transfer request. See example below:
And I found this article where you can see that the process was different a few months ago, you could set a new owner immediately without sending invitation.
I tested the setOwner() method with a Google Workspace account and it works as within Google Workspace you can directly transfer file ownership between users within the same organization, then I tested the same script with a Gmail account and tried to set another Gmail account as the new owner and I got the same error message: "Exception: Access denied: DriveApp".
Based on all the information, it seems that this behavior is expected as the process to change a file ownership for Gmail accounts is different now, you can’t set a new owner directly, the person you invite to own the file must accept your request to complete the transfer.
I don't have enough reputation to comment, but I'd second what Lorena Gomez said that it's likely an issue with your OAuthScopes. According to the Apps Script documentation, the setOwner() method on the File class requires the /auth/drive authorization scope.
It looks like you have both the /auth/drive and the /auth/drive.file scope configured, and I think the /auth/drive.file scope, which is narrower, is overriding the broader /auth/drive scope, which is required to call setOwner().
Have you tried removing the /auth/drive.file scope and running the script only with /auth/drive?

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.

How to properly set sharing permissions on a new file in a GAS-based form

I have had some inconsistent results using GAS to set sharing for files created in the script, from an uploaded blob.
Gobally, the GAS script serves an Html form, collects the uploaded file, and makes some logging and processing. It is a heavily modified version of this https://ctrlq.org/code/19747-google-forms-upload-files adapted for report submissions from students.
The only clues I have so far are related to the position of the line that sets the sharing, relative to the lines in which the file is created:
var folder = DriveApp.getFolderById(dropbox);
// Get the blob
var contentType = data.substring(5,data.indexOf(';'))
var bytes = Utilities.base64Decode(data.substr(data.indexOf('base64,')+7))
var blob = Utilities.newBlob(bytes, contentType, filename)
// Create a folder for the file if it does not exist
try{
var subFolder = folder.getFolder(tp);
}
catch(e) {
var subFolder = folder.createFolder(tp);
}
var file = subFolder.createFile(blob) // Create the file
From this point on, strange things happen. I have narrowed down the problem to the actual line that sets the sharing:
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW)
If this is immediately after the file creation, it works only some times. Over 200 reports have been submitted, and ~50% of them are visible to anyone with the link, while the rest are only shared privately.
Whether these permissions are set or not, everything else completes successfully. So i have no error log to know for sure what is going on.
By moving this line to the end of the function, subsequent submissions have properly set the permissions:
// .... processing lines that include logging to a spreadsheet, sending emails, and setting other permissions.
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW)
return "OK";
My guess is that, for some reason, the time between file creation and permission modification affects the effectivenes of the .setSharing() line.
One hypothesis is that the file may be inheriting the parent folder's permissions (which is not public) with "lag". In other words, the permissions set by the .setSharing() function would be overwritten by some obsecure inheritance function behind file creation line:
var file = subFolder.createFile(blob) // Create the file
The "default" permissions must surely be set at some point, but there is no "flush" function, that I know of, to force or wait for this to happen (such as the one used in Google Spreadsheets).
EDIT: i have found a thread where similar "non determinism" occurs, involving permission inheritance from the parent folder (see this issue).
I would like to know how to make sure that permissions are always set correctly.
Cheers
:)

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...