WorryFree Computers   »   [go: up one dir, main page]

One of things that makes Apps Script great is its ability to act as a hub for various types of Google data. In addition to our built-in services for popular products such as Gmail, Drive, Docs, and Calendar, we also provide a line of advanced Google services that let you use existing Google APIs such as Analytics and Tasks. Today, we're expanding that family of advanced services to include the following:

From Google Apps administrators and data-heads to Glass Explorers and YouTube content creators, this collection of new services has something for everyone. Getting started with advanced services is easy, since we take cake of the authorization for you and even provide autocomplete in the script editor.

While our built-in services are hand-crafted for ease-of-use, our advanced services are automatically generated from existing public Google APIs. They provide access to the full power of the underlying API but can be slightly more difficult to use. Let's look at some sample code that searches for YouTube videos with the keyword "dogs".

function searchByKeyword() {
  var part = 'id,snippet';
  var optionalArgs = {
    q: 'dogs',
    maxResults: 25
  };

  var results = YouTube.Search.list(part, optionalArgs);

  for (var i = 0; i < results.items.length; i++) {
    var item = results.items[i];
    Logger.log('[%s] Title: %s', item.id.videoId, item.snippet.title);
  }
}

This function uses the YouTube.Search.list() method, which has a required parameter part and optional parameters q and maxResults, among others. Required parameters are passed individually as method arguments, while optional parameters are passed as one key-value map. The full list of parameters this method accepts can be found in the YouTube API's reference documentation.

We're also changing our advanced services to behave more like vanilla JavaScript, so that it's easier to reference the APIs' existing documentation. You can now pass native JavaScript objects into these services' methods, and access the results using regular dot-notation. Below is some sample code that adds a new user to a Google Apps domain.

function addUser() {
  var user = {
    primaryEmail: 'liz@example.com',
    name: {
      givenName: 'Elizabeth',
      familyName: 'Smith'
    },
    // Generate a random password string.
    password: Math.random().toString(36)
  };
  user = AdminDirectory.Users.insert(user);
  Logger.log('User %s created with ID %s.', user.primaryEmail, user.id);
}

Notice that the user resource is constructed as a plain object literal, and the ID of the created user is accessed via dot-notation. The legacy getter/setter notation will continue to work but will no longer appear in autocomplete.

Finally, it's worth reminding that advanced Google services must be enabled in each script that uses them. This involves toggling them on once in the script editor under Resources > Advanced Google services and again in the associated Google Developers Console project.


Eric Koleda profile

Eric is a Developer Programs Engineer based in NYC on the Google Apps Script team. He's previously worked with the AdWords API and enterprise content management software.

We launched the Admin SDK in May as a new way for developers to build customized administrative tools for organizations that use Google Apps. A top priority for most administrators is keeping their users safe. Today, we're adding new security management features to the Directory API to help administrators manage:

These new utilities allow Google Apps administrators to programmatically manage end-user access to third party applications, and enable third party developers to build new security management features in their applications.

As an example, FlashPanel, a popular tool used by Google Apps administrators, is using these new features to allow domain admins to review installed applications and manage or revoke access to them. The Apps Explorer in FlashPanel allows admins to see which are the most installed apps in his domain or use a filter to review applications by type of permissions (Drive, GMail, etc). It also allows admins to review the number of users who have granted access to a particular application.

The screenshot below shows an example of FlashPanel’s customized view of third-party application installs.

In FlashPanel’s integration, admins have the power to whitelist or blacklist apps, as shown below.

The Directory API now also provides the ability to manage admin notifications that are delivered to the Admin Console. Currently, admins receive notifications for events that affect their domains such as users approaching their storage limits or monthly bills that are due. Now you can use the API to list notifications, update their read status, or pull them into custom tools.

If you are interested in using these new endpoints, please refer to the Directory API documentation.

Ajay Guwalani  

Ajay Guwalani is Product Manager on Google Apps Admin APIs. His current focus is to build next generation admin APIs to make enterprise developers and admins happy.

In 2013, we have made a lot of enhancements to our developer ecosystem. From the Admin SDK to the Google+ API for Domains, we've provided developers with better tools to build applications that integrate with, and add functionality to, Google Apps.

We are starting to roll out some significant changes that will make it easier for developers to build, deploy, and market applications for domain installation. This is a major improvement to our existing Google Apps Marketplace experience that we originally launched in 2010. You can get started right away by following our documentation here.

