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 the current add-on context.
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 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.
Configuring universal actions
You configure universal actions in your add-on's manifest; see Manifests 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 add-on context.
Example
The following code snippet shows an example manifest excerpt for a Google Workspace add-on that uses universal actions while extending Gmail. The code explicitly sets a metadata scope so that the add-on can determine who sent the open message.
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata"
],
"addOns": {
"common": {
"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": [{
"label": "Open google.com",
"openLink": "https://www.google.com"
}, {
"label": "Open contact URL",
"runFunction": "openContactURL"
}, {
"label": "Open settings",
"runFunction": "createSettingsResponse"
}, {
"label": "Run background sync",
"runFunction": "runBackgroundSync"
}],
...
},
"gmail": {
"contextualTriggers": [
{
"unconditional": {},
"onTriggerFunction": "getContextualAddOn"
}
]
},
...
},
...
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 validUniversalActionResponse
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 (see Returning multiple cards). - 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 aUniversalActionResponse
, the UI does not display a new card when the function finishes. Instead the UI displays a loading indicator spinner 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 scopes, in this case so that the
// open message metadata can be read.
var accessToken = e.gmail.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
// Build URL to open based on a base URL and the sender's email.
// This URL must be included in the openLinkUrlPrefixes whitelist.
var messageId = e.gmail.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.
}