Google apps script strips characters away from postdata - google-apps-script

When posting data to my google spreadsheet web app script, Google strips away the norwegian characters from the postdata. e.postData.contents is correct, but e.parameters is incorrect. See the code example below. When you press send, you will see that e.parameters and e.postData.contents returned by the google script are different.
To reproduce the problem with your own google spreadsheet web app:
Make a copy of my google spreadsheet
In the spreadsheet, select Tools->script editor
In the script editor, select Publish->Deploy as web app
In the dialog that opens, set "Who has access to the app" to "Anyone, even anonymous". Finally, press "Deploy".
You are now told that you need to give permissions to the script. Press "Review permissions". A message tells you that you should only allow this script to run if you trust the developer. Click on "Advanced" and click on the link at the bootom. Click allow.
In the new dialog that appears, copy the address of the "Current web app url".
In the code sample, replace the variable actionscript with the address you copied in step 6.
var actionScript = "https://script.google.com/macros/s/AKfycbxW1qHugD1K4adTjGAEt1KqbcbAn1LlaCoWx6GtlNdsNO_E-rTO/exec";
$(document).ready(function(){
var sendButton = $("#send");
if(sendButton != null)
{
sendButton.click(handleSend);
}
});
function handleSend(event) {
var data = $("#name").val();
console.log(data);
var postData = "name="+data;
console.log(postData);
request = $.ajax({
url: actionScript,
type: "post",
data: postData,
beforeSend: function () {
console.log("Loading");
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
},
success: function (result) {
console.log("success");
var s = "<p>e.parameters=" + result.data + "</p><p>e.postData.contents="+result.postdata+"</p>"
$("#result").html(s);
debugger;
},
complete: function () {
console.log('Finished all tasks');
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!doctype html>
<html lang="no">
<head>
<meta charset="utf-8">
</head>
<body>
<input id="name" type="text" value="GÅGHØHRÆR" size="50"/>
<button id="send">Send</button>
<div id="result">
</div>
</body>
</html>

My final solution so far :) is to just use encodeURIComponent to encode the data data posted to the google script:
encodeURIComponent( data )
On the google script side, e.parameters will decode this correctly, at least for the norwegian characters æøå.
The code snippet at the bottom is updated with encodeURIcomponent, i.e. using the solution described above.
----
Before I came to the conclusion above, I went through the following, which I thought could be of use to others as well:
window.btoa(data) did not encode the norwegian characters "æøå" in a the correct way. I had to do
window.btoa(unescape(encodeURIComponent( data )))
as suggested here: https://www.sumitgupta.net/javascript-base64-encode-decode-for-utf-8unicode-string/
https://www.base64decode.org/ and https://www.base64encode.org/ helped me figure this out as window.btoa("æøå")="5vjl" whereas entering æøå in https://www.base64encode.org/ with UTF-8 encoding gives "w6bDuMOl".
On the google app script side, I had to have this code:
var decodedData = Utilities.base64Decode(e.parameter["name"]);
var decodedDataAsString = Utilities.newBlob(decodedData).getDataAsString();
var actionScript = "https://script.google.com/macros/s/AKfycbwbYukbEejyL4yNlbW7xdfXPVZkZFJ7StxUIrKC/exec";
$(document).ready(function(){
var sendButton = $("#send");
if(sendButton != null)
{
sendButton.click(handleSend);
}
});
function handleSend(event) {
var data = $("#name").val();
var data2 = encodeURIComponent( data );
console.log(data2);
var postData = "name="+data2;
console.log(postData);
request = $.ajax({
url: actionScript,
type: "post",
data: postData,
beforeSend: function () {
console.log("Loading");
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
},
success: function (result) {
console.log("success");
var s = "<p>e.parameters['name']=" + result.data + "</p><p>e.postData.contents="+result.postdata+"</p>"
$("#result").html(s);
},
complete: function () {
console.log('Finished all tasks');
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!doctype html>
<html lang="no">
<head>
<meta charset="utf-8">
</head>
<body>
<input id="name" type="text" value="GÅGHØHRÆR" size="50"/>
<button id="send">Send</button>
<div id="result">
</div>
</body>
</html>

Related

Upload file and past the link into active cell a spreadsheet

Trying to make a script to upload a file and paste the downloaded file's link into the active cell of a google spreadsheet.
After clicking "Upload" in the modal window, the file is not written to Google Drive and, accordingly, the link is not written to the cell
Code.gs
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('File')
.addItem('Attach...', 'showForm')
.addToUi();
}
function showForm() {
var html = HtmlService.createHtmlOutputFromFile('index');
SpreadsheetApp.getUi().showModalDialog(html, 'Upload File');
}
function uploadFile(e) {
var newFileName = e.fileName;
var blob = e.file;
var upFile = DriveApp.getFolderById('*FolderID*').createFile(blob).setName(newFileName);
Logger.log(upFile);
var fileUrl = upFile.getUrl();
var formula = '=HYPERLINK("' + fileUrl + '","' + newFileName + '")';
SpreadsheetApp.getActiveRange().setFormula( formula );
return "Uploaded!";
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_center">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<form id="myForm" >
Select File: <input type="file" name="file" accept="*" /><br>
File name: <input type="text" name="fileName" /><br><br>
<input type="button" value="Upload" onclick="upload(this.parentNode);" />
</form>
<script>
window.onload=func1;
function func1() {
document.getElementById('myForm').addEventListener('submit', function(event) {
event.preventDefault();
});
}
function upload(obj){
google.script.run.withSuccessHandler(close).withFailureHandler(close).uploadFile(obj);
}
function close(e) {
console.log(e);
google.script.host.close();
}
</script>
</body>
</html>
Issue and workaround:
On December 9, 2021, the file object got to be able to be parsed from the Javascript side to the Google Apps Script side with V8 runtime. But, in this case, this can be used for only Web Apps. In the current stage, the sidebar and dialog cannot parse the file object on the Javascript side. Ref I think that this is the reason of your issue. So, in the current stage, it is required to send the file object as the string and the byte array for the sidebar and the dialog.
In this case, in order to achieve your goal using the current workaround, I would like to propose the following 2 patterns.
By the way, I think that in your Google Apps Script, an error occurs at SpreadsheetApp.getActiveCell().setFormula( formula );. Because SpreadsheetApp has no method of getActiveCell(). In this case, I think that getActiveRange() might be suitable.
Modified script:
In this modification, the file object is converted to the byte array, and the data is sent to Google Apps Script side.
Google Apps Script side:
function showForm() {
var html = HtmlService.createHtmlOutputFromFile('index');
SpreadsheetApp.getUi().showModalDialog(html, 'Upload File');
}
function uploadFile(e) {
var blob = Utilities.newBlob(...e);
var upFile = DriveApp.getFolderById('*FolderID*').createFile(blob).setName(e[2]);
Logger.log(upFile);
var fileUrl = upFile.getUrl();
var formula = '=HYPERLINK("' + fileUrl + '","' + e[2] + '")';
SpreadsheetApp.getActiveRange().setFormula(formula);
return "Uploaded!";
}
HTML & Javascript side:
<!DOCTYPE html>
<html>
<head>
<base target="_center">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<form id="myForm">
Select File: <input type="file" name="file" accept="*" /><br>
File name: <input type="text" name="fileName" /><br><br>
<input type="button" value="Upload" onclick="upload(this.parentNode);" />
</form>
<script>
window.onload = func1;
function func1() {
document.getElementById('myForm').addEventListener('submit', function(event) {
event.preventDefault();
});
}
function upload(obj) {
const file = obj.file.files[0];
// --- For your additional request, I modified below script.
const extension = function(e) {
const temp = e.split(".");
return temp.length == 1 ? "" : temp.pop();
}(file.name);
const filename = `${obj.fileName.value}.${extension}`;
// ---
const fr = new FileReader();
fr.onload = e => {
const obj = [[...new Int8Array(e.target.result)], file.type, filename];
google.script.run.withSuccessHandler(close).withFailureHandler(close).uploadFile(obj);
};
fr.readAsArrayBuffer(file);
}
function close(e) {
console.log(e);
google.script.host.close();
}
</script>
</body>
</html>
Note:
In your script, I thought that the modified script might be a bit complicated. So, I posted the modified script as an answer.
Reference:
Related thread.
How do I convert jpg or png image to Blob via a Google Apps Script Web App?

Sharepoint How to change function of new document

I have a document library and whenever I click on the new document (https://imgur.com/a/X4ATVX2) it will show this(https://imgur.com/a/6ExJ0Lr) for me to upload a document. How do I change this, so that it will display this (https://imgur.com/a/2JZvPDc) instead? I want to create a new document set instead of uploading a document file. Please advise.
I've watch some guides on the internet, their new is at this position. (https://imgur.com/a/X4ATVX2)
Add the code below into script editor web part in list view page.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function($){
var listTitle="Test600DL";
var rootFolder=_spPageContextInfo.webServerRelativeUrl+"/"+listTitle;
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listTitle+"')/contenttypes?$select=StringId&$filter=Name eq 'Document Set'",
type: "GET",
headers: {
"Accept": "application/json;odata=verbose",
},
success: function (data) {
if(data.d.results.length>0){
var contentTypeId=data.d.results[0].StringId;
var urlString="ContentTypeId="+contentTypeId+"&RootFolder="+rootFolder;
var $newDocumentLink=$("a[id^='idHomePageNewDocument']");
$newDocumentLink.attr("onclick",$newDocumentLink.attr("onclick").replace("Upload.aspx","NewDocSet.aspx").replace("RootFolder=",urlString));
$newDocumentLink.attr("href",$newDocumentLink.attr("href").replace("Upload.aspx","NewDocSet.aspx").replace("RootFolder=",urlString));
}
},
error: function (data) {
//alert("Error");
}
});
});
</script>

Automated download of file from Drive in Web App?

I'm trying to write a polling web app that checks Google Drive and automatically downloads files without user interaction.
Using ContentService I have managed to get things working when I place the code in the doGet function.
However this only works once and there does not appear to be a way to refresh or reload the page automatically on a timer event.
Using a SetTimeout on the client side javascript I can get a function on the server side to automatically trigger at certain intervals but then I am stuck with what to do with the output from ContentService.
The on Success call back will not accept the output from createTextOutput.
My solution does not not need to be deployed and I'm happy to execute from the editor if that expands my choices.
So once I have the output from createTextOutput on my server side what am I supposed to do with it to get it back to the client in order to cause the file download?
I have included the code if that helps.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
setTimeout(
function ()
{
document.getElementById('results').innerHTML = 'Event Timer';
google.script.run
.withSuccessHandler(onSuccess)
.withFailureHandler(onFailure)
.fetchFromGoogleDrive();
}, 60000);
function onSuccess(sHTML)
{
document.getElementById('results').innerHTML = 'File Downloaded ' + sHTML;
}
function onFailure(error)
{
document.getElementById('results').innerHTML = error.message;
}
</script>
</head>
<body>
<div id="results">Waiting to DownLoad!</div>
id="Fetch">Fetch!</button>
</body>
</html>
function doGet() {
Logger.log('doGet');
return HtmlService.createHtmlOutputFromFile('form.html');
}
function fetchFromGoogleDrive() {
//Logger.Log('fetchFromGoogleDrive');
var fileslist = DriveApp.searchFiles("Title contains 'Expected File'");
if (fileslist.hasNext()) {
//Logger.Log('File found');
var afile = fileslist.next();
var aname = afile.getName();
var acontent = afile.getAs('text/plain').getDataAsString();
var output = ContentService.createTextOutput();
output.setMimeType(ContentService.MimeType.CSV);
output.setContent(acontent);
output.downloadAsFile(aname);
return afile.getDownloadUrl();
}
else
{
//Logger.Log('No File Found');
return 'Nothing to download';
}
//Logger.log('All files processed.');
}
EDIT: Different answer after clarification.
If this is intended to run automated as a webapp what I would do is return the getDownloadUrl and create a new iFrame using that that as the source.
Apps Script
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function getDownloadLink(){
//slice removes last parameter gd=true. This needs to be removed. slice is a hack you should do something better
return DriveApp.getFileById("0B_j9_-NbJQQDckwxMHBzeVVuMHc").getDownloadUrl().slice(0,-8);
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<p id="dlBox"></p>
</body>
<script>
function buildLink(res){
var dlBox = document.createElement("iframe");
dlBox.src = res;
document.getElementById("dlBox").appendChild(dlBox)
}
//automate this as you need
google.script.run
.withSuccessHandler(buildLink)
.getDownloadLink();
</script>
</html>

How to store variable data in success ajax to global variable (and call it in another function)

Here is my code.
<head>
<script>
var myGlobal ={};
</script>
</head>
<body>
$(function ()
{
$.ajax({
url: 'test.php',
data: "",
dataType: 'json',
success: function(data)
{
var vname = data.name;
my_global.push(data.name);
}
alert('The user selected: ' + my_global.newval); // i will use this value(vname value)
I would like to use VNAME value in other function..
Put all the code in the same script tags. What you wrote won't even work. The ajax code will just be displayed as text and not handled as script.

HTML 5 File API - Upload File to Server via JQuery

I have an ASP.NET MVC application. I'm trying to let the user upload files to the server. I need to use the HTML 5 File API. Currently, I have the following code:
<div><input id="filesToUpload" type="file" multiple="multiple" /></div>
<div><input type="button" value="upload" onclick="return uploadFiles();" /></div>
function uploadFiles() {
var files = null; // How do I get the files collection from "filesToUpload" here?
if ((files == null) || (files.length)) {
alert("Please choose at least one file to upload.");
return;
}
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
reader.readAsBinaryString(f);
uploadFile(reader.result);
}
}
function uploadFile(f) {
// TODO: I'm not sure how to finish my actual AJAX request to post the file back to the server.
$.ajax({
url: "/uploadFile",
type: "PUT",
contentType: "application/json",
success: function (result) {
alert("File Uploaded!");
},
error: function (p1, p2, p3) {
alert("Upload Failed");
}
});
}
As you can see from the code, I have some knowledge gaps that I am trying to fill in.
How do I get the files collection from the "filesToUpload" HTML
element via JQuery? The reason I want this approach is because I do
not want to begin uploading until the user actually clicks the
"upload" button I've shown below.
What do I set as the "data" value in my $.ajax attempt?
What should the contentType be in my $.ajax attempt?
Thank you very much for your help!