FormResponse object missing when using DriveApp and Smartsheets Sync - google-apps-script

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
}

Related

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?

How to open an url and save a file sent as response (excel) directly to google drive with google script

Hello and thanks for your time.
Below you will find a link (request) for the data/excel file that I would like to save directly to my google drive and in the future import to google sheets:
https://digital.feprecisionplus.com/corppen/en-GB/cp-am/DownloadTool/GetPriceHistoryFile?modelString={%22GrsProjectId%22:%2236800083%22,%22ProjectName%22:%22corppen%22,%22ToolId%22:16,%22LanguageId%22:%221%22,%22LanguageCode%22:%22en-GB%22,%22OverrideDocumentCountryCode%22:null,%22forSaleIn%22:%22%22,%22FSIexclCT%22:%22%22}&filtersString={%22TypeCode%22:%22FA:N6SQ%22,%22FundName%22:%22JLR%20Bond%22,%22BaseCurrency%22:%22GBX%22,%22PriceType%22:1,%22TimePeriod%22:%22%22,%22StartDate%22:%22Mon%20Mar%2001%202021%22,%22EndDate%22:%22Thu%20Mar%2004%202021%22}
I tried google script below ( "my_URL" to be replaced with the link above)
function uploadFile() {
var blob = UrlFetchApp.fetch("my_URL").getBlob()
file = DriveApp.getFolderById("my_FolderID").createFile(blob);
}
but I am getting "Exception: Invalid argument: "my_URL"" response.
Would anyone be able to help me with getting that file saved to drive?
Cheers
Modification points:
When I tested your script using your URL, no error occurs. The XLSX file could be created. But from but I am getting "Exception: Invalid argument: "my_URL"" response., I thought that you might have used https://digital.feprecisionplus.com/corppen/en-GB/cp-am/DownloadTool/GetPriceHistoryFile?modelString={%22GrsProjectId%22:%2236800083%22,%22ProjectName%22:%22corppen%22,%22ToolId%22:16,%22LanguageId%22:%221%22,%22LanguageCode%22:%22en-GB%22,%22OverrideDocumentCountryCode%22:null,%22forSaleIn%22:%22%22,%22FSIexclCT%22:%22%22}&filtersString={%22TypeCode%22:%22FA:N6SQ%22,%22FundName%22:%22JLR%20Bond%22,%22BaseCurrency%22:%22GBX%22,%22PriceType%22:1,%22TimePeriod%22:%22%22,%22StartDate%22:%22Mon%20Mar%2001%202021%22,%22EndDate%22:%22Thu%20Mar%2004%202021%22} as the URL. If it's so, such error occurs.
In this case, please test the URL of https://digital.feprecisionplus.com/corppen/en-GB/cp-am/DownloadTool/GetPriceHistoryFile?modelString=%7B%22GrsProjectId%22:%2236800083%22,%22ProjectName%22:%22corppen%22,%22ToolId%22:16,%22LanguageId%22:%221%22,%22LanguageCode%22:%22en-GB%22,%22OverrideDocumentCountryCode%22:null,%22forSaleIn%22:%22%22,%22FSIexclCT%22:%22%22%7D&filtersString=%7B%22TypeCode%22:%22FA:N6SQ%22,%22FundName%22:%22JLR%20Bond%22,%22BaseCurrency%22:%22GBX%22,%22PriceType%22:1,%22TimePeriod%22:%22%22,%22StartDate%22:%22Mon%20Mar%2001%202021%22,%22EndDate%22:%22Thu%20Mar%2004%202021%22%7D.
{ and } are encoded to %7B and %7D, respectively. (I thought that this might be the reason of your issue.)
When above points are reflected to your script, it becomes as follows.
Modified script:
function uploadFile() {
var url = "https://digital.feprecisionplus.com/corppen/en-GB/cp-am/DownloadTool/GetPriceHistoryFile?modelString=%7B%22GrsProjectId%22:%2236800083%22,%22ProjectName%22:%22corppen%22,%22ToolId%22:16,%22LanguageId%22:%221%22,%22LanguageCode%22:%22en-GB%22,%22OverrideDocumentCountryCode%22:null,%22forSaleIn%22:%22%22,%22FSIexclCT%22:%22%22%7D&filtersString=%7B%22TypeCode%22:%22FA:N6SQ%22,%22FundName%22:%22JLR%20Bond%22,%22BaseCurrency%22:%22GBX%22,%22PriceType%22:1,%22TimePeriod%22:%22%22,%22StartDate%22:%22Mon%20Mar%2001%202021%22,%22EndDate%22:%22Thu%20Mar%2004%202021%22%7D";
var blob = UrlFetchApp.fetch(url).getBlob();
file = DriveApp.getFolderById("my_FolderID").createFile(blob);
}
Before you use this script, please replace my_FolderID with your folder ID.