Some of the major enhancements in the new Google Apps Marketplace:

Improved security — Domain admins can install and authorize your applications for specific organizational units rather than the whole domain.
Consistent development experience — Use OAuth 2.0 for authorization and manage Google APIs in the Google Developer console.
Test before publishing — Test your app easily using the “Test Install” button in the Google Developers console vs. having to publish a live app.
Simplified publishing experience — Publish to the Chrome Web Store to use a single app listing for apps that work with both Google Drive and Google Apps for Business.
Deeper integration — Integrate with services like Google Drive and Google+ and into Google’s new app launcher.
Easier discoverability — Domain admins can discover your great apps directly from the Admin console.

We love our developers who are building third-party solutions for Google users. Please provide feedback on how we can improve the experience and help you connect with over 5 million companies that use Google Apps to improve how they get work done. Here are some great apps to get you inspired - Promevo gPanel, Draw.io, UberConference, AODocs, Spanning Calendar Undelete, Flashpanel, and MindMeister. Get started with your Google Apps integration today!



Arun Nagarajan   profile

Arun is a Developer Advocate on Google Apps. Arun works closely with the community of partners, customers and developers to help them build compelling applications on top of Google Apps. Arun is originally from the Boston area and enjoys basketball and snowboarding.

The APIs for three of Apps Script's advanced servicesAnalytics, BigQuery, and Prediction — will undergo breaking changes on Monday, November 18. If you don't update your code to the new syntax before then, you'll receive error messages such as Required parameter is missing.

Advanced services allow you to easily connect to certain public Google APIs from Apps Script. We're working to expand and improve our advanced services, and as a side effect some methods and parameters that were incorrectly listed as optional are now required.

On November 18, these services will switch to use the new method signatures shown in the tables below. To learn how new arguments should be structured, refer to the documentation for the underlying API. For example, the documentation for the BigQuery service's Jobs.query() method shows the valid properties for the resource object in the "Request body" section of the page.


Old New
Analytics.Management.Uploads
.deleteUploadData(
    accountId, 
    webPropertyId, 
    customDataSourceId, 
    optionalArgs)
.deleteUploadData(
    resource, 
    accountId, 
    webPropertyId, 
    customDataSourceId)
BigQuery.Datasets
.insert(
    resource, 
    optionalArgs)
.insert(
    resource, 
    projectId)
.update(
    resource,
    optionalArgs)
.update(
    resource, 
    projectId,
    datasetId)
BigQuery.Jobs
.insert(
    resource, 
    mediaData,
    optionalArgs)
.insert(
    resource, 
    projectId, 
    mediaData)
.query(
    projectId,
    query)
.query(
    resource,
    projectId)
BigQuery.Tabledata
.insertAll(
    projectId,
    datasetId, 
    tableId,
    optionalArgs)
.insertAll(
    resource,
    projectId, 
    datasetId, 
    tableId)
BigQuery.Tables
.insert(
    resource,
    optionalArgs)
.insert(
    resource, 
    projectId,
    datasetId)
.update(
    resource, 
    optionalArgs)
.update(
    resource, 
    projectId,
    datasetId,
    tableId)
Prediction.Hostedmodels
.predict(
    project, 
    hostedModelName, 
    optionalArgs)
.predict(
    resource,
    project, 
    hostedModelName)
Prediction.Trainedmodels
.insert(
    project, 
    optionalArgs)
.insert(
    resource,
    project)
.predict(
    project, 
    id, 
    optionalArgs)
.predict(
    resource, 
    project, 
    id)
.update(
    project, 
    id, 
    optionalArgs)
.update(
    resource, 
    project, 
    id)

If you want to prepare your code ahead of time, you can add a try/catch around your existing code that retries with the new method signature if the old one fails. For example, the following sample applies this approach to the BigQuery service's Jobs.query() method:

  var result;
  try {
    result = BigQuery.Jobs.query(projectId, query, {
      timeoutMs: 10000
    });
  } catch (e) {
    // Refer to the BigQuery documentation for the structure of the 
    // resource object.
    var resource = {
      query: query,
      timeoutMs: 1000
    };
    result = BigQuery.Jobs.query(resource, projectId);
  }

We apologize for inconvenience and look forward to sharing exciting news about advanced services in the coming weeks.


Eric Koleda profile

Eric is a Developer Programs Engineer based in NYC on the Google Apps Script team. He's previously worked with the AdWords API and enterprise content management software.

