Call a Google Apps Script function in HTML - javascript

I coach a sports team and set up a website for it. I would like to add a button to an admin page which I can click to quickly send an email to everyone on the team. This email will say something like: "Today's schedule has changed, please visit the website for more info."
I am sure this could be achieved easier though a distribution list in outlook or something, but I want to use, and get a better understanding of, Google Apps Script.
I have a Google Spreadsheet with the email addresses and a simple script function which sends the email.
This works great when I click to run it inside the script editor, but is there a way to call this from an external website with a button and some JavaScript?

You need to create a Google UI object that you can embed in your site - you'll do this by creating a Script that you will deploy as a web app. (Refer to Google Apps Script - Web Apps.) That object will contain a button that will invoke your existing script function to send the email.
Publish your web app to execute as you. For your specific situation, you will also want to have access to the app limited to you. That will mean that the UI Blob you create will not function for the general public. This is very incomplete, but produces a blob with a single button that will trigger your script:
function doGet() {
var app = UiApp.createApplication();
var button = app.createButton('Send Schedule Change Email');
app.add(button);
var handler = app.createServerHandler('myClickHandler');
button.addClickHandler(handler);
return app;
}
function myClickHandler(e) {
var app = UiApp.getActiveApplication();
// Call your library function, e.g.
TeamSpreadsheet.sendScheduleChanged();
var label = app.createLabel('Message Sent')
.setId('statusLabel')
.setVisible(false);
label.setVisible(true);
app.close();
return app;
}
Your web app will need to have access to your existing script, which I assume is embedded in your spreadsheet. This is accomplished by first saving a version of your existing script (File - Manage Versions), then adding it as a library to your new web app script (Resources - Manage Libraries). Read more about libraries here. Since your web app will run as you, you can keep your spreadsheet private.
Caveats
The utility script in your library will need to open your spreadsheet in a way that will work from outside of the sheet; use SpreadsheetApp.openById(), rather than SpreadsheetApp.getActiveSpreadsheet(). Unfortunately, openById pukes when you use it to open the host spreadsheet, so you'll want something like this:
var ss = null;
try {
// This works for scripts running in the host spreadsheet
ss = SpreadsheetApp.getActiveSpreadsheet();
} catch(err) {
try {
// This works for web apps
ss = SpreadsheetApp.openById("SPREADSHEET ID");
} catch(err) {
Logger.log("Could not open source spreadsheet.");
// Well, then, not much sense carrying on, is there?
return;
}
}
SpreadsheetApp.setActiveSpreadsheet(ss);
...
In fact, watch for any reliance on calls to get info from "active" objects. You may need to do some gymnastics to get around them.
I've found debugging this type of arrangement to be painful, since the app-script debugger often reports "Server Errors" when you try to trace into a library routine.
I hope that helps gets you started!

As a complement to Mogsdad's answer (that was quite complete and interesting) I'd say that your case could be a bit simpler since you have already a working script.
Take your script and add a doGet() function like in Mogsdad example, define a handler on the button that will call your existing function you wrote to send mails, in this function replace the getActiveSpreadsheet() by SpreadsheetApp.OpenById("the ID of the SS") and the getActiveSheet() by OpenByName() and you're done with the modifications.
After that follow Mogsdad instructions : deploy the script as a webapp running as you and use the provided url to access it from a link on your site.
If you want not to take any risk, do all these changes on a copy of your original spreadsheet so you always keep a working model at hand.
If you want more accurate advice (or if you meet some difficulties) feel free to show your existing script to get some help with the modifications.
PS : please consider this as a simple comment, written in an answer for the comfort of formating

Just publish your script as web application and upon a button click, excecute:
window.open("https://script.google.com/macros/s/<id>/exec");
Don't know if this was possible a year ago.

Related

How to display alert windows or prompt to unregistered users

I am setting up a spreadsheet whose users do not necessarily have a Google account nor should be required to log in.
I would like to interact with users in two ways that are possible with the Browser class. One is displaying an alert in a pop-up window, in order to warn users not to edit a field. This is done with Browser.msgBox or ̀€ui.alert. The second thing is requesting an input in a window, which is done nicely with Browser.inputBox().
While my code works fine when I am logged in as owner, it is useless when edited by an unregistered user.
Can I make those function work for unregistered users and if so, how?
Otherwise, are there workarounds to achieve similar functionalities?
Here is a minimal example of what I did:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSheet();
var eventRange = event.range;
var eventRow = eventRange.getRow();
var eventCol = eventRange.getColumn();
if(eventCol == 1){
protectDateField(ss, eventRow, eventCol)
}
}
function protectDateField(ss, eventRow, eventCol){
var ui = SpreadsheetApp.getUi();
ui.alert("Do Not Edit This Field!");
ss.getRange(eventRow,eventCol).clearContent();
}
Short answer
What you want to do it's not possible on Google Sheets.
Explanation
To run a script bounded to a spreadsheet the user should be an editor for that spreadsheet.
Simple triggers can't access services that require authorization to run.
Some additional restrictions apply when triggers are triggered by anonymous users.
NOTES:
The required authorization scopes by each method are included on the documentation section for each method.
Anonymous users can't authorize scripts to be ran by installable triggers as the authorization process requires a usar account to log the respective authorization.
For further details see:
https://developers.google.com/apps-script/guides/sheets
https://developers.google.com/apps-script/guides/triggers
Workarounds
There is no way to achieve the desired functionality by solely using Google Sheets, but you could
create a web app by using Apps Script and embed on it a spreadsheet
create a web app by using another platform and embed on it spreadsheet

