Upload a file to google drive by an anonymous user - google-apps-script

I use a Google Apps Script for that.
Code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('form.html').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function uploadFile(form) {
var folderId = "folder_id";
try {
var folder = DriveApp.getFolderById(folderId);
var blob = form.picToLoad;
var file = folder.createFile(blob);
return "File uploaded successfully " + file.getUrl();
} catch (error) {
Logger.log(error);
return error.toString();
}
}
form.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1 id="main-heading">Main Heading</h1>
<br/>
<div id="formDiv">
<form id="myForm">
<input name="picToLoad" type="file" /><br/>
<input type="button" value="Submit" onclick="picUploadJs(this.parentNode)" />
</form>
</div>
<div id="status" style="display: none">
Uploading. Please wait...
</div>
</body>
<script>
function picUploadJs(frmData) {
document.getElementById('status').style.display = 'inline';
google.script.run
.withSuccessHandler(updateOutput)
.uploadFile(frmData)
};
function updateOutput() {
var outputDiv = document.getElementById('status');
outputDiv.innerHTML = "The File was UPLOADED!";
}
</script>
</html>
It all works fine when I'm authenticated within my domain (I use G Suite).
However, if I'm logged into Google as another user (e.g. a normal Gmail user) or not logged at all, I still can access the page, but the script doesn't execute properly with the following error in the console:
Error
google.script.run.withSuccessHandler(...).processForm is not a function
at picUploadJs (VM84 userCodeAppPanel:9)
at HTMLInputElement.onclick (VM104 userCodeAppPanel:1)
No additional logs are shown in the Log at the level of Apps Script.
I have deployed the script as:
Execute the app as: Me (and authorised it)
Who can access the app: Anyone, even anonymous
So, I think all should work fine and anyone should be able to upload a file to my drive. Unfortunately that's not the case.
Again, this happens only when I access the script from outside of my domain.
Can anyone see what's wrong?

As it was suggested in the comments by Zig Mandel, making a copy of the script and running it solved the problem. This restarted the process of authorisation so perhaps there was something wrongly initiallised with the permissions.

Related

Download a created Google Doc from a deployed web app

I've created a script that, when deployed as a web app, asks the user for some input, and creates and writes to a Google document.
Apparently, downloading the Google document from the script is not possible, and the next best thing seems to convert the doc and save it in My Drive.
I have tried this very simple approach, inspired by Convert Google Doc to PDF using Google Script Editior
Project is deployed as a web app
Code.gs:
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function editDoc(data) {
let doc = DocumentApp.create("Title");
let body = doc.getBody();
body.setText(data);
docblob = doc.getAs('application/pdf');
docblob.setName(doc.getName() + ".pdf");
let file = DriveApp.createFile(docblob);
}
Index.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form onsubmit="edit()">
<input type="text" id="input">
<input type="submit" id="sub" value="Submit">
</form>
<script>
function edit() {
google.script.run.editDoc(document.getElementById("input").value);
}
</script>
</body>
</html>
As a result, this adds a pdf to my google drive, which should be the pdf form of the created google doc, however it is blank.
I believe your goal as follows.
You want to create new Google Document including the value from the input tag.
And, you want to export the Google Document as a PDF file.
You want to achieve this using Google Apps Script.
Modification points:
I think that the reason of however it is blank is that the Document is not saved.
In order to download the PDF file, I would like to propose to convert the PDF data to the base64 data and set it as the data URL, and then, download it.
When above points are reflected to your script, it becomes as follows.
Modified script:
Google Apps Script side: Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function editDoc(data) {
let doc = DocumentApp.create("Title");
let body = doc.getBody();
body.setText(data);
doc.saveAndClose();
return {
data: "data:application/pdf;base64," + Utilities.base64Encode(doc.getBlob().getBytes()),
filename: doc.getName() + ".pdf"
};
}
HTML & Javascript side: Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form onsubmit="event.preventDefault(); edit();">
<input type="text" id="input">
<input type="submit" id="sub" value="Submit">
</form>
<script>
function edit() {
google.script.run.withSuccessHandler(({data, filename}) => {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = filename;
a.href = data;
a.click();
}).editDoc(document.getElementById("input").value);
}
</script>
</body>
</html>
I thought that in this case, a simple button can be also used instead of the submit button.
In the case of Google Docs, when a blog is retrieved, in the current stage, the blob is the PDF format.
In this modified script, when a text is inputted and a button is clicked, a PDF file is downloaded to the local PC.
Note:
In your script, it seems that Web Apps is used. In this case, when the script of Web Apps is modified, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
Class google.script.run
saveAndClose()
base64Encode(data)