We launched Actions in the Inbox at Google I/O 2013 as a quick way for users to get things done directly from Gmail. Integrating with this technology only requires adding some markup to an email to define what the message is about and what actions the user can perform.

We support a variety of action types covering common scenarios such as adding a movie to a queue, product reviews, or other even pre-defined requests. Especially popular with senders is the One-Click Action to validate a user’s email address, as shown below:


If you are using Mandrill, the email infrastructure service from MailChimp, writing a Python app to send one of those emails, only takes a few lines of code! Take a look at this example:

import mandrill

# Replace with your own values
API_KEY = 'YOUR_API_KEY'
FROM_ADDRESS = 'YOUR_FROM_ADDRESS'
TO_ADDRESS = 'YOUR_TO_ADDRESS'
SUBJECT = 'Please validate your email address'

HTML_CONTENT = """
<html>
  <body>
    <script type='application/ld+json'>
    {
      "@context": "http://schema.org",
      "@type": "EmailMessage",
      "action": {
        "@type": "ConfirmAction",
        "name": "Confirm Registration",
        "handler": {
          "@type": "HttpActionHandler",
          "url": "https://mydomain.com/validate?id=abc123"
        }
      }
    }
    </script>
    <p>Please click on this link to validate your email address:</p>
    <p><a href="https://mydomain.com/validate?id=abc123">https://mydomain.com/validate?id=abc123</a></p>
  </body>
</html>
"""

# Instantiate the Mandrill client with your API Key
mandrill_client = mandrill.Mandrill(API_KEY)

message = {
 'html': HTML_CONTENT,
 'subject': SUBJECT,
 'from_email': FROM_ADDRESS,
 'to': [{'email': TO_ADDRESS}],
}

result = mandrill_client.messages.send(message=message)

To run this app, just replace the API key with the credentials from your Mandrill account and configure the sender and recipient addresses. You should also edit the HTML content to provide your action handler URL, and customize the messaging if you want.

You can use Actions in the Inbox to reduce the friction and increase the conversion rate. Please note that you are not limited to validating email addresses: you can also review products and services, reply to event invitations, and much more.

For more information, please visit our documentation at https://developers.google.com/gmail/actions. You can also ask questions on Stack Overflow, with the tag google-schemas.

Claudio Cherubino   profile | twitter | blog

Claudio is an engineer in the Gmail Developer Relations team. Prior to Google, he worked as software developer, technology evangelist, community manager, consultant, technical translator and has contributed to many open-source projects. His current interests include Google APIs, new technologies and coffee.

Apps Script started out as a simple tool to let developers add new features to Google Apps, but it’s grown into a programming platform that thousands of professional coders use every day. We hear a couple common requests from developers when they’re building complex projects with Apps Script: they want a full-featured IDE, and they want to sync to external version-control systems like GitHub.

Today, we’re introducing support for Apps Script in the Google Plugin for Eclipse. You can now sync with your existing Apps Script files on Google Drive, edit them in Eclipse — offline, if necessary, and with all the benefits of autocomplete — then write your code back to Drive so you can run it in the cloud. Because the plugin stores a copy of each script in a local workspace, you can manage Apps Script projects with your favorite version-control system.


Getting started is easy:

  1. Install Eclipse, if you don’t already use it. If you already have Eclipse, make sure it’s at least version 3.7 (Indigo) for either Java or Java EE.
  2. In Eclipse, select Help > Eclipse Marketplace, then install the Google Plugin for Eclipse (or see our Getting Started guide for alternate installation instructions).
  3. Click Sign in to Google in the bottom-right corner of Eclipse, then enter your username and password.
  4. Select File > Import to transfer your projects into Eclipse. Whenever you’re online, the plugin will automatically sync your local edits with Google Drive.

For step-by-step instructions on installation and use, see the documentation on using Apps Script with the Google Plugin for Eclipse.

Just in case you were wondering, the plugin uses the public Google Drive SDK to sync Apps Script files between your local file system and Google’s servers. We’ve previously covered the techniques it uses in our documentation on importing and exporting projects and the recent episode of Apps Script Crash Course on Google Developers Live below.




Norman Cohen   profile

Norman Cohen is a software engineer based in Google’s New York office. He works primarily on the Google Plugin for Eclipse, focusing on improving developer experience in the cloud.

Last month, we announced several new ways to customize Google Forms. As of this week, three of those options are also available in forms created from Apps Script — embedding YouTube videos, displaying a progress bar, and showing a custom message if a form isn’t accepting responses.


