Universal Actions

Universal actions are menu item elements that allow a user to open a new web page, display new UI cards, or run a specific Apps Script function when selected. In operation they are very similar to card actions, except that universal actions are always placed on every card in your add-on, regardless of what Gmail message the user has open.

By using universal actions, you can make sure the user always has access to certain functionality, regardless of which part of your add-on they interact with. Here are some example use cases for universal actions:

  • Open a settings web page (or display a settings card).
  • Show help information to the user.
  • Start a new workflow, such as 'Add new customer'.
  • Display a card that lets a user send feedback about the add-on.

Whenever you have an action that does not depend on the current email context, you should consider making it a universal action.

Using universal actions

Universal actions are configured in your add-on's project manifest. Once you've configured a universal action, it is always available to users of your add-on. If the user is viewing a card, the set of universal actions you've defined always appears in the card menu, after any card actions you've defined for that card. Universal actions appear in the card menus in the same order in which they are defined in the add-on's manifest.

You can also build an add-on that uses universal actions exclusively. Normally when a user opens an add-on, Gmail checks to see if the currently viewed email triggers the contextual trigger defined in the add-on's manifest. If the contextual trigger fires*, the add-on runs the associated trigger function, usually to build and display UI cards.

If you have configured universal actions but do not define a contextual trigger or the one you have defined does not fire for the open email, opening the add-on displays a list of the defined universal actions instead. You do not need to define a card for this purpose; the list is automatically created. The user can then select any of these actions to run script functions, open web pages or display cards, which have the universal actions listed in their menus as well.

Configuring universal actions

You configure universal actions in your add-on's manifest; see the Manifests guide for more details.

For each action, you specify the text that should appear in the menu for that action. You can then specify an openLink field indicating that the action should directly open a web page in a new tab. Alternatively, you can specify a runFunction field that specifies an Apps Script callback function to execute when the universal action is selected.

When runFunction is used, the callback function specified usually does one of the following:

  • Builds UI cards to display immediately by returning a built UniversalActionResponse object.
  • Opens a URL, perhaps after doing other tasks, by returning a built UniversalActionResponse object.
  • Conducts background tasks that do not switch to a new card or open a URL. In this case the callback function returns nothing.

When called, the callback function is passed an event object containing information about the open card and the open Gmail message ID.

Example

The following code snippet shows an example manifest excerpt for an add-on that uses universal actions without any contextual triggers. The code declares a metadata explicit scope so that the add-on can determine who sent the open message.

  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute",
    "https://www.googleapis.com/auth/gmail.addons.current.message.metadata"
  ],
  "gmail": {
    "name": "Universal Actions Only Addon",
    "logoUrl": "https://www.example.com/hosted/images/2x/my-icon.png",
    "openLinkUrlPrefixes": [
      "https://www.google.com",
      "https://www.example.com/urlbase"
    ],
    "universalActions": [{
        "text": "Open google.com",
        "openLink": "https://www.google.com"
      }, {
        "text": "Open contact URL",
        "runFunction": "openContactURL"
      }, {
        "text": "Open settings",
        "runFunction": "createSettingsResponse"
      }, {
        "text": "Run background sync",
        "runFunction": "runBackgroundSync"
    }],
    "version": "TRUSTED_TESTER_V2"
  }

The three universal actions defined in preceding example do the following:

  • Open google.com opens https://www.google.com in a new tab.
  • Open contact URL runs a function that determines what URL to open and then opens it in a new tab using an OpenLink object. The code builds the URL using the sender's email address.
  • Open settings runs the createSettingsCards() function defined in the add-on script project. This function returns a valid UniversalActionResponse object containing a set of cards with add-on setting and other information. After the function finishes building this object, the UI displays the list of cards in the same way that a contextual trigger using basic navigation results in a card list.
  • Run background sync runs the runBackgroundSync() function defined in the add-on script project. This function does not build cards; instead it performs some other background tasks that do not change the UI. Since the function doesn't return a UniversalActionResponse, the UI does not display a new card when the function finishes. Instead the UI displays a loading indicator spinnner while the function is running.

Here is an example of how you might construct the openContactURL(), createSettingsResponse(), and runBackgroundSync() functions:

/**
 * Open a contact URL.
 * @param {Object} e an event object
 * @return {UniversalActionResponse}
 */
function openContactURL(e) {
  // Activate temporary Gmail add-on scopes, in this case so that the
  // open message metadata can be read.
  var accessToken = e.messageMetadata.accessToken;
  GmailApp.setCurrentMessageAccessToken(accessToken);

  // Build URL to open based on a base URL and the sender's email.
  var messageId = e.messageMetadata.messageId;
  var message = GmailApp.getMessageById(messageId);
  var sender = message.getFrom();
  var url = "https://www.example.com/urlbase/" + sender;
  return CardService.newUniversalActionResponseBuilder()
      .setOpenLink(CardService.newOpenLink()
          .setUrl(url))
      .build();
}

/**
 * Create a collection of cards to control the add-on settings and
 * present other information. These cards are displayed in a list when
 * the user selects the associated "Open settings" universal action.
 *
 * @param {Object} e an event object
 * @return {UniversalActionResponse}
 */
function createSettingsResponse(e) {
  return CardService.newUniversalActionResponseBuilder()
      .displayAddOnCards(
          [createSettingCard(), createAboutCard()])
      .build();
}

/**
 * Create and return a built settings card.
 * @return {Card}
 */
function createSettingCard() {
  return CardService.newCardBuilder()
      .setHeader(CardService.newCardHeader().setTitle('Settings'))
      .addSection(CardService.newCardSection()
          .addWidget(CardService.newSelectionInput()
              .setType(CardService.SelectionInputType.CHECK_BOX)
              .addItem("Ask before deleting contact", "contact", false)
              .addItem("Ask before deleting cache", "cache", false)
              .addItem("Preserve contact ID after deletion", "contactId", false))
          // ... continue adding widgets or other sections here ...
      ).build();   // Don't forget to build the card!
}

/**
 * Create and return a built 'About' informational card.
 * @return {Card}
 */
function createAboutCard() {
  return CardService.newCardBuilder()
      .setHeader(CardService.newCardHeader().setTitle('About'))
      .addSection(CardService.newCardSection()
          .addWidget(CardService.newTextParagraph()
              .setText('This add-on manages contact information. For more '
                  + 'details see the <a href="https://www.example.com/help">'
                  + 'help page</a>.'))
      // ... add other information widgets or sections here ...
      ).build();  // Don't forget to build the card!
}

/**
 * Run background tasks, none of which should alter the UI.
 * Also records the time of sync in the script properties.
 *
 * @param {Object} e an event object
 */
function runBackgroundSync(e) {
  var props = PropertiesService.getUserProperties();
  props.setProperty("syncTime", new Date().toString());

  syncWithContacts();  // Not shown.
  updateCache();       // Not shown.
  validate();          // Not shown.

  // no return value tells the UI to keep showing the current card.
}