Google Forms Upload File - using/modifying the response - google-apps-script

Within a larger process (not relevant here) I would like to use the native Google Forms "File Upload" Question in a form.
I have been trying to understand how to use the response to :
rename the file uploaded
move/copy/save the file to a specific location
(this will be in the end determined by another response item, but for the moment, lets just call it "myDesitinationFolder"
I can of course, in the responses, in the Google interface, or in a Spreadsheet, see the files (and from the spreadsheet, the URL to the file)
but I would like to do my processing before the file is "saved to Drive" or in any case, during the onFormSubmit()
All i find though is a value [randomalphanumeric] and I am unable to find the documentation that explains what this [element] is. (if it is an object - but the square [] would seems to say this is NOT the case)
(Updated after clarification)
the sample code used to get the value comes from Stack.
.
fonction onFormSubmit(e){
var form = FormApp.getActiveForm()
var formResponses = form.getResponses();
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i];
var itemResponses = formResponse.getItemResponses();
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
Logger.log('Response #%s to the question "%s" was "%s"',
(i + 1).toString(),
itemResponse.getItem().getTitle(),
itemResponse.getResponse());
console.log('Response #%s to the question "%s" was "%s"',
(i + 1).toString(),
itemResponse.getItem().getTitle(),
itemResponse.getResponse());
}
}
}
OUTPUT :
Response #19 to the question "fileUpload" was "[1OQeNOTREALCODE5SoAaPiw4s2M-cfRDJ]"
(NOTREALCODE added by me to anonymise response)
my Question : is that a file object? can i access the "save" process to change the destination and the name before hand?
or would my best option be to do a post submit process on an updated sheet (where i have the file URL etc..) (I really dont want to go that way, I would prefer to have it all within the form (I don't need the sheet otherwise)
thanks

When a file upload form is submitted, Google Form response stores the ID of the file in Google Drive. You can use the DriveApp service to access the uploaded file by ID and rename it, move it or copy to another folder.
function renameFile(id) {
var file = DriveApp.getFileById(id);
file.setName("Some name here");
}

Related

How update Google form display after changing file name with script

I'm using a Google form with multiples subpart (Multiple choices, Short answer...) and one upload file box.
When I upload a file, the name of the logged user is added into the file name. I don't want this.
So I use a Google script to change their name, it's ok in the folder directory (the file names are correctly updated), but in the Google form display ("Responses" section), there is always the old names.
How can I force the refresh of the Form display ?
Here is my script :
function myFunction() {
var existingForm = FormApp.getActiveForm();
var formResponses = existingForm.getResponses();
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i];
var itemResponses = formResponse.getItemResponses();
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
var title = itemResponse.getItem().getTitle();
var files = itemResponse.getResponse();
for (var n in files) {
var my_file = files[n];
var dFile = DriveApp.getFileById(my_file);
var dFileName = dFile.getName();
dFile.setName("New name " + n);
}
}
}
}
Thanks for your help
Upon checking the available methods and Form Settings, there is no way to change the file names under Responses even if the actual file in the directory/folder is already renamed.
Even the Forms UI doesn't provide any way of renaming the uploaded file name under Responses so it is highly unlikely to be supported in Apps Script as well.

Trying to set permissions for "anyone with link can view" on file uploaded via form "file upload" section