Adding a YouTube video is as simple as any other Google Forms operation in Apps Script — from the Form object, just call addVideoItem(), then setVideoUrl(youtubeUrl). Naturally, you can also control the video’s size, alignment, and so forth.

To show a progress bar, call setProgressBar(enabled). Don’t even need a second sentence to explain that one. The custom message for a form that isn’t accepting responses is similarly easy: setCustomClosedFormMessage(message), and you’re done.

Want to give it a try yourself? Copy and paste the sample code below into the script editor at script.google.com, then hit Run. When the script finishes, click View > Logs to grab the URL for your new form, or look for it in Google Drive.

function showNewFormsFeatures() {
  var form = FormApp.create('New Features in Google Forms');
  var url = form.getPublishedUrl();
  
  form.addVideoItem()
    .setVideoUrl('http://www.youtube.com/watch?v=38H7WpsTD0M');
  
  form.addMultipleChoiceItem()
    .setTitle('Look, a YouTube video! Is that cool, or what?')
    .setChoiceValues(['Cool', 'What']);

  form.addPageBreakItem();
  
  form.addCheckboxItem()
    .setTitle('Progress bars are silly on one-page forms.')
    .setChoiceValues(['Ah, that explains why the form has two pages.']);
  
  form.setProgressBar(true);

  form.setCustomClosedFormMessage('Too late — this form is closed. Sorry!');
  // form.setAcceptingResponses(false); // Uncomment to see custom message.

  Logger.log('Open this URL to see the form: %s', url);
}

Dan Lazin   profile | twitter

Dan is a technical writer on the Developer Relations team for Google Apps Script. Before joining Google, he worked as video-game designer and newspaper reporter. He has bicycled through 17 countries.

Security is a top priority for Google, just as it is for many of our Google Apps customers. As a domain administrator, a big part of keeping your users safe is knowing when and how they are using their accounts. Since we launched the Admin SDK Reports API in June, we've continued to add features to let you more easily visualize Google Apps' usage and security in your domain. These new features include:

Security Reports

  • Login audit—View all web browser based logins with IP information for all users in your domain. You can use this data to monitor all successful, failed, and suspicious logins in your domain.
  • Authorized applications—View a list of third-party applications that users in your domain have shared data with. Gain visibility into how many users are accessing each application. Revoke access to specific apps using the security tab on Admin console.

Usage Reports

  • Storage quota—View user-level quota usage. This is available both as total usage and split by Gmail, Drive and Google+ photos for every user in the domain. Monitor which users are nearing their quota limits and acquire more storage if necessary.
  • Google+ usage—View 1-day, 7-day and 30-day active Google+ usage in your domain. See the number of Hangouts attended by users in your domain.

Refer to the Reports API documentation for more details on how to use the Reports API and to see what is possible with all of our entire Admin SDK.


Rishi Dhand profile

Rishi Dhand is a Product Manager on the Google Apps for Business team. In addition to working on data migration features, he also works on the Google Apps administration platform with focus on building new security and admin reporting features. In his free time, he enjoys playing squash and badminton.

Drive is a great drop zone for incoming files -- no matter if they’re coming from cameras, scanners, faxes, or countless other devices or apps. But throwing files into the root folder makes it difficult for users to find and organize their content.

I’ve seen developers create a folder when the user first connects the app with Drive to keep files organized by the app that created it. It’s a simple technique that is easy to implement and good default behavior for most applications.

With the Picker API, we can go a step further and offer users the choice of destinations too. For example, I’d like my scanner to put work files in one folder and personal files in another, or even configure multiple destinations depending on the type of document I’m scanning.

For this particular use case, the picker needs to be configured to show folders and only show folders. That requires customizing the DocsView just a little.

var docsView = new google.picker.DocsView()
  .setIncludeFolders(true) 
  .setMimeTypes('application/vnd.google-apps.folder')
  .setSelectFolderEnabled(true);

By enabling folders & the ability to select them while simultaneously filtering out everything else, users can quickly and easily select one of their existing folders as a destination. The rest of the code to show the picker is par for the course.

// Handle user actions with the picker.
var callback = function(data) {
  if (data.action == google.picker.Action.PICKED) {
    var doc = data.docs[0];
    alert("You picked " + doc.id);
  }
};

var picker = new google.picker.PickerBuilder()
  .addView(docsView)
  .setCallback(callback)
  .build();