Update data in (really) real-time with Javascript

hi
I want to build a control panel for a web art application that needs to run in fullscreen, so all this panel, that controls stuff like colors and speed values, have to be located at a different window.
My idea is to have a database storing all these values and when I make a change in the control panel window the corresponding variable in the application window gets updated too. So, it's basically a real-time update that I could do with AJAX setting a interval to keep checking for changes BUT my problem is: I can't wait 30 seconds or so for the update to happen and I guess a every-1-second AJAX request would be impossible.
Final question: is there a way to create a sort of a listener to changes in the database and fire the update event in the main application only immediately after I change some value in the control panel? Does Angular or another framework have this capability?
(Sorry for the long explanation, but I hope my question is clearer by offering the context [: )
A web socket powered application would have this benefit. This carries a bit more complexity on the back end, but has the benefit of making your application as close to real-time as can be reasonably expected.
The Mozilla Development Network has some good documentation on websockets.
On the front end, the WebSocket object should work for you on most modern browsers.
I'm not sure what your back end is written in, but Socket.IO for Node.js and Tornado for Python will make your applications web-socket capable
If one window is opening the other windows via JavaScript, you can keep the reference to the opened window and use otherWindow.postMessage to pass messages across
"Parent" window looks like
// set up to receive messages
window.addEventListener('message', function (e) {
if (e.origin !== 'http://my.url')
return; // ignore unknown source
console.log(e.message);
});
// set up to send messages
var otherWindow = window.open('/foo', '_blank');
otherWindow.postMessage('hello world', 'http://my.url');
"Child" windows look similar
// same setup to recieve
// ...
// set up to send
var otherWindow = window.opener;
// ... same as before
For the realtime I would recommend using a library like socket.io or using a database like firebase.
For the fullscreen I would recommend using a library like angular-screenfull
i use https://pushjs.io/, had exactly the same problem and this is a really simple solution for your problem. It is capable of sending and listening to events without any database interference.

Is it possible to allow a user to (after pushing a button) call a specific number?

I'm making an app using apparchitect and I would like to know how to use JavaScript to call a number. I have tried using different ones already uploaded to no avail. Is it possible to allow a user to (after pushing a button) call a specific number?
In the simplified app maker I can use javascript if they do not have the function I want. This is for an iphone app. Please help this app is really important. Thank you.
The easiest answer for your iPhone app is to include the following HTML (as seen from this answer:
tap to call
Tapping the "tap to call" text on your phone will activate the link and send a message to your app to launch to the provided URL. As long as your app is responding to your webview properly and to the OS, everything should work without JavaScript:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: activatedURLString]]
twilio nodejs library. It's incredibly easy.
1.) Make an account with twilio.
2.) Hook up your button with this code:
// Your accountSid and authToken from twilio.com/user/account go in between quotes
var accountSid = "";
var authToken = "";
var client = require('twilio')(accountSid, authToken); // don't forget to install the twilio libary from npm
client.messages.create({
body: "Jenny please?! I love you <3",
to: "+14159352345",
from: "+14158141829"
}, function(err, message) {
process.stdout.write(message.sid);
});
In fact, I feel like being really nice, so I just uploaded a repository to github which does exactly what you are asking. It uses the Meteor framework. To run it, clone the repo, navigate to the folder in your command line and simple type "meteor" into the command prompt. The site will be running in your browser at localhost3000.