How to access to the google sheet the short cut ist pointing at?

[![enter image description here][1]][1]I have made a shortcut (new feature of google) of a google sheets.
Now I want to get all the Spreadsheets I have in a directory:
these function does not work anymore to treat all the Spreadsheet: folder.getFilesByType(mimeType) because the 'new' shortcut a new mime type has been introduced.
The ID of the shortcut is different from the original file. You can not access anymore over ID of the files you find in the directory.
What is the solution to select only spreadsheets and spreadsheets
shortcuts in a directory?
How to open the spreadsheet a shortcut is pointing at?
function myFunction() {
var ss=SpreadsheetApp.getActiveSheet();
var idRepertoire=ss.getRange("A1").getValue();
var monRep=DriveApp.getFolderById(idRepertoire);
var fichiers=monRep.getFiles();
while (fichiers.hasNext()) {
var fichier=fichiers.next();
var parentFichiers=fichier.getParents();
while (parentFichiers.hasNext()){
var parent=parentFichiers.next();
var identiteF=fichier.getId();
ss.appendRow([fichier.getName(),fichier.getMimeType(),identiteF,parent.getName()]);
SpreadsheetApp.openById(identiteF).getSheetName("test").getRange("A1"); // causes an error for the shortcut
}
}
}
[1]: https://i.stack.imgur.com/grcin.png
Both of your questions can be solved by using the Drive API.
Explanation
In order to retrieve all the spreadsheets and the spreadsheet shortcuts, you can use the GET request to retrieve the files:
For spreadsheet types:
GET https://www.googleapis.com/drive/v3/files
With the following parameter for the q field
mimeType = 'application/vnd.google-apps.spreadsheet'
For shortcut types:
GET https://www.googleapis.com/drive/v3/files
With the following parameter for the q field
mimeType = 'application/vnd.google-apps.shortcut'
Afterwards, if you want to retrieve the files from the shortcuts above, you can simply do a GET request for each shortcut. So for one shortcut, you can do something like this, where fileId is in fact the id of the shortcut.
GET https://www.googleapis.com/drive/v3/files/fileId
With the following parameters for the fields field
shortcutDetails
After this GET request, you will receive a response in which you can find the mimeType of the shortcut as well as the original id of the file from the shortcut.
{
"shortcutDetails": {
"targetId": "ID_OF_THE_ORIGINAL_FILE",
"targetMimeType": "MIME_TYPE_OF_THE_ORIGINAL_FILE"
}
}
Reference
Drive API Files:list;
Drive API Files:get;
Drive API Files.

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

How to reference GmailApp in a js file other than Code.gs?

Is it possible to reference GmailApp in a my_custom_js.html file, as opposed to the Code.gs file?
The following works when used in Code.gs:
// BEGIN email
// define email recipients
var email_recipient = uploader_email;
// Email subject
var subject = "Form submitted"
// Email body
var body = my_html;
Logger.log(email_recipient);
// Send email
GmailApp.sendEmail(email_recipient, subject, body);
// END email
But it doesn't work when used in a function in my_custom_js.html.
Developer Tools > Console shows error:
Uncaught ReferenceError: Logger is not defined
Uncaught ReferenceError: GmailApp is not defined
The reason I want it to run in the custom script, is that it utilises:
// BEGIN handle form submit
function handleFormSubmit(formObject) {
google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
}
// END handle form submit
And, if I understand that code correctly, updateUrl is only running after a successful operation - so I'd like to send the email from the updateUrl() function (ie after a successful operation).
Perhaps I need to use something like a scriptlet , but for use in js files?
Edit
I'm looking into:
https://developers.google.com/apps-script/guides/html/reference/run
And will see if I can pass through the values to a function defined in Code.gs using the following in my_custom_js.html:
google.script.run.sendNotificationEmail(arg1,arg2);
Edit
That last idea worked, but would appreciate any insight on the original question, thanks.
This week for the first time I have been using Google's realatively new Javascript Client API. Using this api you could trigger your Google apps script to run from any custom website and get a callback with the result from any methods you would normally use with in the appscript. It took a bit of fiddling to get it to work, including setting up things in API console, but it's so cool you can run and call and trigger any appscript from anywhere.