Google App Script and Html Form not communicating - ajax

I need help with Google App Script on a Google Sheet and an Html form. I just cannot get them to connect properly. The form has a text box and two buttons. The user enters a name in the text box and press the start button. The start button records time to a variable. When finished, the user presses finish. The finish button records the time and processes the text box and start button. This info is sent back to the Google App Script to be written to the Sheet. I would like to use an AJAX or JQuery call, but it doesn't seem to be working. Need a little help getting the nice form working. I have tried doGet(e) and doGet() functions, but those aren't working. I have tried lots of different versions of the code. This isn't my final html form, but it has the same point. If I click the button, AJAX should return something to the Google App Script after processing. New to Google App Scripting and need help. Thanks!
code.gs
function doGet(e) {
var result = "";
try {
result = "Hi" + e.queryString;
//should write to the spreadsheet the information here
} catch (f) {
result= "Error" + f.toString();
}
result=JSON.stringify({"result":result});
return ContentService
.createTextOutput( "(" + result + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
//where does HtmlService.createHtmlOutput('index.html') go?
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
// Make an AJAX call to Google Script
function callGoogleScript() {
var url = "https://script.google.com/macros/s/" my script id "/exec";
var data = { name: "Tom", city: "Nowhere" };
var request = jQuery.ajax({
url:url+encodeURIComponent(data),
method: "GET",
dataType: "jsonp"
});
}
// print the returned data
function ctrlq(e) {
console.log(e.result)
}
</script>
</head>
<body>
<button id="test" name="test" onclick="callGoogleScript()">Test</script>
</body>
</html>
Edit: This is for a Web App.

Related

Cannot call getUI from this context when using google.script.run

I'm making a program that will automatically open the launch meeting page to take me to my next ZOOM class on time, and our schools have 'a days' and 'b days', each with a different schedule, so I have an HTML page that has two buttons, one that will trigger the schedule for an A day and another that triggers the schedule for a B day. I'm testing the functions that will open the new tab and run that function from the HTML, but when I run it from the HTML, I get an error message in my executions that says cannot call DocumentApp.getUI from this context. My code is here, if you put it into GAS you can see for yourself.
Part of my code came from this answer
My code.gs file
function doGet() {
return HtmlService.createHtmlOutputFromFile('index.html');
}
function openUrl( url ){
var html = HtmlService.createHtmlOutput('<html><script>'
+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
+'if(document.createEvent){'
+' var event=document.createEvent("MouseEvents");'
+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
+' event.initEvent("click",true,true); a.dispatchEvent(event);'
+'}else{ a.click() }'
+'close();'
+'</script>'
+'<body style="word-break:break-word;font-family:sans-serif;">Failed to open automatically. Click here to proceed.</body>'
+'<script>google.script.host.setHeight(40);google.script.host.setWidth(410)</script>'
+'</html>')
.setWidth( 90 ).setHeight( 1 );
DocumentApp.getUi().showModalDialog( html, "Opening ..." );
}
function LaZoom(){
openUrl('https://op97-org.zoom.us/j/9622570589');
}
My HTML file
<!DOCTYPE html>
<html>
<button onclick = 'aDay()'>A day</button>
<button onclick = 'bDay()'>B day</button>
</html>
<script>
function aDay(){
google.script.run.LaZoom();
alert('ran')
}
</script>
You can easily do this client side using window.open instead of going back and forth between server and client.
function aDay(){window.open('A_URL')}

Google App Scripts Function to Open URL [duplicate]

Is there a way to write a google apps script so when ran, a second browser window opens to www.google.com (or another site of my choice)?
I am trying to come up with a work-around to my previous question here:
Can I add a hyperlink inside a message box of a Google Apps spreadsheet
This function opens a URL without requiring additional user interaction.
/**
* Open a URL in a new tab.
*/
function openUrl( url ){
var html = HtmlService.createHtmlOutput('<html><script>'
+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
+'if(document.createEvent){'
+' var event=document.createEvent("MouseEvents");'
+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
+' event.initEvent("click",true,true); a.dispatchEvent(event);'
+'}else{ a.click() }'
+'close();'
+'</script>'
// Offer URL as clickable link in case above code fails.
+'<body style="word-break:break-word;font-family:sans-serif;">Failed to open automatically. Click here to proceed.</body>'
+'<script>google.script.host.setHeight(40);google.script.host.setWidth(410)</script>'
+'</html>')
.setWidth( 90 ).setHeight( 1 );
SpreadsheetApp.getUi().showModalDialog( html, "Opening ..." );
}
This method works by creating a temporary dialog box, so it will not work in contexts where the UI service is not accessible, such as the script editor or a custom G Sheets formula.
You can build a small UI that does the job like this :
function test(){
showURL("http://www.google.com")
}
//
function showURL(href){
var app = UiApp.createApplication().setHeight(50).setWidth(200);
app.setTitle("Show URL");
var link = app.createAnchor('open ', href).setId("link");
app.add(link);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
If you want to 'show' the URL, just change this line like this :
var link = app.createAnchor(href, href).setId("link");
EDIT : link to a demo spreadsheet in read only because too many people keep writing unwanted things on it (just make a copy to use instead).
EDIT : UiApp was deprecated by Google on 11th Dec 2014, this method could break at any time and needs updating to use HTML service instead!
EDIT :
below is an implementation using html service.
function testNew(){
showAnchor('Stackoverflow','http://stackoverflow.com/questions/tagged/google-apps-script');
}
function showAnchor(name,url) {
var html = '<html><body>'+name+'</body></html>';
var ui = HtmlService.createHtmlOutput(html)
SpreadsheetApp.getUi().showModelessDialog(ui,"demo");
}
There really isn't a need to create a custom click event as suggested in the bountied answer or to show the url as suggested in the accepted answer.
window.open(url)1 does open web pages automatically without user interaction, provided pop- up blockers are disabled(as is the case with Stephen's answer)
openUrl.html
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<script>
const url1 ='https://stackoverflow.com/a/54675103';
const winRef = window.open(url1);
winRef ? google.script.host.close() : window.alert('Allow popup to redirect you to '+url1) ;
window.onload=function(){document.getElementById('url').href = url1;}
</script>
</head>
<body>
Kindly allow pop ups</br>
Or <a id='url'>Click here </a>to continue!!!
</body>
</html>
code.gs:
function modalUrl(){
SpreadsheetApp.getUi()
.showModalDialog(
HtmlService.createHtmlOutputFromFile('openUrl').setHeight(50),
'Opening StackOverflow'
)
}
Google Apps Script will not open automatically web pages, but it could be used to display a message with links, buttons that the user could click on them to open the desired web pages or even to use the Window object and methods like addEventListener() to open URLs.
It's worth to note that UiApp is now deprecated. From Class UiApp - Google Apps Script - Google Developers
Deprecated. The UI service was deprecated on December 11, 2014. To
create user interfaces, use the HTML service instead.
The example in the HTML Service linked page is pretty simple,
Code.gs
// Use this code for Google Docs, Forms, or new Sheets.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Dialog')
.addItem('Open', 'openDialog')
.addToUi();
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Dialog title');
}
A customized version of index.html to show two hyperlinks
<a href='http://stackoverflow.com' target='_blank'>Stack Overflow</a>
<br/>
<a href='http://meta.stackoverflow.com/' target='_blank'>Meta Stack Overflow</a>
Building of off an earlier example, I think there is a cleaner way of doing this. Create an index.html file in your project and using Stephen's code from above, just convert it into an HTML doc.
<!DOCTYPE html>
<html>
<base target="_top">
<script>
function onSuccess(url) {
var a = document.createElement("a");
a.href = url;
a.target = "_blank";
window.close = function () {
window.setTimeout(function() {
google.script.host.close();
}, 9);
};
if (document.createEvent) {
var event = document.createEvent("MouseEvents");
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
window.document.body.append(a);
}
event.initEvent("click", true, true);
a.dispatchEvent(event);
} else {
a.click();
}
close();
}
function onFailure(url) {
var div = document.getElementById('failureContent');
var link = 'Process';
div.innerHtml = "Failure to open automatically: " + link;
}
google.script.run.withSuccessHandler(onSuccess).withFailureHandler(onFailure).getUrl();
</script>
<body>
<div id="failureContent"></div>
</body>
<script>
google.script.host.setHeight(40);
google.script.host.setWidth(410);
</script>
</html>
Then, in your Code.gs script, you can have something like the following,
function getUrl() {
return 'http://whatever.com';
}
function openUrl() {
var html = HtmlService.createHtmlOutputFromFile("index");
html.setWidth(90).setHeight(1);
var ui = SpreadsheetApp.getUi().showModalDialog(html, "Opening ..." );
}
I liked #Stephen M. Harris's answer, and it worked for me until recently. I'm not sure why it stopped working.
What works for me now on 2021-09-01:
function openUrl( url ){
Logger.log('openUrl. url: ' + url);
const html = `<html>
<a id='url' href="${url}">Click here</a>
<script>
var winRef = window.open("${url}");
winRef ? google.script.host.close() : window.alert('Configure browser to allow popup to redirect you to ${url}') ;
</script>
</html>`;
Logger.log('openUrl. html: ' + html);
var htmlOutput = HtmlService.createHtmlOutput(html).setWidth( 250 ).setHeight( 300 );
Logger.log('openUrl. htmlOutput: ' + htmlOutput);
SpreadsheetApp.getUi().showModalDialog( htmlOutput, `openUrl function in generic.gs is now opening a URL...` ); // https://developers.google.com/apps-script/reference/base/ui#showModalDialog(Object,String) Requires authorization with this scope: https://www.googleapis.com/auth/script.container.ui See https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes
}
https://developers.google.com/apps-script/reference/base/ui#showModalDialog(Object,String) Requires authorization with this scope: https://www.googleapis.com/auth/script.container.ui See https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes

mikrotik add hotspot user from javascript API

I try to develop an app to create user in mikrotik user manager via api. And I also tried JavaScript in the terminal by using following command:
$node script name.js
That is working and a user created.
Then I tried to run that JavaScript by on click html button. Then JavaScript doesn't run and no user crated. Code follows:
<html>
<head>
</head>
<body>
<button type = "button" onclick="conn();">Try it</button>
<script type="text/javascript">
var api = require('mikronode');
var connection = new api('192.168.5.1','admin','xxxxxx');
connection.connect(function conn() {
conn.closeOnDone(true); // All channels need to complete before the connection will close.
var actionChannel=conn.openChannel();
// These will run synchronsously
actionChannel.write(['/tool/user-manager/user/add','=username=tiran','=password=123456','=customer=admin']); // don't care to do anything after it's done.
actionChannel.write(['/tool/user-manager/user/create-and-activate-profile','=customer=admin','=numbers=tiran','=profile=general']); // don't care to do anything after it's done.
//actionChannel.write('/tool/user-manager/user/print',function(chan) {
//chan.on('done',function(data) {
//packets=api.parseItems(data);
//packets.forEach(function(packet) {
//alert('done');
//alert('user: '+JSON.stringify(packet));
//console.log('user: '+JSON.stringify(packet));
//});
//listenChannel.close(); // This should call the /cancel command to stop the listen.
//});
//});
actionChannel.close(); // The above commands will complete before this is closed.
});
</script>
</body>
</html>
That code is for NodeJS, you cannot run in directly on the browser as JavaScript

jQuery mobile : Linking next page doesn't work on Windows phone using phonegap

Currently im building a application using phonegap & jQuery Mobile
I have done the version which is perfectly working on iOS & Android.But the same code does not work on windows phone.When i click any link,redirection to the respective page is not loading..Its still says "Error Page loading".
<!DOCTYPE html>
Test
<div id="bg">
<div style="padding-top:14%;width:100%;text-align:center">
<div style="float:left;text-align:center;width:50%"><img src="pics/btn_1.png" /></div>
<div style="float:left;text-align:center;width:50%"><img src="pics/btn_2.png" /></div>
</div>
<div style="clear:both"></div>
</div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript">
app.initialize();
</script>
</body>
Need help on this.
Solution
Add data-ajax=false or rel=external to your anchor tag. But, if you do this, you will lose transitions. This tells the framework to do a full page reload to clear out the Ajax hash in the URL. You could enable this if the incoming device is a windows phone if needed :
$(document).on("mobileinit", function () {
//check for windows phone
$.mobile.ajaxEnabled = false;
});
Else, make your code into a single page template. Here's a demo of that : http://jsfiddle.net/hungerpain/aYW2f/
Edit
Currently jQM doesn't support query string parameters. You could use the localStorage API to store the parameters in cache and retrieve them later. Assuming you want to go to index.html from here :
<img src="pics/btn_2.png" />
You'd add a click event for it :
$(document).on("click", "a", function() {
//gets qs=2 and changes it into ["qs",2]
var query = this.href.split["?"][2].split["="];
//construct an array out of that
var paramString = { query[0]: query[1]} ;
//store it in localstorage
locaStorage["query"] = JSON.stringify(paramString);
//continue redirection
return true;
});
In your index.html :
$(document).on("pageinit", "[data-role=page]", function() {
//store it in localstorage
var params = JSON.parse(locaStorage["query"]);
//now params will contain { "qs" : 2 }
//you could access "2" by params["qs"]
});
More info about localStorage here.
I had Also same issue and finally resolve it by using below code
my html page is index.html and i am writtinga all code in one html
Before
$.mobile.changePage( "#second", {});
After
var url = window.location.href;
url = url.split('#').pop().split('?').pop();
url = url.replace(url.substring(url.lastIndexOf('/') + 1),"index.html#second");
$.mobile.changePage(url, { reloadPage : false, changeHash : false });
and suppose you have multiple html page then for more one page to another you can use
var url = window.location.href;
url = url.split('#').pop().split('?').pop();
url = url.replace(url.substring(url.lastIndexOf('/') + 1),"second.html");
$.mobile.changePage(url, { reloadPage : false, changeHash : false });
There is no support of querystring in web application using phonegap for windows phone 7.
However we can replace ? with # or anything else to pass the data,
like convert
Sample.html?id=12312
to
Sample.html#id=12312

Firefox extensions and full file paths from HTML form?

I have built a Firefox extension using the Addon SDK that opens up a new tab with a HTML page from the extensions directory and attaches a content script to it:
function openHtmlLoadFormTab(htmlFileName, jsWorkerFileName) {
tabs.open({
url: data.url(htmlFileName),
onReady: function(tab) {
var tabWorker = tab.attach({
contentScriptFile: [ data.url(jsJquery), data.url(jsWorkerFileName) ]
});
}
});
}
I have an <input type="file"> in the HTML file and some code that handles the "submit" event in the JS file (these files are given by htmlFileName and jsWorkerFileName respectively)
Because of security reasons, I cannot access the full file path in JS with document.getElementById('uploadid').value. I only get the file's name.
However, since this is a Firefox extension, I'm wondering if there is anyway to override this restriction?
I have been looking into netscape.security.PrivilegeManager.enablePrivilege("UniversalFileRead") and mozFullPath but I haven't been able to get it to work. I believe it's deprecated anyway?
The other solution is to build an XUL-based UI and prompt for the file there somehow, but I would like to know for sure if there is anyway to get this to work in HTML.
First edit with small example code
I built a small sample extension to illustrate how I'm doing things.
lib/main.js
var self = require('self');
var tabs = require('tabs');
var data = self.data;
var jsLoadForm = "load-form.js", htmlLoadForm = "load-form.html";
var jsJquery = 'jquery-1.8.0.min.js';
exports.onUnload = function(reason) {};
exports.main = function(options, callbacks) {
// TODO: remove this debugging line
openHtmlLoadFormTab(htmlLoadForm, jsLoadForm);
};
function openHtmlLoadFormTab(htmlFileName, jsWorkerFileName) {
tabs.open({
url: data.url(htmlFileName),
onReady: function(tab) {
var tabWorker = tab.attach({
contentScriptFile: [ data.url(jsJquery), data.url(jsWorkerFileName) ]
});
}
});
}
data/load-form.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form</title>
<script lang="text/javascript">
function fileChanged(e) {
// this is just the file name
alert("html js: files[0].name: " + e.files[0].name);
// mozFullPath is indeed empty, NOT undefined
alert("html js: files[0].mozFullPath: " + e.files[0].mozFullPath);
}
</script>
</head>
<body>
<form name="my-form" id="my-form" action="">
<div>
<label for="uploadid1" id="uploadlabel1">File (JS in HTML):</label>
<input type="file" name="uploadid1" id="uploadid1" onchange="fileChanged(this)"/>
</div>
<div>
<label for="uploadid2" id="uploadlabel2">File (JS in content script): </label>
<input type="file" name="uploadid2" id="uploadid2" onchange="fileChangedInContentScript(this)"/>
</div>
<div>
<label for="uploadid3" id="uploadlabel3">File (JS using jQuery in content script):</label>
<input type="file" name="uploadid3" id="uploadid3" />
</div>
</form>
</body>
</html>
data/load-form.js
$(document).ready(function() {
$("#uploadid3").change(function(e) {
// in jquery, e.files is null
if(e.files != null)
console.log("jquery: e.files is defined");
else
console.log("jquery: e.files is null");
// this works, prints the file name though
console.log("$('#uploadid3').val(): " + $("#uploadid3").val());
// this is undefined
console.log("$('#uploadid3').mozFullPath: " + $("#uploadid3").mozFullPath);
});
});
// this handler never gets called
function fileChangedInContentScript(e) {
alert("js content script: filechanged in content script called");
}
As you can see in main.js, I used jquery-1.8.0.min.js, downloaded from the jQuery website.
Note: I also tried these without jQuery included as a content script when I opened the tab in main.js, but no luck.
The conclusion is that mozFullPath is indeed empty when I access it from JS embedded in the HTML page and I cannot find a way to access mozFullPath from jQuery, nor can I find a way to add a onchange handler in load-form.html that's defined in load-form.js
Second edit with onchange handler in the load-form.js content-script
I added the following code to load-form.js to catch the onchange event.
I also removed the jQuery content script from main.js
document.addEventListener("DOMContentLoaded", function() {
try {
document.getElementById("uploadid2").addEventListener('change', function(e) {
console.log("addeventlistener worked!");
console.log("e: " + e);
console.log("e.target: " + e.target);
console.log("e.target.files: " + e.target.files);
console.log("e.target.files[0].name: " + e.target.files[0].name);
console.log("e.target.files[0].mozFullPath: " + e.target.files[0].mozFullPath);
});
console.log('added event listener')
} catch(e) {
console.log('adding event listener failed: ' + e);
}
}, false);
This still outputs an empty string for mozFullPath:
info: added event listener
info: addeventlistener worked!
info: e: [object Event]
info: e.target: [object HTMLInputElement]
info: e.target.files: [object FileList]
info: e.target.files[0].name: test.sh
info: e.target.files[0].mozFullPath:
Is there anyway to acquire the needed permissions? How can I get my hands on that full path? I need the full path so I can pass it to an application the extension launches. (There are workaround solutions where I can do without the full path, but they decrease the quality of the extension)
fileInput.value property is meant to be accessible to web pages so it will only give you the file name, not the full path - web pages have no reason to know the full path on your machine. However, as a privileged extension you should be able to access the File.mozFullPath property. In this particular case you would do it like this:
var files = document.getElementById('uploadid').files;
if (files.length > 0)
{
// Assuming that only one file can be selected
// we care only about the first entry
console.log(files[0].mozFullPath);
}
The big question of course is whether your code is allowed to access File.mozFullPath. I suspect that a content script in the Add-on SDK won't have the necessary privileges. The main extension code will have the privileges but getting to the input field from there is hard...

Resources