Upload files to Google Drive using Google Apps Script

I have found a video that explains how to upload files to Google Drive and here's the video link
https://www.youtube.com/watch?v=l9atSDs7-oI
And here's the script used
function uploadFiles(url) {
var response = UrlFetchApp.fetch(url)
var fileName = getFilenameFromURL(url)
var folder = DriveApp.getFolderById('1IxMiswEfi67ovoBf8ZH1RV7qVPx1Ks6l');
var blob = response.getBlob();
var file = folder.createFile(blob)
file.setName(fileName)
file.setDescription("Download from the " + url)
return file.getUrl();
}
function getFilenameFromURL(url) {
//(host-ish)/(path-ish/)(filename)
var re = /^https?:\/\/([^\/]+)\/([^?]*\/)?([^\/?]+)/;
var match = re.exec(url);
if (match) {
return unescape(match[3]);
}
return null;
}
function doGet(e){
var html = HtmlService.createHtmlOutputFromFile('index.html')
return html.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
}
/*create index.html on the Google app project and put the below code and remove this comment*/
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<title>Upload Files</title>
</head>
<body>
<h1>Upload files to Google drive from URL</h1>
<form>
<label>Enter the URL</label>
<input type="text" name="myFile" id="url" style="height:5%; width:70%">
<br>
<br>
<input type="button" id="submitBtn" value="Upload Files">
<label id="resp"><label>
</form>
<script>
document.getElementById('submitBtn').addEventListener('click',
function(e){
var url= document.getElementById("url").value;
google.script.run.withSuccessHandler(onSuccess).uploadFiles(url)
})
function onSuccess(url){
document.getElementById('resp').innerHTML = "File uploaded to path" + url;
}
</script>
</body>
</html>
I have changed the folder id in the code. When I tried to use that, I encountered an error Uncaught at uploadFiles (Code:7).
I think that your script works when both the inputted URL and the folder ID is correct in your script. From this situation, please check the following point.
At the Web Apps of Google Apps script, when you use the URL of https://script.google.com/macros/s/###/exec, when the script of Web Apps is modified, the latest script is reflected to the Web Apps by redeploying the Web Apps as new version.
This is the important point for using the Web Apps of Google Apps Script. So I would like to propose to confirm above.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script

Google apps script get user input with no length limit

I want to take user input (HTML specifically) using either:
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Paste HTML below');
or
var input = Browser.inputBox('Paste HTML below', Browser.Buttons.OK_CANCEL);
These work fine for small inputs, however when copying over the entire HTML for a page of interest an error occurs (in each case). This error cannot be caught, it simply crashes the script.
Do you know why this is happening? I can't find anything in the docs that mention limits on input size.
Any experience doing this a different way?
Edit: as per a suggestion in the comments, I have tried another method (below). This also fails (with no error message) when passed large input.
First I set up Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Paste Sitemap Content Below
<textarea id="user-input-box" rows="4" cols="50"></textarea>
<script>
function logToConsole() {
var userInput = document.getElementById("user-input-box").value;
google.script.run.doSomething(userInput);
}
</script>
<input type="button" value="Close" onclick="logToConsole();google.script.host.close();" />
</body>
</html>
Then in file Code.gs
function testDialog() {
var html = HtmlService.createHtmlOutputFromFile('Page')
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'My custom dialog');
}
function doSomething(userInput){
Logger.log(userInput);
}
I just ran into the same problem and couldn't log the error. In my case as is yours, you're calling your logToConsole() function and then directly after you're closing the dialog by using google.script.host.close();
google.script.host.close() is the problem. For some reason it can cancel the script execution - this typically happens when you're sending a lot of data back. The trick is to use a successHandler when you call your script which then calls google.script.host.close(). This way, the data transfer from the dialog finishes correctly and when you call withSuccessHandler(), that callback closes the dialog. Try this amendment to your code:
<script>
function logToConsole() {
var userInput = document.getElementById("user-input-box").value;
google.script.run.withSuccessHandler(closeDialog).doSomething(userInput);
}
function closeDialog() {
google.script.host.close();
}
</script>
<input type="button" value="Close" onclick="logToConsole()" />