picker.setVisible(true);

Not only is this easy to implement, it’s safer for users. By offloading this functionality to the Picker API, an app only needs the drive.file scope to write files into the user’s preferred location.

You can learn more about the Picker API at developers.google.com or ask questions at StackOverflow with the google-drive-sdk tag.



Steven Bazyl   profile | twitter

Steve is a Developer Advocate for Google Drive and enjoys helping developers build better apps.

Google+ make it easy for Google Apps customers to connect and share within their organisation and encourage collaboration between teams. Today we’re launching an update to the Google+ Android app that includes a number of new features for Google Apps customers, and a new developer offering, the Google+ Domains API.

The Google+ Domains API allows Google Apps customers to integrate Google+ into their existing tools and processes, and allows enterprise software vendors to access Google+ from their products. Applications using the Google+ Domains API can act on behalf of Google Apps users to share posts within the same domain, comment on posts shared within the domain, and manage Circles. In addition, the Google+ Domains API enables Google Apps domain administrators to pre-populate the Circles of new employees, or review sharing activity.

For example, Ocado is building a tool that uses the Google+ Domains API to regularly sync team membership stored in Active Directory with the circles of their employees. This will ensure that every employee always has an up to date circle containing the other members of their team. Cloudlock is using the Google+ Domains API to add support for Google+ to its suite of data loss prevention, governance, and compliance applications.


Any developer can begin developing with the Google+ Domains API today. However only members of a Google Apps domain can use Google+ Domains API applications. To get started check out the documentation. If you have any questions, you can consult the google-plus tag on Stack Overflow, or join the “Developing with Google+” Google+ Community.


Posted by Thor Mitchell, Product Manager, Google+ API

Cross posted on the Google+ Developers blog.

Google Apps Script is, first and foremost, a tool for making Google Apps more powerful — and today’s addition of programmatic control over data-validation rules in Google Sheets is a perfect example. For a quick demo, make a copy of this spreadsheet, then follow the instructions provided.


For the last few months, scriptable access to the data validation feature in Sheets has been the most requested feature on the Apps Script issue tracker. A common use for data-validation rules is to require that a cell’s value match one of the values in a different range. The following example shows how to achieve that goal with Apps Script. First, we use the newDataValidation() method to construct a DataValidationBuilder, then set the appropriate options and apply the final DataValidation with setDataValidation().

// Set the data-validation rule for cell A1 to require a value from B1:B10.
var cell = SpreadsheetApp.getActive().getRange('A1');
var range = SpreadsheetApp.getActive().getRange('B1:B10');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range)
    .build();
cell.setDataValidation(rule);

It’s also possible to modify existing data-validation rules. The next example changes rules that require a date in 2013 to require a date in 2014 instead. You’ll see that the script calls getDataValidations() to retrieve the existing rules, then uses getCriteriaType(), getCriteriaValues(), and the DataValidationCriteria enum to examine the rules before applying the new date restriction via the advanced withCriteria() method.

// Change existing data-validation rules that require a date in 2013
// to require a date in 2014.
var oldDates = [new Date('1/1/2013'), new Date('12/31/2013')];
var newDates = [new Date('1/1/2014'), new Date('12/31/2014')];
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns());
var rules = range.getDataValidations();

for (var i = 0; i < rules.length; i++) {
  for (var j = 0; j < rules[i].length; j++) {
    var rule = rules[i][j];

    if (rule != null) {
      var criteria = rule.getCriteriaType();
      var args = rule.getCriteriaValues();

      if (criteria == SpreadsheetApp.DataValidationCriteria.DATE_BETWEEN
          && args[0].getTime() == oldDates[0].getTime() 
          && args[1].getTime() == oldDates[1].getTime()) {
        rules[i][j] = rule.copy().withCriteria(criteria, newDates).build();
      }
    }
  }
}
range.setDataValidations(rules);

With this new feature in Apps Script, you should find it much easier to manage complex data-validation scenarios. Please keep the feature requests coming!


Asim Fazal   profile

Asim is a software engineer on the Google Sheets team in New York — and an enthusiastic occasional contributor to the Apps Script team.

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.


The old way — and the new.

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.


Steve Lieberman   profile

Steve is an engineer on the Apps Script team in NYC. Before joining Google, he developed financial-trading systems and researched automatically-parallelizing compilers.

Today, we are pleased to announce a new release of the Email Migration API. This version of the API provides a simple RESTful interface with several enhancements that make it even easier to write client applications to migrate email to Google Apps.

