For developers, part of the simplicity of Apps Script has always been that authorization requires zero setup — but we heard from users that the process required too many clicks. At Google I/O this year, we launched an opt-in version of an easier authorization flow; today, that new flow becomes the default for all new scripts.
Besides being prettier and easier, the new flow offers benefits behind the scenes: it allows more scripts to be simultaneously authorized on the same account, which means you shouldn’t need to reauthorize a script unless the code changes substantially.
For developers who use the advanced Google services, the new flow also gets rid of some manual steps that were previously required. Every new script now automatically creates a project in the Google APIs Console — no more messing with secret keys!
If you want the same experience for your existing scripts, you can upgrade them manually in just a few seconds.
engineering/backend-support
engineering-backend-support
If your app needs to keep up with changes in Drive, whether to sync files, initiate workflows, or just keep users up to date with the latest info, you’re likely familiar with Drive’s changes feed. But periodic polling for changes has always required a delicate balance between resources and timeliness.
Now there’s a better way. With push notifications for the Drive API, periodic polling is no longer necessary. Your app can subscribe for changes to a user’s drive and get notified whenever changes occur.
Suppose your app is hosted on a server with my-host.com domain and push notifications should be delivered to an HTTPS web-hook https://my-host.com/notification:
my-host.com
https://my-host.com/notification
String subscriptionId = UUID.randomUUID().toString(); Channel request = new Channel() .setId(subscriptionId) .setType("web_hook") .setAddress("https://my-host.com/notification"); drive.changes().watch(request).execute();
As long as the subscription is active, Google Drive will trigger a web-hook callback at https://my-host.com/notification. The app can then query the change feed to catch up from the last synchronization point:
changes = service.changes().list() .setStartChangeId(lastChangeId).execute();
If your app only needs to be notified about changes to a particular file or folder your app can watch just those files rather than the entire change feed.
If you are interested in using this new feature, please refer to the documentation at developers.google.com. You can see push notifications in action with the Push Notifications Playground and view the source at Github.
Many developers have come to prefer JSON for data serialization, but we recognize that good ol' XML is still an important format for many Apps Script users. Our existing XML service is good at parsing XML, but has limited ability to create or alter existing documents. In order to provide a more complete and consistent experience, we have created a new XML service, which launches today. The new service is accessed using XmlService, in contrast to the old service which was simply called Xml.
XmlService
Xml
Let's take a look at how you can use the new service to create an XML representation of the emails in your Gmail inbox.
function createXml() { var root = XmlService.createElement('threads'); var threads = GmailApp.getInboxThreads(); for (var i = 0; i < threads.length; i++) { var child = XmlService.createElement('thread') .setAttribute('messageCount', threads[i].getMessageCount()) .setAttribute('isUnread', threads[i].isUnread()) .setText(threads[i].getFirstMessageSubject()); root.addContent(child); } var document = XmlService.createDocument(root); var xml = XmlService.getPrettyFormat().format(document); Logger.log(xml); }
The code above logs XML that looks something like this:
<?xml version="1.0" encoding="UTF-8"?> <threads> <thread messageCount="1" isUnread="true"> Can't wait for the new XML service! </thread> <thread messageCount="1" isUnread="true"> 50% off all widgets through Friday </thread> <thread messageCount="3" isUnread="false"> Don't forget about the picnic on Saturday </thread> </threads>
The new XML service has some notable advantages over the old service:
CDATA sections
Comments
With the launch of this new service, we are deprecating some of our older XML tools in Apps Script, specifically the old XML service, the SOAP service, and the JavaScript feature E4X. Calls to these services will continue to work, but we encourage you to start migrating your code to the new XML service for better long-term support. On February 1, 2014, these old services will no longer appear in auto-complete or in our documentation, per the Apps Script sunset schedule.
Ever wanted to programmatically insert something at the cursor in Google Docs (say, a “Sign Here” image) or read the user’s selection (maybe for an on-the-spot translation)? Starting today, you can.
Apps Scripts bound to Google Docs can now access the active user's Cursor and Selection by calling Document.getCursor() and Document.getSelection(), respectively. The returned objects provide useful information like the element the cursor is positioned in and an array of all of the elements contained in the selection.
Document.getCursor()
Document.getSelection()
This Google Doc contains a simple script that uses Apps Script’s Language Service to translate selected text from English to Spanish through a custom menu item.
Here, it uses the getSelectedElements() method of the Selection class to get an array of selected elements:
getSelectedElements()
Selection
var selection = DocumentApp.getActiveDocument().getSelection(); if (selection) { var elements = selection.getSelectedElements();
Next, it loops through each element, performs the translation, and replaces the original text:
var translatedText = LanguageApp.translate( element.asText().getText(), 'EN', 'ES'); element.asText().setText(translatedText);
At Google I/O this year, Apps Script engineer Jonathan Rascher demonstrated Bibstro, a bibliography sample app for Google Docs that inserts inline citations at the cursor. Today, we’re releasing the source code for Bibstro; you can also try it out by making of copy of this Google Doc.
To insert text, the script calls the aptly named insertText() method of the Cursor object:
insertText()
Cursor
var cursor = DocumentApp.getActiveDocument().getCursor(); if (cursor) { // Determine the text of the new inline citation to insert. var citation = bibStrategy.getInlineCitationText(...); var surroundingText = cursor.getSurroundingText().getText(); var surroundingTextOffset = cursor.getSurroundingTextOffset(); if (surroundingTextOffset > 0 && surroundingText.charAt(surroundingTextOffset - 1) != ' ') { // If the cursor follows a non-space character, insert a space // and then the citation. cursor.insertText(' ' + citation); } else { // Otherwise, just insert the citation. cursor.insertText(citation); } }
You’ll also notice that the script uses the Cursor class’s getSurroundingText() method to determine whether to insert a space before the new inline citation.
getSurroundingText()
To help you become familiar with how cursor and selection work, we've also created a Cursor Inspector sample script. As you navigate through a document, the script displays up-to-date information about your cursor or selection in a custom sidebar. We’re also releasing the source code for Cursor Inspector on GitHub.
These new APIs are available immediately. We’re excited to see what kind of scripts you come up with!
Flubaroo, a popular Apps Script application that helps teachers with grading, has just reached version 3.0. The new features and improvements include:
If you know any teachers who aren’t using Flubaroo yet, why not encourage them to try it out? It doesn’t cost a thing, and has helped thousands of teachers save time and gain insight into student performance — all through the power of Apps Script.
Ever look at the data returned when using the Drive API? A files.list call, even if just returning a single file, can yield upwards of 4kb of data. Drive has a rich set of metadata about files, but chances are your application only needs a small fraction of what’s available.
files.list
One of the simplest but most effective optimizations you can make when building apps with the Drive API is limiting the amount of data returned to only those fields needed for your particular use case. The fields query parameter gives you that control, and the results can be dramatic.
A simple example of this is using the files.list call to display a list of files to a user. The naive query, https://www.googleapis.com/drive/v2/files?maxResults=100, generated more than 380kb of data when I ran it against my own corpus. But to render this list nicely, an app only needs a few bits of information -- the document title, icon & thumbnail URLs, the mime type, and of course the file ID.
https://www.googleapis.com/drive/v2/files?maxResults=100
Using the fields query parameter, the results can be trimmed to just the necessary fields and those needed for fetching subsequent pages of data. The optimized query is https://www.googleapis.com/drive/v2/files?maxResults=100&fields=items(iconLink%2Cid%2Ckind%2CmimeType%2CthumbnailLink%2Ctitle)%2CnextPageToken.
https://www.googleapis.com/drive/v2/files?maxResults=100&fields=items(iconLink%2Cid%2Ckind%2CmimeType%2CthumbnailLink%2Ctitle)%2CnextPageToken
After modifying the query the resulting data was only 30k. That’s more than a 90% reduction in data size! Besides reducing the amount of data on the wire, these hints also enable us to further optimize how queries are processed. Not only is there less data to send, but also less time spent getting it in the first place.