var BAR_ID = 'PROGRESS_BAR_ID'; var BAR_HEIGHT = 10; // px var presentation = SlidesApp.getActivePresentation(); function createBars() { var slides = presentation.getSlides(); deleteBars(); for (var i = 0; i < slides.length; ++i) { var ratioComplete = (i / (slides.length - 1)); var x = 0; var y = presentation.getPageHeight() - BAR_HEIGHT; var barWidth = presentation.getPageWidth() * ratioComplete; if (barWidth > 0) { var bar = slides[i].insertShape(SlidesApp.ShapeType.RECTANGLE, x, y, barWidth, BAR_HEIGHT); bar.getBorder().setTransparent(); bar.setLinkUrl(BAR_ID); } } }
function onMessage(e) { var bot = e.message.annotations[0].userMention.user.displayName; var loc = encodeURI(e.message.text.substring(bot.length+2)); var mapClick = { "openLink": { "url": "https://google.com/maps/search/?api=1&query=" + loc } }; return { // see JSON payload in the documentation link above }; }
var slide = presentation.appendSlide(SlidesApp.PredefinedLayout.BLANK); var image = slide.insertImage(link);
var TIMEZONE = "America/Los_Angeles"; var EVENT = { "start": {"dateTime": "2017-07-01T19:00:00", "timeZone": TIMEZONE}, "end": {"dateTime": "2017-07-01T22:00:00", "timeZone": TIMEZONE}, "recurrence": ["RRULE:FREQ=MONTHLY;INTERVAL=2;UNTIL=20171231"] };
GCAL.events().patch(calendarId='primary', eventId=EVENT_ID, sendNotifications=True, body=EVENT).execute()
function createGradedCheckboxQuestionWithAutofeedback() { // Make sure the form is a quiz. var form = FormApp.getActiveForm(); form.setIsQuiz(true); // Make a 10 point question and set feedback on it var item = FormApp.getActiveForm().addCheckboxItem(); item.setTitle("What flavors are in neapolitan ice cream?"); item.setPoints(10); // chocolate, vanilla, and strawberry are the correct answers item.setChoices([ item.createChoice("chocolate", true), item.createChoice("vanilla", true), item.createChoice("rum raisin", false), item.createChoice("strawberry", true), item.createChoice("mint", false) ]); // If the respondent answers correctly, they'll see this feedback when they view //scores. var correctFeedback = FormApp.createFeedback() .setText("You're an ice cream expert!") .build(); item.setFeedbackForCorrect(correctFeedback); // If they respond incorrectly, they'll see this feedback with helpful links to //read more about ice cream. var incorrectFeedback = FormApp.createFeedback() .setText("Sorry, wrong answer") .addLink( "https://en.wikipedia.org/wiki/Neapolitan_ice_cream", "Read more") .build(); item.setFeedbackForIncorrect(incorrectFeedback); }
{ "repeatCell": { "range": { "endRowIndex": 1 }, "cell": { "userEnteredFormat": { "textFormat": { "bold": true } } }, "fields": "userEnteredFormat/textFormat/bold", } }
"fields": "userEnteredFormat/textFormat(bold,italic)"
insertText
batchUpdate()
objectID
{ "insertText": { "objectId": objectID, "text": "Hello World!\n" }
{ "createShape": { "shapeType": "SMILEY_FACE", "elementProperties": { "pageObjectId": slideID, "size": { "height": { "magnitude": 3000000, "unit": "EMU" }, "width": { "magnitude": 3000000, "unit": "EMU" } }, "transform": { "unit": "EMU", "scaleX": 1.3449, "scaleY": 1.3031, "translateX": 4671925, "translateY": 450150 } } } }
requests
SLIDES
deckID)
SLIDES.presentations().batchUpdate(presentationId=deckID, body=requests).execute()
Posted by Wesley Chun (@wescpy), Developer Advocate, G Suite
It's common knowledge that presentations utilize a set of images to impart ideas to the audience. As a result, one of the best practices for creating great slide decks is to minimize the overall amount of text. It means that if you do have text in a presentation, the (few) words you use must have higher impact and be visually appealing. This is even more true when the slides are generated by a software application, say using the Google Slides API, rather than being crafted by hand.
The G Suite team recently launched the first Slides API, opening up a whole new category of applications. Since then, we've published several videos to help you realize some of those possibilities, showing you how to replace text and images in slides as well as how to generate slides from spreadsheet data. To round out this trifecta of key API use cases, we're adding text formatting to the conversation.
Developers manipulate text in Google Slides by sending API requests. Similar to the Google Sheets API, these requests come in the form of JSON payloads sent to the API's batchUpdate() method. Here's the JavaScript for inserting text in some shape (shapeID) on a slide:
shapeID
{ "insertText": { "objectId": shapeID, "text": "Hello World!\n" }
In the video, developers learn that writing text, such as the request above, is less complex than reading or formatting because both the latter require developers to know how text on a slide is structured. Notice for writing that just the copy, and optionally an index, are all that's required. (That index defaults to zero if not provided.)
Assuming "Hello World!" has been successfully inserted in a shape on a slide, a request to bold just the "Hello" looks like this:
{ "updateTextStyle": { "objectId": shapeID, "style": { "bold": true }, "textRange": { "type": "FIXED_RANGE", "startIndex": 0, "endIndex": 5 }, "fields": "bold" }
To better understand text structure & styling in Google Slides, check out the text concepts guide in the documentation. For a detailed look at the complete code sample featured in the DevByte, check out the deep dive post. To see more samples for common API operations, take a look at this page. We hope the videos and all these developer resources help you create that next great app that automates producing highly impactful presentations for your users!
Email continues to be a dominant form of communication, personally and professionally, and our email signature serves as both a lightweight introduction and a business card. It's also a way to slip-in a sprinkling of your personality. Wouldn't it be interesting if you could automatically change your signature whenever you wanted without using the Gmail settings interface every time? That is exactly what our latest video is all about.
If your app has already created a Gmail API service endpoint, say in a variable named GMAIL, and you have the YOUR_EMAIL email address whose signature should be changed as well as the text of the new signature, updating it via the API is as pretty straightforward, as illustrated by this Python call to the GMAIL.users().settings().sendAs().patch() method:
GMAIL
YOUR_EMAIL
GMAIL.users().settings().sendAs().patch()
signature = {'signature': '"I heart cats." ~anonymous'} GMAIL.users().settings().sendAs().patch(userId='me', sendAsEmail=YOUR_EMAIL, body=signature).execute()
For more details about the code sample used in the requests above as well as in the video, check out the deepdive post. In addition to email signatures, other settings the API can modify include: filters, forwarding (addresses and auto-forwarding), IMAP and POP settings to control external email access, and the vacation responder. Be aware that while API access to most settings are available for any G Suite Gmail account, a few sensitive operations, such as modifying send-as aliases or forwarding, are restricted to users with domain-wide authority.
Developers interested in using the Gmail API to access email threads and messages instead of settings can check out this other video where we show developers how to search for threads with a minimum number of messages, say to look for the most discussed topics from a mailing list. Regardless of your use-case, you can find out more about the Gmail API in the developer documentation. If you're new to the API, we suggest you start with the overview page which can point you in the right direction!
Be sure to subscribe to the Google Developers channel and check out other episodes in the G Suite Dev Show video series.
Formatting spreadsheets is accomplished by creating a set of request commands in the form of JSON payloads, and sending them to the API. Here is a sample JavaScript Object made up of an array of requests (only one this time) to bold the first row of the default Sheet automatically created for you (whose ID is 0):
{"requests": [ {"repeatCell": { "range": { "sheetId": 0, "startRowIndex": 0, "endRowIndex": 1 }, "cell": { "userEnteredFormat": { "textFormat": { "bold": true } } }, "fields": "userEnteredFormat.textFormat.bold" }} ]}
SHEETS.spreadsheets().batchUpdate(spreadsheetId=SHEET_ID, body=requests).execute()
For more details on the code in the video, check out the deepdive blog post. As you can probably guess, the key challenge is in constructing the JSON payload to send to API calls—the common operations samples can really help you with this. You can also check out our JavaScript codelab where we guide you through writing a Node.js app that manages customer orders for a toy company, featuring the toy orders data we looked at today but in a relational database. While the resulting equivalent Sheet is featured prominently in today's video, we will revisit it again in an upcoming episode showing you how to generate slides with spreadsheet data using the new Google Slides API, so stay tuned for that!
We hope all these resources help developers enhance their next app using G Suite APIs! Please subscribe to our channel and tell us what topics you would like to see in other episodes of the G Suite Dev Show!
// create new slide (title & body layout) { "createSlide": { "slideLayoutReference": { "predefinedLayout": "TITLE_AND_BODY" } } }, // insert text into textbox { "insertText": { "objectId": titleID, "text": "Hello World!" } }, // add bullets to text paragraphs { "createParagraphBullets": { "objectId": shapeID, "textRange": { "type": "ALL" } } }, // replace text "variables" with image { "replaceAllShapesWithImage": { "imageUrl": imageURL, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{COMPANY_LOGO}}" } } }
Posted by Michael Winser, Product Lead, Google Apps and Wesley Chun, Developer Advocate, Google Apps
Last week, we clarified the expectations and responsibilities when accessing Google user data via OAuth 2.0. Today, we’re announcing that in order to better protect users, we are increasing account security for enterprise Gmail users effective October 5, 2016. At this time, a new policy will take effect whereby users in a Google Apps domain, while changing their passwords on or after this date, will result in the revocation of the OAuth 2.0 tokens of apps that access their mailboxes using Gmail-based authorization scopes. Please note that users will not notice any specific changes on this date and their applications will continue to work. It is only when a user changes their password from that point moving forward that their Gmail-related tokens become invalid.
Developers should modify their applications to handle HTTP 400 or 401 error codes resulting from revoked tokens and prompt their users to go through the OAuth flow again to re-authorize those apps, such that they can access the user’s mailbox again (additional details below). Late last year, we announced a similar, planned change to our security policy that impacted a broader set of authorization scopes. We later decided not to move forward with that change for Apps customers and began working on a less impactful update as described above.
What is a revoked token?
A revoked OAuth 2.0 token no longer provides access to a user’s resources. Any attempt to use a revoked token in API calls will result in an error. Any existing token strings will no longer have any value and should be discarded. Applications accessing Google APIs should be modified to handle failed API calls.
Token revocation itself is not a new feature. Users have always been able to revoke access to applications in Security Checkup, and Google Apps admins have the ability to do the same in the Admin console. In addition, tokens that were not used for extended periods of time have always been subject to expiration or revocation. This change in our security policy will likely increase the rate of revoked tokens that applications see, since in some cases the process will now take place automatically.
What APIs and scopes are impacted?
To achieve the security benefits of this policy change with minimal admin confusion and end-user disruption, we’ve decided to limit its application to mail scopes only and to exclude Apps Script tokens. Apps installed via the Google Apps Marketplace are also not subject to the token revocation. Once this change is in effect, third-party mail apps like Apple Mail and Thunderbird―as well as other applications that use multiple scopes that include at least one mail scope―will stop accessing data upon password reset until a new OAuth 2.0 token has been granted. Your application will need to detect this scenario, notify the user that your application has lost access to their account data, and prompt them to go through the OAuth 2.0 flow again.
Mobile mail applications are also included in this policy change. For example, users who use the native mail application on iOS will have to re-authorize with their Google account credentials when their password has been changed. This new behavior for third-party mail apps on mobile aligns with the current behavior of the Gmail apps on iOS and Android, which also require re-authorization upon password reset.
How can I determine if my token was revoked?
Both short-lived access tokens and long-lived refresh tokens will be revoked when a user changes their password. Using a revoked access token to access an API or to generate a new access token will result in either HTTP 400 or 401 errors. If your application uses a library to access the API or handle the OAuth flow, then these errors will likely be thrown as exceptions. Consult the library’s documentation for information on how to catch these exceptions. NOTE: because HTTP 400 errors may be caused by a variety of reasons, expect the payload from a 400 due to a revoked token to be similar to the following:
{ "error_description": "Token has been revoked.", "error": "invalid_grant" }
How should my application handle revoked tokens?
This change emphasizes that token revocation should be considered a normal condition, not an error scenario. Your application should expect and detect the condition, and your UI should be optimized for restoring tokens.
To ensure that your application works correctly, we recommend doing the following:
If your application uses incremental authorization to accrue multiple scopes in the same token, you should track which features and scopes a given user has enabled. The end result is that if your app requested and obtained authorization for multiple scopes, and at least one of them is a mail scope, that token will be revoked, meaning you will need to prompt your user to re-authorize for all scopes originally granted.
Many applications use tokens to perform background or server-to-server API calls. Users expect this background activity to continue reliably. Since this policy change also affects those apps, this makes prompt notification requesting re-authorization even more important.
What is the timeline for this change?
To summarize, properly configured applications should be expected to handle invalid tokens in general, whether they be from expiration, non-existence, and revocation as normal conditions. We encourage developers to make any necessary changes to give their users the best experience possible. The policy change is planned to take effect on October 5, 2016.
Please see this Help Center article and FAQ for more details and the full list of mail scopes. Moving forward, any additional scopes to be added to the policy will be communicated in advance. We will provide those details as they become available.
Posted by Pierce Vollucci, Associate Product Manager, Gmail and Steve Bazyl, Developer Programs Engineer, Google Apps
When you send emails, your recipients might read them on a computer, tablet, or phone—or more likely, all three. However your message might look different on all these devices. Later this month, you’ll be able to use CSS media queries with Gmail and Inbox by Gmail to ensure that your message is formatted the way you intended, whether it's viewed on a computer, a phone in portrait mode, or a tablet in landscape mode. You’ll be able to change styles based on width, rotation, and resolution, allowing for more responsive formatting to optimize your email for every device.
In discussions with email designers, these supported CSS rules were identified as the most useful media queries to support responsive design. This is just one part of an overall effort to expand CSS support in Gmail and to give email designers more control over how their messages are rendered. For example, the CSS below applies the color red when the screen width exceeds 500px.
@media screen and (min-width: 500px) { .colored { color:red; } }
You can find the full list of supported CSS rules in the developer documentation. We hope this reference helps you create more feature-rich, responsive email for users. Happy formatting!
Google Calendar and Google Drive for Atlassian HipChat
Guest post by Rich Manalang, Partner Engineering Lead at Atlassian. Posted by Wesley Chun, Developer Advocate, Google Apps.
Atlassian has been building collaboration software for over 14 years. With products that include JIRA, Confluence, Bitbucket, and HipChat, our organization has learned a lot about how teams work effectively.
HipChat launched the Connect API in November 2015, and since then we’ve continued to build upon our ecosystem of integrations and collaborations. A few months ago, our team looked at potential integrations that would be a perfect marriage with HipChat — and today, we’re excited to share the Google Calendar and Google Drive integration for HipChat.
Millions of people use Google’s products everyday, so we instantly knew this was the right opportunity. Many of HipChat’s customers are developers, and they told us that managing time and better access to files were two of the most important things in their day-to-day. Now with Google integrations available inside of HipChat, there’s no need to launch another browser tab or app.
By building Google Calendar directly into HipChat, we’re improving the signal-to-noise ratio on a daily basis. Before this integration, we all dealt with context-switching between apps and browser tabs. Now, customers can use HipChat to view and share various calendars, schedules and important dates in the right sidebar. Our customers spend their entire working day inside our HipChat app — unlike email, you don’t just fire it up and quit periodically. So naturally, having your calendar up-front is compelling. And what’s more, you can slice and dice which ones you see on a per-room basis. Say you’re a program manager — if you go into the Engineering HipChat “room,” you can see the Engineering and related calendars. Then, when you switch into the Marketing room, you may see different calendars depending on whom you’re collaborating with.
Having dual calendars front and center within HipChat is critical for staying on top of my work. I’m personally very excited about the Google Calendar integration because it’s one of the most important apps I use day-in and day-out. As a single parent with two kids busy at school, I need to know everything that's going on. My calendar is stacked, and I want to see it all at a glance. That urgency is similar when considering the most important documents in someone's daily workflow.
When we started working on the Google Drive integration, we wanted to focus on what was most important — accessibility, shareability, and ease of use.
There are many benefits to bringing third party integrations right into HipChat. The Google Drive integration allows teams to collaborate and work together while saving time and eliminating context switching. Being able to access documents, presentations, and files is critical whether a user is at the office or remote. It integrates nicely into the right side bar, enabling users to access, share to the room, and collaborate around important documents, presentations, and spreadsheets. We worked with third party developer Topdox, who was a tremendous partner in bringing this new feature into HipChat. We’re getting great feedback around the speed and simplicity of sharing files without ever having to leave the HipChat application.
Why would Google Developers be interested?
When we built these integrations, we wanted to give our users a nice balance between out-of-the-box usefulness but also ultimate flexibility in which calendars and accounts a user can view. To do that, we wanted one UI that can display multiple calendars from multiple Google accounts — similar to what most Calendar mobile apps do today, including Google’s own mobile Calendar app.
These new integrations were built entirely on top of Google’s API. Google’s Calendar API is a full featured API that gave us everything we needed to create a calendar experience fit for HipChat’s users. On top of that, the API was designed with efficiency in mind with push notifications for changes to resources and incremental syncing to improve performance and bandwidth use.
Building on top of Google APIs has allowed us to think of new ways to bring even tighter integrations with our products along with the myriad of add-ons built by Atlassian’s ecosystem. One idea under consideration is to link JIRA Software and Google Calendar so that all your JIRA issues are overlayed onto a Google Calendar. Then this calendar can be shared with the relevant HipChat room bringing it all together and enabling teams to get more done. We’d love to hear your feedback on this idea.
We think there are many opportunities to improve how teams work together by integrating with Google and Atlassian. You can find out more about Atlassian Connect on our developer’s site and the Google APIs on theirs.