Previously working Google App Script now produces permission error - google-chrome

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?

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.

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

FormResponse object missing when using DriveApp and Smartsheets Sync

My ultimate goal is to access the contents of a file uploaded via a Google Form from within a function triggered by formSubmit. I added some info in a comment to this question, but I think I need to update the question itself. When I deactivate the Smartsheets Sync add-on in the web form, this all works as expected. My theory is that the Smartsheets Sync add-on is not preserving the Event object in certain scenarios.
I began with:
function onFormSubmit (e) {
Logger.log (e);
}
I set up my trigger and tested a form submission, and in the log, I saw:
[<datetime>] {authMode=FULL, source=Form, response=FormResponse triggerUid=<id>}
as expected. I also explored the FormResponse object and verified that a valid Google Drive ID is in the response.
Next, I added a call to DriveApp.getFileById:
function onFormSubmit (e) {
Logger.log (e);
var responses = e.response.getItemResponses ();
var file = DriveApp.getFileById (responses [1].getResponse ());
Logger.log (file);
}
Resubmitting a form brings up a permission error with DriveApp. Not surprising, so I ran onFormSubmit directly from the script editor. It failed because it was invoked without an Event object, but it did invoke the dialog that allowed me to grant DriveApp permissions.
Now, when I submit a form, the Event object doesn't contain a FormResponse object. From the log:
[<datetime>] {authMode=FULL, source=Form, triggerUid=<id>}
So, does granting DriveApp permission somehow revoke permission to inspect the user's response? Alternatively, is there another way for me to use Google App Script to access a file uploaded via a Google Form?
The file ID is put into the file upload answer (response). The following code gets the answer to the file upload question, which is the file ID. Note that arrays are zero indexed, so the first question is at index zero. This code assumes that the file upload question is the very first question.
If this answers your question, you can mark it as correct by clicking the green arrow.
function onFormSubmit(e) {
var file,fileID,form,responseID,submittedResponse,uploadResponse;
responseID = e.response.getId();//The the ID of the current reponse
Logger.log('responseID: ' + responseID)
form = FormApp.getActiveForm();//Get the Form that this script is bound to
submittedResponse = form.getResponse(responseID);//Get the response that
//was just submitted
uploadResponse = submittedResponse.getItemResponses()[0];//This assumes
//that the very first question is the file upload
fileID = uploadResponse.getResponse();//Get the file ID of the file just uploaded
Logger.log('fileID: ' + fileID)
file = DriveApp.getFileById(fileID);//Get the file
Logger.log('file.getName(): ' + file.getName());//verify that this is
//the correct file - and that the code is working
}

Why Google Apps Script hangs on calling next() function

Today I can't run scripts connected to a specific Google Spreadsheet which contains .next() function calls, which I use to access other Spreadsheets by name. It shows Running script Cancel Dismiss message at the top of spreadsheet until exceeding maximum execution time. The strange thing is that I have similar files for every day with exactly same scripts, and the one for yesterday works fine. I created MWE below. So, when I run it I can get the alerts for the first 2 messages, but not the 3rd one.
function test_script_hanging() {
var ui = SpreadsheetApp.getUi();
ui.alert("before getting files by name");
var getFilefile = DriveApp.getFilesByName("file");
ui.alert("after getting files by name");
var getID = getFilefile.next().getId();
ui.alert("after getting Id");
}
In some situations I notice an error message saying something like "Authorization is needed to perform this action" on the line where the .next() function is being called. I tried to revoke authorization of the file and give authorization again, but that didn't help. I tried to give full access to a given script, but couldn't google the way to do it.
Maybe I can add some more functions in my script to require full access during authorization, and maybe that will help to run my main function.
My questions are:
Why the script hangs?
How to fix this?
Can such kind of things (randomly and without any notice) happen in a free version of Google account?
This works for me:
var fldr = DriveApp.getFolderById(folderID)
var file = fldr.getFilesByName(filename);
while(file.hasNext())
{
var fi = file.next();
if(fi.getName() == filename)
{
var id=fi.getId();
}
}

Strange permission issue with getOwner method in Google Apps Script

When using this line of code in a Google Apps Script
var user = folders[n].getOwner().getEmail()
I get an error saying I am not authorized to perform such an action (your version may vary, I am translating from italian).
What gives? I am just retrieving an information, such as the owner of a folder.
When the script processes a folder I own, the error does not arise, the error arises when it encounters a folder not of mine. The matter is that this line of code is just for spotting folders which are not of mine, to avoid issuing method that would correctly rise an error, like setTrashed. The script looks for empty folders to delete them, but I cannot delete folders I am not the owner of of course. And yes I am into Google apps for business, does it make some difference?
There isn't any specifc warning about file.getOwner().getEmail(), but there is for Class Session.
In limited-privilege executions (such as in response to onOpen or
onEdit), we only return the identity of the active user if both the
user and the script owner are part of the same domain. This is to
protect the privacy of consumer users, who may not want their email
address exposed.
I have no problem with this in a consumer account.
The following function is an excerpt from a gist I posted for a previous question. It wraps the call to .getEmail() (or getUserLoginId() if you prefer) in a try ... catch block, so it avoids errors for users crossing Apps Domains.
function getFileInfo (file,fileType) {
var fileInfo = {
id: file.getId(),
name: file.getName(),
size: file.getSize(),
type: (fileType == "file") ? docTypeToText_(file.getFileType()) : "folder",
created: file.getDateCreated(),
description: file.getDescription(),
owner: file.getOwner()
}
try {
fileInfo.owner = file.getOwner().getEmail()//.getUserLoginId()
} catch(e)
{
// Possible permission problem
fileInfo.owner = "unknown";
}
return fileInfo;
}
UPDATE: Since this was first posted, something has changed. Now my consumer account encounters the aforementioned error when trying to access getOwner() for a file shared from another account. (March 3, 2013)