IFRAME
var html = HtmlService.createHtmlOutputFromFile('foo'); html.setSandboxMode(HtmlService.SandboxMode.IFRAME);
We couldn’t resist using the new HTML service ourselves, so we’ve published an alternate version of the Google Translate add-on on GitHub that uses the iframe sandbox mode as well as Polymer and material design concepts.
With the launch of the IFRAME sandbox, it is now time to bid goodbye to UiApp. The UI service was a very useful way to serve user interfaces in Docs and Sheets at a time when sandboxing technologies were still evolving. Effective today, UI service is deprecated. It will be removed from documentation and autocomplete in the script editor on June 30, 2015. If you have built any UIs using UiApp, they will continue to work.
UiApp
<script type="application/ld+json"> { "@context": "http://schema.org", "@type": "FoodEstablishmentReservation", "reservationNumber": "WTA1EK", "reservationStatus": "http://schema.org/Confirmed", . . . information about dining customer . . . "reservationFor": { "@type": "FoodEstablishment", "name": "Charlie’s Cafe", "address": { "@type": "PostalAddress", "streetAddress": "1600 Amphitheatre Parkway", "addressLocality": "Mountain View", "addressRegion": "CA", "postalCode": "94043", "addressCountry": "United States" }, "telephone": "+1 650 253 0000" }, "startTime": "2015-01-01T19:30:00-07:00", "partySize": "2" } </script>
DocsListDialog is a widget used by only a small fraction of Apps Script projects to provide a Google Drive "file open" dialog in a UI service user interface. In almost all cases, using Google Picker in HTML service is preferable and more secure.
DocsListDialog
Before September 30, 2014, we require scripts using DocsListDialog to make a small update to improve security.
Specifically, if you use DocsListDialog, you'll need to start calling a new method, setOAuthToken(oAuthToken) before you call showDocsPicker(). The new method sets an OAuth 2.0 token to use when fetching data for the dialog, on behalf of the user whose content should be shown.
setOAuthToken(oAuthToken)
showDocsPicker()
So long as the app isn't a web app set to execute as "me" (the developer), you can get the necessary OAuth 2.0 token by calling ScriptApp.getOAuthToken(). The example below shows how to convert an old DocsListDialog implementation to the new model.
ScriptApp.getOAuthToken()
function showDialog() { var app = UiApp.createApplication(); app.createDocsListDialog() .addCloseHandler(serverHandler) .addSelectionHandler(serverHandler) .showDocsPicker(); SpreadsheetApp.getUi() .showModalDialog(app,' '); }
function showDialog() { var app = UiApp.createApplication(); app.createDocsListDialog() .addCloseHandler(serverHandler) .addSelectionHandler(serverHandler) .setOAuthToken(ScriptApp.getOAuthToken()) .showDocsPicker(); SpreadsheetApp.getUi() .showModalDialog(app,' '); }
To ensure your script continues to work properly, be sure to make this change before September 30.
Posted by Dan Lazin, Googler
string CreateFile(Stream content, string fileName, string mimeType, string folderId, string accessToken){ var bytes = Encoding.UTF8.GetBytes( "\r\n--boundary\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n{\"title\":\"" + fileName + "\",\"parents\":[{\"id\":\"" + folderId + "\"}]}" + "\r\n--boundary\r\nContent-Type: " + mimeType + "\r\n\r\n"); var tmpStream = new MemoryStream(); tmpStream.Write(bytes, 0, bytes.Length); content.CopyTo(tmpStream); bytes = Encoding.UTF8.GetBytes("\r\n--boundary--\r\n"); tmpStream.Write(bytes, 0, bytes.Length); var request = WebRequest.Create("https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart"); request.Method = "POST"; request.Headers.Add("Authorization", "Bearer " + accessToken); request.ContentType = "multipart/related; boundary=boundary"; request.ContentLength = tmpStream.Length; var buffer = new byte[2048]; int readed; tmpStream.Seek(0, SeekOrigin.Begin); while ((readed = tmpStream.Read(buffer, 0, 2048)) > 0) { request.GetRequestStream().Write(buffer, 0, readed); } return request.GetResponse().GetResponseStream(); }
final RateLimiter driveApiRateLimiter = RateLimiter.create(QUOTA_RATE_PER_SECOND);
public Result performDriveApiCall(driveApiRateLimiter, otherParams){ driveApiRateLimiter.acquire(); // blocks according to rate // make API call...
1, 2, 4, 8, 16, 32, 60, 60, 60
ExponentialBackOff backoff = new ExponentialBackOff.Builder() .setInitialIntervalMillis(ONE_SECOND) .setMultiplier(2.0) .setMaxIntervalMillis(ONE_MINUTE) .setMaxElapsedTimeMillis(FIVE_MINUTES) .build();
1.04, 1.9, 4.23, 7.8, etc.
.98, 2.04, 4.1, 8.15, etc.
builder.setRandomizationFactor(RANDOMIZATION_FACTOR);
private HttpBackOffUnsuccessfulResponseHandler handler = new HttpBackOffUnsuccessfulResponseHandler(backoff); public void initialize(HttpRequest request){ request.setUnsuccessfulResponseHandler(handler); }
Editor’s Note: Guest author Alex Moore is the CEO of Baydin, an email productivity company. --Arun Nagarajan
var d = new Date(); d.setDate(d.getDate() - DAYS_TO_SEARCH); var dateString = d.getFullYe ar() + "/" + (d.getMonth() + 1) + "/" + d.getDate(); threads = GmailApp.search("in:Sent after:" + dateString);
var userEmailAddress = Session.getEffectiveUser().getEmail(); var EMAIL_REGEX = /[a-zA-Z0-9\._\-]+@[a-zA-Z0-9\.\-]+\.[a-z\.A-Z]+/g; # if the label already exists, createLabel will return the existing label var label = GmailApp.createLabel("AwaitingResponse"); var threadsToUpdate = []; for (var i = 0; i < threads.length; i++) { var thread = threads[i]; var lastMessage = thread.getMessages()[thread.getMessageCount()-1]; lastMessageSender = lastMessage.getFrom().match(EMAIL_REGEX)[0]; if (lastMessageSender == userEmailAddress) { threadsToUpdate.push[thread]; } } label.addToThreads(threads)
In the last few months, we've added a number of new features to Google Apps Script, including add-ons for Sheets and Docs and 7 new advanced services.
We're eager to maintain that momentum — focusing on new features that help you do more with Google Apps. As a result, we're deprecating two Apps Script services for which good replacements exist elsewhere: ScriptDB (a NoSQL database that has been marked as experimental since it was introduced) and the Domain service (which encapsulates the GroupsManager, NicknameManager, and UserManager global objects).
GroupsManager
NicknameManager
UserManager
Both ScriptDB and the Domain service will be turned off on November 20, 2014.
Before then, you'll need to port any ScriptDB projects to another data store, like Google Cloud SQL or a third-party NoSQL database. We've created a migration guide that explains how to export your data from ScriptDB and suggests a few alternate data stores. We have also improved the documentation for connecting to external databases through JDBC to make it a little easier for you to set up Cloud SQL with Apps Script.
The Domain service, which only Google Apps domain administrators can use, is replaced by the recently added Admin SDK Directory and Admin SDK Reports advanced services. Those advanced services also provide many new features that the Domain service does not — like managing users' devices, OAuth tokens, and application-specific passwords — so we expect that you'll prefer using them in the future.