Chrome Extension Writing to Google Spreadsheet [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I've been doing some research and for some reason can't find a good example showing this anywhere, and I am starting to wonder if it's even possible.
What's I'm looking to do is to have my extension write data within a Google Spreadsheet, so that the sheet is being used as a database.
Does anyone have any documentation that I could follow through? Considering that the Spreadsheet API doesn't seem to allow JavaScript, is that even possible?
THanks.
Yes, it is definitely possible. I have used the Spreadsheet API extensively using Javascript. You'll need to use the Protocol version of the API as documented here: https://developers.google.com/google-apps/spreadsheets/
This requires sending signed requests using OAuth2 (the older auth protocols aren't really reliable anymore.) so I suggest using an OAuth2 library like JSO.
https://github.com/andreassolberg/jso
When writing your javascript you'll need to write functions that create an XML string to interface with the Protocol API. Parsing the responses is pretty straight forward. I've included a snippet of the code I've used. You can also see my answer to a related question using JQuery here. JQuery .ajax POST to Spreadsheets API?
function appendSpreadsheet(){
//Constructs the XML string to interface with the Spreadsheet API.
//This function adds the value of the param foo to the cell in the first empty row in the column called 'columnTitle'.
//The Spreadsheet API will return an error if there isn't a column with that title.
function constructAtomXML(foo){
var atom = ["<?xml version='1.0' encoding='UTF-8'?>",
'<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">',//'--END_OF_PART\r\n',
'<gsx:columnTitle>',foo,'</gsx:columnTitle>',//'--END_OF_PART\r\n',
'</entry>'].join('');
return atom;
};
var params = {
'method': 'POST',
'headers': {
'GData-Version': '3.0',
'Content-Type': 'application/atom+xml'
},
'body': constructAtomXML(foo)
};
var docId //Get this from the spreadsheet URL or from the Google Drive API.
var worksheetId = 'od6'; //The worksheet Id for the first sheet is 'od6' by default.
url = 'https://spreadsheets.google.com/feeds/list/'+docId+'/'+worksheetId+'/private/full';
sendSignedRequest(url, handleSuccess, params); //Use your OAuth2 lib
}
I think you are having same question which I had some months ago. I was looking for some library to do same but couldn't found any so I end up with creating one called gsloader. I am using this library in this jiraProgressTracker chrome extension. Chrome extension is under development but gsloader library is ready to use.
Here is what you need to do.
Create a google cloud project under this, https://cloud.google.com/console#/project. Be patient, it will take some time.
Under "Registered Apps", do not delete the "Service Account - Project".
Under "Registered Apps", register a new app, choose platform web application.
Under "APIs", select "Drive API".
In newly created app, paste your chrome application url(like chrome-extension://) for "web origin"
Copy "client id" from OAuth 2.0 Client ID from app created in step 3
Add gsloader library, into you html page. It needs require.js and js-logger and jQuery. If you can't use requirejs, please let me know I will try to create library by removing requirejs dependency, though it may take more time for me to do it.
Following is some code snippet to go with.
// Do Authorization
var clientId = "<your client id>";
GSLoader.setClientId(clientId);
// Load existing spreadsheet
GSLoader.loadSpreadsheet("spreadsheet id");
// Create spreadsheet
GSLoader.createSpreadsheet("spreadsheet id")
There are enough methods and objects available to work with, rather than mentioning all here I will try to make documentation available.
Please let me know, how does it works with you overall.

Chrome extension(callbacks/localstorage)

I want to make a Chrome extension that does this (automatically logs onto a website, probably not the best approach since it was my first time doing JS) and has an option page where the user can set up his info and get it saved (localstorage?) so that the content script can access it as well as the options page.
Here's what I've come to after a whole lot of researching, looking at examples etc.:
http://pastebin.com/yTiXp4VY (source code of all files there).
In the end I gave up and just used trial and error so there's tons of errors there. The console is reporting it can't run for security reasons. Please explain me what is wrong with this version. I don't need you to fix my code, I am just learning JS.
For most part you code seems to be correct. Only one note: your options page can access localStorage directly, it doesn't need the background page for that. But the main issue seems to be the way your content script is built - it assumes that the user/password data will be available immediately (synchronously). That's not how it works: you are sending a message to the background page, eventually the background page will answer and your callback in the content script will be called. The simplest approach is to delay all actions until that happens and you have the data. And it should be easier to send a single message to the background page that will give you all the data. Something like this:
var userName = null;
var passWord = null;
chrome.extension.sendRequest( { eventName: "getLogin" },
function(response) {
userName = response.user;
passWord = response.pass;
// Now we can continue doing whatever we are doing
checkIfAutoUrl();
}
);
And the message processing in your background page would look like this:
function onRequest(request, sender, sendResponse) {
if (request.eventName == "getLogin") {
sendResponse({user: getStorage("user"), pass: getStorage("pass")});
}
}
Note that the response is an object with two properties user and pass here - this way the content script can immediately continue once it got the response, it has all the necessary data and doesn't need to wait for a second response.
Update: Concerning the error message you get: the Content Security Policy is meant to protect your extension from being exploited by a malicious website. One part of that protection is disallowing inline scripts in HTML - like onload="load_options()". All scripts that will run need to be placed in your JavaScript files. So instead of this onload attribute you can put the following into options.js:
window.addEventListener("load", load_options, false);
See addEventListener documentation on MDN.

Categories