This API is now part of our Admin SDK, which is useful for managing applications for Google Apps users through the Google APIs Console. Additionally, the Email Migration API v2 now includes support for:
  • Delegated administrators
  • OAuth 2.0
  • Media uploads
The new API also provides support for migrating additional email metadata (such as folders and labels) from existing email systems into Google Apps. For example, a user’s email stored in a nested folder named engineering/backend-support can be migrated with the label engineering-backend-support to retain the previous organization structure.

For more information about the API, visit the the Getting Started guide or explore the API Reference.

Greg Knoke Google+

Greg Knoke is a technical writer in the Google Drive Developer Relations Team. Prior to joining Google, he worked as a scientist developing image and signal processing algorithms. His current interests include new technologies, content management, information architecture, cooking, music, and photography.

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:


  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.



Steven Bazyl   profile | twitter

Steve is a Developer Advocate for Google Drive and enjoys helping developers build better apps.

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.

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:

  • The ability to alter parsed XML content and save it back to a string.
  • Access to all entity types in the XML document (CDATA sections, Comments, etc.)
  • More control over the formatting of the XML string.

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.


Example #1: Selection Translator

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:

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);

Example #2: Bibliography App

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:

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.


Example #3: Cursor Inspector

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!


Kalyan Reddy profile | Stack Overflow

Kalyan is a Developer Programs Engineer on the Google Apps Script team in New York City. He is committed to increasing developers’ productivity by helping them fully utilize the power of Apps Script. In his free time, he enjoys participating in the maker community and hacking together robots.

Flubaroo, a popular Apps Script application that helps teachers with grading, has just reached version 3.0. The new features and improvements include:

  • Smarter emailing of grades.
  • Option to email "Help Tips" for each question.
  • Option to send students individualized feedback.
  • Multi-language support, with first language of Spanish.
  • Easier to read grade emails.

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.


Dave Abouav   profile

Dave is a Googler who also teaches physics at community college at nights. He developed Flubaroo as his 20%-time project, and runs edCode.org, a community of teachers and developers creating free, open-source tools for education.

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.

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.

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.

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.



Steven Bazyl   profile | twitter

Steve is a Developer Advocate for Google Drive and enjoys helping developers build better apps.

Editor’s Note: Guest author Niels Buekers is a Google Apps consultant at Capgemini Belgium. — Arun Nagarajan

During a recent Google Apps migration project, we received several requests to create custom groups of contacts so that users could more easily email frequent collaborators. Before switching to Google Apps, users created their own private distribution lists — but this approach led to overlapping groups that quickly fell out of sync.

The problem was a perfect case for Google Apps Script. We built a great solution that gives users as much power as possible with just a quick administrator review.


The situation before: either manually adding each contact or using a private contacts group.


Solution overview

To start the process, a user adds a specific label to a Gmail message. A script that runs on a timed trigger then generates a request to create a group for all the addresses in the message. The script writes this data to a spreadsheet that tracks group names and administrator approval.

/**
 * Retrieves all 'group_request' threads and creates a request.
 */
function processInbox() {
  // Get threads that have the group_request label.
  var groupRequestLabel = GmailApp.getUserLabelByName('group_request');
  var threads = groupRequestLabel.getThreads(0, 10);
  
  // For each thread, retrieve all recipients and create a group request.
  for (var i = 0; i < threads.length; i++) {
    var firstMessage = threads[i].getMessages()[0];
    var sender = firstMessage.getFrom();
    var recipients = [];
    
    // Add sender.
    recipients.push(parseAddresses(sender));
    
    // Add recipients.
    if (threads[i].getMessages()[0].getTo()) {
      var toRecipients = parseAddresses(firstMessage.getTo());
      recipients.push(toRecipients);
    }
    
    // Add CCs.
    if (threads[i].getMessages()[0].getCc()){
      var ccRecipients = parseAddresses(firstMessage.getCc());
      recipients.push(ccRecipients);
    }

    // Write all recipients to a cell in the spreadsheet
    // and send emails to ask for group name and approval.
    createGroupRequestForRecipients(recipients,
        Session.getActiveUser().getEmail());
    
    // Remove label from this thread now that it has been processed.
    threads[i].removeLabel(groupRequestLabel);
  }
};

Handling the request