A "File upload" section is used in a form: I need the form user (not owner) to be able to share the file with someone other than the owner of the form.
It appears the results of the file-upload in the form puts the file into the current form user's Drive and provides a URL that the owner of the form can view. I need that URL to be sharable with others (via the script, not manual intervention).
Triggered by the form input, my script runs:
DriveApp.getFileById(id).setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.NONE);
... where id is derived from the URL provided from the form (see Easiest way to get file ID from URL on Google Apps Script ... as I know I'm getting the ID right).
As just user with a link to the form, I get no errors... nor do I see errors entering in the form as the owner.
But, the URL cannot be seen by anybody but the spreadsheet owner (not even the user who entered and owns the file can view the link)... so DriveApp.Access.ANYONE_WITH_LINK is not working. Non-owners of the form will be running this. I'm guessing setSharing is being run as the owner of the form, not the user, and therefore can't set it to be sharable? Is there a way for the script to make the file/link viewable?
But, even if the form owner runs the form, the URL can't be viewed by others. I don't think the ID changes once the file becomes sharable? So I must be calling setSharing incorrectly?
Maybe the ID in the URL provided by the form is n some "no-mans-land" where it's not the true ID of the file?
Example routine that just goes down column 4 of a spreadsheet with these URLs and makes them sharable:
`
function SetPermissions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var maintsheet = ss.getSheetByName('Maintenance');
var lastmr = maintsheet.getLastRow();
for (var i = 2; i <= lastmr; i++) {
var fileURL = maintsheet.getRange(i,4).getValue();
Logger.log(fileURL);
for (var j in fileURL.split(",")) {
Logger.log(j);
Logger.log(fileURL.split(",")[j]);
var fileID = fileURL.split(",")[j].match(/[-\w]{25,}/)[0]
Logger.log(fileID);
DriveApp.getFileById(fileID).setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.NONE);
}
}
}
`
... run by the owner of the form/spreadsheet, no errors, and the logged data looks correct. But, give the URL to another user, and they get a permissions error.
This fixed it:
function SetPermissions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var maintsheet = ss.getSheetByName('Maintenance');
var lastmr = maintsheet.getLastRow();
for (var i = 2; i <= lastmr; i++) {
var fileURL = maintsheet.getRange(i,4).getValue();
Logger.log(fileURL);
for (var j in fileURL.split(",")) {
Logger.log(fileURL.split(",")[j]);
var fileID = fileURL.split(",")[j].match(/[-\w]{25,}/)[0]
Logger.log(fileID);
var file = DriveApp.getFileById(fileID);
file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
var newURL = file.getUrl();
maintsheet.getRange(i, 4).setValue(newURL);
}
}
}
Doh: https://developers.google.com/apps-script/reference/drive/permission
... I was telling it: no permissions to anybody with the link! VIEW, is the right option!
Here's a function that given the sort of list of URLs the form will stick in the cell (the form can be setup to allow multiple pics per cell), set the permissions, and return the list of URLs back, this time as Google formats the URL in getURL():
function AnyoneWithLinkCanView(urls) {
var newURLs = "";
if ( urls.length > 0 ) {
for each (var j in urls.split(",")) {
var fileid = j.match(/[-\w]{25,}/)[0];
var file = DriveApp.getFileById(fileid);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
if ( newURLs.length > 0 ) {
newURLs += ", ";
}
newURLs += file.getUrl();
}
}
return newURLs;
}
In my case, as the URLs will form the message body of an email, my call looks like:
var message = AnyoneWithLinkCanView(dataSheet.getRange(lastr,i).getValue());
Form inputs run as whoever creates the form trigger. If you want the script to run as her give her access to the script and a macro that creates the form trigger.

Parse XML file (which is stored on GoogleDrive) with Google app script

I have a few XML files stored on GoogleDrive. I would like to transfer data from XML file to Google spreadsheet with google apps script.
Is it possible parse XML file (which is stored on GoogleDrive) with Google apps script?
I post a similar question here :
How to parse a XML file stored in my google drive but which stands out as a html type?
Maybe it can help you if i understand your situation: You have your xml files in google drive and not on extern sites.
First, If XmlFileonDrive is an object repredenting a xml file on your drive
you can retrieve the fileId like this:
var fileId=XmlFileonDrive.getUrl().match(/https:\/\/drive.google.com\/file\/d\/(.*)\/view.*/)[1];
Then you could xmlParsing them like it work now for me :
var data = DriveApp.getFileById(fileId).getBlob().getDataAsString();
var xmlDocument=XmlService.parse(data);
var root=xmlDocument.getRootElement();
var mynamespace=root.getNamespace();
var titleTag=root.getChild("title",root.getNamespace()).getText();
First you must understand how to parse XML data, to get file using apps script. Unfortunately we can't directly get xml files in the google drive. It must be located outside google drive or in an eternal website.
Refer to this site in using apps script parsing xml:
// Log the title and labels for the first page of blog posts on the Google Apps Developer blog.
function parseXml() {
var url = 'http://googleappsdeveloper.blogspot.com/atom.xml';
var xml = UrlFetchApp.fetch(url).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement();
var atom = XmlService.getNamespace('http://www.w3.org/2005/Atom');
var entries = document.getRootElement().getChildren('entry', atom);
for (var i = 0; i < entries.length; i++) {
var title = entries[i].getChild('title', atom).getText();
var categoryElements = entries[i].getChildren('category', atom);
var labels = [];
for (var j = 0; j < categoryElements.length; j++) {
labels.push(categoryElements[j].getAttribute('term').getValue());
}
Logger.log('%s (%s)', title, labels.join(', '));
}
}
Then forward the values to the function that will create a spreadsheet. Here is another useful tutorial.

