We've just announced Google Docs and Sheets add-ons — new tools created by developers like you that give Google users even more features in their documents and spreadsheets. Joining the launch are more than 60 add-ons that partners have built using Apps Script. Now, we're opening up the platform in a developer-preview phase. If you have a cool idea for Docs and Sheets users, we'd love to publish your code in the add-on store and get it in front of millions of users.
To browse through add-ons for Docs and Sheets, select Get add-ons in the Add-ons menu of any document or spreadsheet. (Add-ons for spreadsheets are only available in the new Google Sheets).
Docs and Sheets add-ons are powered by Google Apps Script, a server-side JavaScript platform that requires zero setup. Even though add-ons are in developer preview right now, the tools and APIs are available to everyone. The only restriction is on final publication to the store.
Once you have a great working prototype in Docs or Sheets, please apply to publish. Scripts that are distributed as add-ons gain a host of benefits:
Thanks to hard work from our developer partners, the add-ons in the store look and feel just like native features of Google Docs and Sheets. We're providing a couple of new resources to help all developers achieve the same visual quality: a CSS package that applies standard Google styling to typography, buttons, and other form elements, and a UI style guide that provides great guidance on designing a Googley user experience.
Add-ons are available in the new version of Google Sheets as a replacement for the older version's script gallery. If you have a popular script in the old gallery, now's a great time to upgrade it to newer technology.
We can't wait to see the new uses you'll dream up for add-ons, and we're looking forward to your feedback on Google+ and questions on Stack Overflow. Better yet, if you're free at noon Eastern time this Friday, join us live on YouTube for a special add-on-centric episode of Apps Unscripted.
Developers like you have built amazing scripts with Apps Script, and we want to make Apps Script even more useful. We've been working hard to add a variety of new APIs and features in Google Apps Script. Today, we're ready to share a few of them with you.
Named ranges are a popular feature of the Spreadsheet service; and now, you’ll be able to do something similar in the Document service. With named ranges, your scripts can tag a section of a Google Doc for later reference. For example, a bibliography script could set a named range on every citation in a document, then easily update the citations in the future.
You can use a similar API to manage bookmarks, too. Unlike named ranges, bookmarks are visible to the user and allow you to link to a particular place in a document.
Also for Google Docs, we've added the server-side methods setCursor(position) and setSelection(range) to change the user's cursor position or active selection, plus a client-side method, google.script.host.editor.focus(), which switches the browser focus from a sidebar or dialog back to the document.
setCursor(position)
setSelection(range)
google.script.host.editor.focus()
Oh, and hey: the Undo command in Google Docs can now revert changes made by a script.
If you've used HTML service much, you'll know that the Caja security sandbox has two modes. NATIVE mode imposes fewer restrictions than EMULATED mode and generally runs faster. As of today, NATIVE is now the default if you have not specified which mode your script should use. In a few edge cases, this may affect how existing web apps operate; if so, simply append .setSandboxMode(HtmlService.SandboxMode.EMULATED) to your HtmlOutput object to restore the old behavior.
NATIVE
EMULATED
.setSandboxMode(HtmlService.SandboxMode.EMULATED)
HtmlOutput
In preparation for future improvements to Apps Script, we've revamped script properties and user properties, combining them into a unified Properties service and adding the notion of document properties, which are (surprise!) specific to a particular document, but shared among all collaborators. The biggest change is that user properties are no longer shared between scripts, but our guide to the Properties service provides all the details. As part of the change, the old ScriptProperties and UserProperties services have been deprecated, although they will continue to function in existing scripts until a sunset date is announced.
We’re excited to bring you these new tools. With these new additions, we have also deprecated Finance service. It will remain available for the next six months but will be turned off on September 26, 2014.
Rails.application.config.middleware.use OmniAuth::Builder do provider :google_oauth2, ENV["GAM_OAUTH_KEY"], ENV["GAM_OAUTH_SECRET"] end
# Subclass the GoogleOauth2 Omniauth strategy for # Google Apps Marketplace V2 SSO. module OmniAuth module Strategies class GoogleAppsMarketplace < OmniAuth::Strategies::GoogleOauth2 option :name, 'google_apps_marketplace' end end end
Rails.application.config.middleware.use OmniAuth::Builder do provider :google_oauth2, ENV["OAUTH_KEY"], ENV["OAUTH_SECRET"], {:scope => ENV["OAUTH_SCOPE"]} provider :google_apps_marketplace, ENV["GAM_OAUTH_KEY"], ENV["GAM_OAUTH_SECRET"], { :scope => ENV["GAM_OAUTH_SCOPE"], :access_type => 'online' }end
Gmail.connect!(:xoauth, 'ben@example.com', { token: authentication.token, secret: authentication.secret, consumer_key: google.key, consumer_secret: google.secret, read_only: true })
key = Google::APIClient::PKCS12.load_key( google_apps.service.p12path, # this is a constant value Google uses # to password protect the key. 'notasecret' )service_account = Google::APIClient::JWTAsserter.new( google_apps.service.email, 'https://mail.google.com/', key )client = Google::APIClient.new( :application_name => APPLICATION_NAME, :version => APPLICATION_VERSION ).tap do |client| client.authorization = service_account.authorize('ben@example.com')end Google.connect!(:xoauth2, 'ben@example.com', { :oauth2_token => client.authorization.access_token, })
# Subclass the GoogleOauth2 Omniauth strategy for # Google Apps Marketplace V2 SSO. module OmniAuth module Strategies class GoogleAppsMarketplace < OmniAuth::Strategies::GoogleOauth2 option :name, 'google_apps_marketplace' def request_phase # Store the opensocial_viewer_id in the session. # this allows us to bind the Google Apps contextual # gadget to a user account. if request.params['opensocial_viewer_id'] session[:opensocial_viewer_id] = request.params['opensocial_viewer_id'] end super end end end end
Since we launched the Admin SDK in May 2013, we’ve been constantly improving it based on your feedback and feature requests. Our first API updates of 2014 bring some features at the top of your requests: push notifications for users, push notifications for activities, and structured user search.
Tired of polling user resources to detect changes? With push notifications for users, you can watch for changes to user resources and receive notifications from the Directory API whenever a watched user resource changes.
Check out our user notifications developer guide to learn how to improve the performances of your application by eliminating the extra network and compute costs involved with polling user resources.
Do you want to be notified when changes to a certain document occur or specific events such as the change of a user’s password happen? With push notifications for activities you can watch for changes to activities resources, and receive notifications from the Reports API whenever a watched activity resource changes.
Check out our activities notifications developer guide to learn how to receive notifications for the activities of your interest.
Do you want to know which users in your domain are members of a certain organization, or which users have a common manager in their reporting chain? The new query parameter we’ve just added to the users.list method of the Directory API allows you to perform rich queries over most attributes from the user profile.
For example, you can now retrieve all the users in the Human Resources org by using the query:
orgName='Human Resources'
manager='janesmith@example.com'
Check out our user search developer guide for a more comprehensive guide on how to build your queries and more examples of what you can do.
public void changeOwner(String user, String fileId, String newOwner) { // Find what is the current permission of the new owner on the file Permission newOwnerPermission = null; PermissionList permissionList = RetriableTask.execute(new DrivePermissionListTask(drive.permissions().list(fileId))); newOwnerPermission = findPermission(permissionList, newOwner); if (newOwnerPermission == null) { // New owner is not in the list, we need to insert it newOwnerPermission = new Permission(); newOwnerPermission.setValue(newOwner); newOwnerPermission.setType("user"); newOwnerPermission.setRole("owner"); Drive.Permissions.Insert insert = drive.permissions().insert(fileId, newOwnerPermission); RetriableTask.execute(new DrivePermissionInsertTask(insert)); } else { // New owner is already in the list, update the existing permission newOwnerPermission.setRole("owner"); Drive.Permissions.Update update = drive.permissions().update(fileId, newOwnerPermission.getId(), newOwnerPermission); update.setTransferOwnership(true); RetriableTask.execute(new DrivePermissionUpdateTask(update)); } }
public class RetriableTask implements Callable { [...] private final Callable task; [...] @Override public T call() { T result = null; try { startTime = System.currentTimeMillis(); result = task.call(); } catch (NonFatalErrorException e) { if (numberOfTriesLeft > 0) { // Wait some time, using exponential back-off in case of multiple attempts Thread.sleep(getWaitTime()); // Try again result = call(); } else { // Too many failed attempts: now this is a fatal error throw new RetryException(); } } catch (FatalErrorException e) { // This one should not be retried Throwables.propagate(e); } return result; }
// Launch user interface and allow user to select file IntentSender i = Drive.DriveApi .newOpenFileActivityBuilder() .setMimeType(new String[] { “text/plain” }) .build(mGoogleApiClient); startIntentSenderForResult(i, REQ_CODE_OPEN, null, 0, 0, 0);