Once the request has been processed and written to the spreadsheet, the script sends the user an email that asks her to suggest a name for the group in an Apps Script web app. A second email asks the administrator to visit the web app to approve or decline the request. The results are again stored in the spreadsheet.

The spreadsheet contains a second script, which is triggered for each modification. Once the script confirms that the request has been approved, it uses the Apps Script Domain Service to create the new group.

/**
 * Creates a new group in the Google Apps cPanel with the provided name
 * and members.
 */
function createGroupWithAddresses(addresses,groupName){
  var group = GroupsManager.createGroup(groupName, groupName, groupName,
    GroupsManager.PermissionLevel.DOMAIN);
  var splitAddresses = addresses.split(',');
  for (var i = 0; i < splitAddresses.length; i++) {
    Logger.log('Adding ' + splitAddresses[i]);
    group.addMember(splitAddresses[i]);
  }
};

The result after successfully running the script.

This solution provides a simple way for users to request new Google groups, without all the overhead of manually creating an admin-managed distribution list.


Niels Buekers   profile | Twitter

Niels is a Google Apps consultant at Capgemini Belgium, with interest in both the technical track and change management. He recently visited Google’s London office to participate in a Google Apps Script hackathon, which resulted in the above solution. Niels is a strong believer in cloud solutions and loves to spread the word about Google Apps.

Editor’s Note: Guest author Arnaud Breton is a co-founder of UniShared — Nicolas Garnier

A few weeks ago, Clément and I started to take online courses (based on videos), mainly on Coursera, edX (the MIT-Harvard venture), and Udacity, the biggest online course providers.

Right from the start, we found that it was really painful to take digital notes while watching the video lectures: we had to switch between multiple windows, struggling to interact with them (no shortcuts to play/pause, etc) and no ways of matching the notes to videos... Finally we ended up taking notes with the old fashioned and archaic pens & papers.

After giving it a thought, we were convinced that we could make this much better. We both live in Mountain View, fifteen minutes away from the Google HQ, where a Google Drive hackathon was taking place so we decided to take this opportunity to find and build a solution to our problem. And we came up with the idea of a simple Google Drive app, which would embed both the video lectures and the notes, in the same window, letting us leverage all the shortcuts. Additionally, it would enable us to synchronize the notes with the videos, to be able to smoothly go to the related part of the videos while studying our notes. During the two days - and two nights - we developed that missing app, helped by the amazing Googlers from the Drive team and meeting other amazing people who came to also give birth to their ideas or to help us.

As a tech guy, it was a new exciting challenge to build this app, especially in such a short time. Fortunately, leveraging Google robust and powerful infrastructure and frameworks made it possible. It seemed obvious for us to leverage the Google Drive and Youtube APIs, to host our app on Google App Engine and to build it with the amazing AngularJS.

At the end of the hackathon, we managed to get a viable app, enabling students to create, save and synchronize their notes and videos. We were even rewarded by the Drive team who gave us a prize for our efforts!

VideoNot.es was born!


A few hours later, we started to spread the word about our new app and were convinced that it could be as useful for other students as it was for us. Immediately, hundreds of students started to use it, giving us useful feedback on what mattered the most to them. Based on their feedback, we continued to improve our app, with the number of users increasing. Today, one month after the hackathon, more than 300 VideoNotes are created daily all around the world, and the number continues to grow day after day!

We also got featured on The Next Web describing VideoNot.es as “a really useful tool to make, store and share notes about online videos as you watch them”

So, what’s next for us?
Obviously, continuing to improve the app based on feedback is our top priority.

Also, we have discovered new use-cases where VideoNot.es can be useful. For example journalists contacted us to tell us that they use it to write articles based on interview records. But also people in charge of the transcription of videos are also very excited about VideoNot.es.


It got us more convinced than ever that note-taking can be drastically improved, especially by adapting it to its learning context. We want to go even further. Bringing back social interactions in online classrooms, letting students easily share their notes, again leveraging Google Drive was the first step. Adding real-time collaboration and Q&A features is the second step and we will definitely take advantage of the Google Drive Realtime API for this.

Thanks a lot to the Drive team who organized this hackathon to give our idea a try through their amazing tools! And this is just the beginning of the adventure, that started from a problem we faced, whose solution has been found at the Google hackathon, and which is just starting to reach its potential.

Arnaud Breton profile

Arnaud Breton is passionate about the impact that technologies have on the world.  He is the co-founder and CTO of UniShared / VideoNot.es.