How To Download / Export Sheets In Spreadheet Via Google Apps Script

The task is to automate the manual process accomplished by the menu option "File | Download As | Plain Text"
I want to be able to control the saved file name, which cannot be done via the menu.
At the time this is invoked, the user would be sitting on the sheet in the spreadsheet. Ultimately, I'd make it a menu option, but for testing I'm just creating a function that I can run manually.
After reading several other threads for possible techniques, this is what I've come up with.
It builds a custom name for the file, makes the call, and the response code is 200.
Ideally, I'd like to avoid the open / save dialog. In other words, just save the file without additional user intervention. I'd want to save in a specific folder and I've tried it with a complete file spec, but the result is the same.
If I copy the URL displayed in the Logger and paste it into a browser, it initiates the open / save dialog, so that string works.
Here's the code as a function.
function testExportSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var oSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sId = ss.getId();
var ssID=sId + "&gid=" + oSheet.getSheetId();
var url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="
+ ssID + "&exportFormat=tsv";
Logger.log(url);
var fn = ss.getName() + "-" + oSheet.getSheetName() + ".csv";
var sHeaders = {"Content-Disposition" : "attachment; filename=\"" + fn + "\""};
var sOptions = {"contentType" : "text/html", "headers" : sHeaders};
Logger.log(sOptions);
x = UrlFetchApp.fetch(url, sOptions)
Logger.log(x.getResponseCode());
}
I have exported a spreadsheet as CSV directly into a local hard drive as follows:
Get the CSV content from current sheet using a variation of function convertRangeToCsvFile_() from the tutorial on this page https://developers.google.com/apps-script/articles/docslist_tutorial#section3
var csvFile = convertRangeToCsvFile_(...);
Then select a drive folder that is syncing to a local computer using Drive
var localFolder = DocsList.getFolderById("055G...GM");
And finally save the CSV file into the "local" folder
localFolder.createFile("sample.csv", csvFile);
That's it.
This app script returns a file for download instead of web page to display:
function doGet(){
var outputDocument = DocumentApp.create('My custom csv file name');
var content = getCsv();
var textContent = ContentService.createTextOutput(content);
textContent.setMimeType(ContentService.MimeType.CSV);
textContent.downloadAsFile("4NocniMaraton.csv");
return textContent;
}
In case you are looking to export all of the sheets in s spreadsheet to csv without having to manually do it one by one, here's another thread about it:
Using the google drive API to download a spreadsheet in csv format
The download can be done. But not the "Write to the hard drive" of the computer.
Write issue:
You mean write a file to the hard drive of the computer, using Google Apps Script? Sorry, but you will need more than GAS to do this. For security reasons, I doubt this is possible with only GAS, have never seen anything like this in GAS.
Google Drive API will let you do a download, just needs OAuth and the URL you gave.
This is the function that I found when looking up the same question. This function was linked to by #Mogsdad and the link no longer exists.
function convertRangeToCsvFile_(csvFileName) {
// Get the selected range in the spreadsheet
var ws = SpreadsheetApp.getActiveSpreadsheet().getActiveSelection();
try {
var data = ws.getValues();
var csvFile = undefined;
// Loop through the data in the range and build a string with the CSV data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// Join each row's columns
// Add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
I found it here and here

Error uploading .xls file and converting to Google Spreadsheet via GAS

Prior Research
Please do not close this question as a duplicate because my question deals with how to resolve the specific error message I am receiving and not the general question of whether my objective is achievable or not — as some other related questions, yielded by my research and below detailed, have asked.
Related questions and why they do not apply here
This question, asked 7/27/2012, does not apply because it: (1) is too old (after 10 months, new solutions/methods might exist) and (2) does not deal with the specific error message I am experiencing.
This question, asked 10/12/2012, fails to apply for similar reasons.
My below code was copied from here which was forked from here. These are presumably, working solutions because they have been referenced as such from other question/answer exchanges here on Stack Overflow.
Objective
Programmatically, I am trying to:
Search my email inbox.
Find Excel (.xls) file attachments.
Upload those .xls file attachments to Google Drive.
While uploading, convert the .xls files into a Google Spreadsheet file format.
Problem
When I execute processInbox() (code shown at the bottom of this question), it fails and I get the error message shown below.
Error Message
Request failed for returned code 403.
Server response:
{
"error":{
"errors":[
{
"domain":"usageLimits",
"reason":"accessNotConfigured",
"message":"AccessNotConfigured"
}
],
"code":403,
"message":"AccessNotConfigured"
}
}
(line 13, file "DriveUpload")
Question
What am I doing wrong? And how can I fix it?
For example, do I need to do something special in my API console relative to setting up my project to, say, access Google Drive or something? What am I missing?
Note: I have not yet successfully implemented oAuth in any of my applications, yet.
Error Source
Line 13
(This is the code line referenced by the error message.)
var uploadRequest = UrlFetchApp.fetch("https://www.googleapis.com/upload/drive/v2/files/?uploadType=media&convert=true&key="+key, params); // convert=true convert xls to google spreadsheet
Code
The complete body of code I am working with is shown below for your reference. I extracted the error-triggering, “line 13,” and highlighted it above to help us focus on the proximate cause of the problem.
DriveUpload.js
function uploadXls(file) {
authorize();
var key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // <-- developer key
var metadata = { title: file.getName() }
var params = {method:"post",
oAuthServiceName: "drive",
oAuthUseToken: "always",
contentType: "application/vnd.ms-excel",
contentLength: file.getBytes().length,
payload: file.getBytes()
};
// convert=true convert xls to google spreadsheet
var uploadRequest = UrlFetchApp.fetch("https://www.googleapis.com/upload/drive/v2/files/?uploadType=media&convert=true&key="+key, params);
var uploadResponse = Utilities.jsonParse(uploadRequest.getContentText());
var params = {method:"put",
oAuthServiceName: "drive",
oAuthUseToken: "always",
contentType: "application/json",
payload: Utilities.jsonStringify(metadata)
};
var metaRequest = UrlFetchApp.fetch("https://www.googleapis.com/drive/v2/files/"+uploadResponse.id+"?key="+key, params)
return DocsList.getFileById(uploadResponse.id);
}
function authorize() {
var oauthConfig = UrlFetchApp.addOAuthService("drive");
var scope = "https://www.googleapis.com/auth/drive";
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oauthConfig.setAuthorizationUrl("https://accounts.google.com/OAuthAuthorizeToken");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
}
function processInbox() {
// get all threads in inbox
var threads = GmailApp.getInboxThreads();
for (var i = 0; i < threads.length; i++) {
// get all messages in a given thread
var messages = threads[i].getMessages();
// iterate over each message
for (var j = 0; j < messages.length; j++) {
// log message subject
var subject = messages[j].getSubject()
//Logger.log(subject);
if ( subject == "with xls attach" ){
Logger.log(messages[j].getSubject());
var attach = messages[j].getAttachments()[0];
var name = attach.getName();
var type = attach.getContentType();
//var data = attach.getDataAsString();
Logger.log( name + " " + type + " " );
var file = uploadXls(attach);
SpreadsheetApp.open(file);
}
}
}
};
Drive API is already built in GAS: https://developers.google.com/apps-script/reference/drive/
Use DriveApp and your problems go away ;-)
This maybe a temp solution
Step 1: Use a Google Form to Collect Data to a Google spreadsheet
Step 2: Add the Zoho Sheet App to your Google Drive
In a Zoho Sheet
Goto
Data Menu
»Link External Data
Select either
CSV
RSS/Atom Feed
or HTML Page
You can schedule it to update at specific time intervals
What I like is the VBA and Macros in Zoho
You can also do Pivot Charts and Tables
You can copy and paste Excel VBA into Zoho !
I have an Unpivot VBA that I will run on my Tabular dataset
before I can do a PivotChart
It is hard to beat all the functionality of Excel and I often fall back on familiar tools !
If I hear of anything I will post it
Good luck