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