Editor’s Note: Guest author Jason Gordon is a co-founder of Beth Macri Designs — Arun Nagarajan

Beth Macri Designs creates jewelry from the point of view of a structural engineer. The forms are designed using generative 3D software systems and materialized using 3D printing technologies. Our company understands that to make beautiful fine jewelry, 3D printing is only the first step; traditional jewelry craft is then employed for final production. After our first product, The Hidden Message Necklace, was recently featured on The View as part of its Valentine's Day Gift Guide, we had a lot of orders to ship out. As soon as the mail leaves the building, though, the process is literally out of our hands: something unexpected was bound to happen to at least one or two packages. Several package-tracking services exist, but getting the names and tracking numbers into them was a cut-and-paste operation.

I knew that all of the tracking numbers were being delivered by email and I had already set up a Gmail filter to archive them and apply a label. With a little help from Google Apps Script, I knew I could automatically parse those emails and add them to my account on PackageTrackr (which syncs to their newer service, Fara).

The script supports reading emails from multiple shipping providers and is set up so one could easily add more. Every 30 minutes on a time-driven trigger, using the Gmail service, the script runs and looks through unread emails from the shipping provider label, then parses the name and tracking number out of each one. The provider, tracking number, and recipient are stored in a JavaScript array.

function getUSPSConversations(){
  return GmailApp.search("in:usps is:unread subject:(Click-N-Ship)");
}

function matchUSPSHTML(data){
  var out = [];
  var track_num = data.match( 
      /TrackConfirmAction\Winput\.action\WtLabels\=(\d+)/g);
  var to = data.match(/Shipped.to.*[\r\n]*.*>([a-zA-Z\s-_]*)<br>/g);
  for(i in track_num){
    var o = new Object();
    var track = track_num[i].match(/(\d+)/g);
    var person = to[i].match(/>([a-zA-Z\s-_]+)<br>/);
    var myPerson = person[1].replace(/(\r\n|\n|\r)/gm,"")
    o["number"]=track[0];
    o["carrier"]="USPS";
    o["person"]=myPerson;
    out.push(o);
  }
  return out;
}

You can parse all of your different shipping providers in one run of the script. After all of the shipment emails are read, it composes an email to PackageTrackr to give it all of the tracking numbers it just harvested.

var user = Session.getActiveUser().getEmail();
if(data.length > 0){    
  for(d in data){
    body += this["formatForPackageTrackr"](data[d]["number"], 
        data[d]["carrier"], data[d]["person"]);
  }
  
  GmailApp.sendEmail("track@packagetrackr.com", "Add Packages",
      body, {bcc: user});
}

function formatForPackageTrackr(tracking_num, service, person){
  return "#:" + tracking_num + " " + service + " " + person + "\n";
}

Down the line, other shipping providers could be added such as UPS and Fedex. Additionally, more tracking services could be added instead of just PackageTrackr.


Jason Gordon   profile

Jason Gordon is a co-founder at jewelry startup Beth Macri Designs. He is responsible for software development, logistics and e-commerce. While working at Beth Macri Designs, Jason gets to find creative ways to put his software development skills to work to improve logistics and user experience.


Earlier this month, we announced that storage quota is now shared between Google Drive, GMail, and Google+ photos. As part of this change, the Google Drive API and the Google Documents List API will be updated over the next few weeks to start returning the updated storage quota information.

In the Google Drive API, this appears in the about collection.

GET https://www.googleapis.com/drive/v2/about


{
 "kind": "drive#about",
 "quotaBytesTotal": 16106127360,
 "quotaBytesUsed": 17936436,
 …
}


In the Documents List API, this appears in the metadata feed.

GET https://docs.google.com/feeds/metadata/default


<entry>
 <gd:quotaBytesTotal>16106127360</gd:quotaBytesTotal>
 <gd:quotaBytesUsed>17936436</gd:quotaBytesUsed>
 …
</entry>

The new values in the quotaBytesTotal field reflect total storage quota across all the unified products. If you rely on this value, you may notice a change in your apps, but we expect it to behave as if a user has just purchased more storage.

The new values for quotaBytesUsed field will reflect the total amount of storage used across the unified products.  (This field used to reflect the total amount of storage used by Google Drive.)



Ali Afshar profile | twitter

Tech Lead, Google Drive Developer Relations. As an eternal open source advocate, he contributes to a number of open source applications, and is the author of the PIDA Python IDE. Once an intensive care physician, he has a special interest in all aspects of technology for healthcare