Getting the "You do not have permission to call getFolderByID" error, when calling that method in HTML

I am going through the book Google Apps Scripts, 2nd Edition. The lesson I am on for creating a web app asked me to use the following code:
function doGet() {
var html = HtmlService.createTemplateFromFile('index').evaluate()
.setTitle('06 Automating Forms')
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
return html;
}
However, when I run this code I get the "You do not have permission to call getFolderById" error message. I don't understand why I am getting this error message. Also, it says the error is on line 2.
I am calling the "getFolderByID" method in my index.html file. Here is that code:
<div class="body">
<div><h2>This App will allow you to create a form from a template
in Google Docs.</h2></div>
<hr>
<div id="options">
<?var files =
DriveApp.getFolderById('0B6YzuBeNxooXSWE3Vm9WMlVnWkk').getFiles();?>
<select id='template'>
<option value="notSel">Select a Template</option>
<?while (files.hasNext()){
var file = files.next();?>
<option value="<?=file.getId()?>"><?=file.getName()?></option>
<?}?>
</select>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script>
</script>
<style>
.body{
padding: 10px;
}
</style>
Is there a way to get permission to call that method? I thought if I run the code a box pops up and allowing me access to that area of Drive.
Only script files have the ability to ask for authorization, html files don't.
The simplest thing to do is to add a dummy function in your code.gs file like this :
function myFunction() {
DriveApp.getFolderById('0B6YzuBeNxooXSWE3Vm9WMlVnWkk');
}
run it from the script editor and you will get the authorization request as expected.

"getCalendarById" is not defined

What is wrong with my Code.gs that gives me "getCalendarById" is not defined? Also If I add CalendarApp. in front of the getCalendarById method the whole thing breaks and the URL returns nothing but a drive error.
Solution:
I do have to add CalendarApp in front of getCalendarById I read the doc wrong (doc link). In that doc under the section 'Granting Access Rights' I read -
Scripts do not request authorization if they...or if you access the script as a web app that runs under the script owner's user identity.
I was running the script under my own identity but failed to realize that the quote above meant it will not go out and get authorization, not that it still requires you to get it yourself . Many thanks to Serge insas for his great help, sticking with me and understanding.
Code.gs:
function doGet() {
return HtmlService.createHtmlOutputFromFile('index.html').setSandboxMode(HtmlService.SandboxMode.NATIVE);
}
function listEvents(dateSelected) {
var calendar = getCalendarById('en.usa#holiday#group.v.calendar.google.com').getEventsForDay(new Date(dateSelected)); // if I put calendarapp. the whole thing returns an error from the URL
Logger.log('Number of events: ' + calendar.length);
}
html:
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/themes/redmond/jquery-ui.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<div class="container" >
<div>
<h1>Welcome</h1>
<p>Please select a date below.</p>
<p>Click Here: <input type="text" name="date" id="datepicker" /><p>
</div>
</div>
<div id='test'></div>
<script>
$("#datepicker").datepicker();
$("#datepicker").on("change", function () {
var dateSelected = $(this).val()
$("#test").text(dateSelected);
google.script.run.listEvents(dateSelected);
});
</script>
getCalendarById() is a method of the calendarApp service, the correct syntax is as follows :
var calendar = CalendarApp.getCalendarById("xxxxxxxx");
See doc here