通用操作是指菜单项元素,可让用户在被选中后打开新网页、显示新的界面卡片或运行特定的 Apps 脚本函数。操作方式与卡片操作非常相似,只不过无论当前的插件上下文如何,通用操作始终是对插件中的每张卡片执行。
通过使用通用操作,您可以确保用户始终可以访问特定功能,无论他们与插件的哪个部分互动。以下是通用操作的一些示例用例:
- 打开设置网页(或显示设置卡片)。
- 向用户显示帮助信息。
- 启动新的工作流程,例如“添加新客户”。
- 显示一张卡片,以便用户发送关于该插件的反馈。
每当您的操作不依赖于当前上下文时,都应考虑将其设为通用操作。
使用通用操作
通用操作在插件的项目清单中配置。配置通用操作后,您的插件用户就可以随时使用该操作。如果用户查看的是卡片,您定义的一组通用操作始终会显示在卡片菜单中,位于您为该卡片定义的任何卡片操作之后。通用操作在卡片菜单中显示的顺序与在插件清单中定义的顺序相同。
配置通用操作
您可以在插件的清单中配置通用操作;如需了解详情,请参阅清单。
对于每项操作,您可以指定应在该操作的菜单中显示的文本。然后,您可以指定 openLink
字段,指示该操作应直接在新标签页中打开网页。或者,您也可以指定一个 runFunction
字段,用于指定在选择通用操作时要执行的 Apps 脚本回调函数。
使用 runFunction
时,指定的回调函数通常会执行以下某项操作:
- 通过返回已构建的
UniversalActionResponse
对象来构建要立即显示的界面卡片。 - 通过返回已构建的
UniversalActionResponse
对象来打开网址(可能会在执行其他任务之后打开)。 - 执行后台任务,不会切换到新卡片或打开网址。 在这种情况下,回调函数不会返回任何内容。
被调用时,系统会向回调函数传递一个事件对象,其中包含有关打开的卡片和插件上下文的信息。
示例
以下代码段展示了 Google Workspace 插件的示例清单摘录,该插件在扩展 Gmail 时使用通用操作。该代码会明确设置元数据范围,以便插件可以确定打开的邮件的发件人。
"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"
}
]
},
...
},
...
上例中定义的三种通用操作将执行以下操作:
- 打开 google.com 会在一个新标签页中打开 https://www.google.com。
- 打开联系人网址会运行一个函数来确定要打开的网址,然后使用
OpenLink
对象在新标签页中打开该网址。该代码会使用发件人的电子邮件地址构建网址。 - 打开设置会运行插件脚本项目中定义的
createSettingsCards()
函数。此函数会返回一个有效的UniversalActionResponse
对象,其中包含一组具有插件设置和其他信息的卡片。函数构建完此对象后,界面会显示卡片列表(请参阅返回多张卡片)。 - 运行后台同步:运行在插件脚本项目中定义的
runBackgroundSync()
函数。此函数不会构建卡片;而是会执行一些不会更改界面的其他后台任务。由于该函数不返回UniversalActionResponse
,因此当该函数运行完毕时,界面不会显示新卡片。而是会在函数运行期间显示一个加载指示器旋转图标。
以下示例展示了如何构造 openContactURL()
、createSettingsResponse()
和 runBackgroundSync()
函数:
/**
* 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.
}