通用操作

通用操作是菜单项元素,允许用户打开新网页、显示新的界面卡片或在选择后运行特定的 Apps 脚本功能。它们在操作上与卡片操作非常相似,只不过无论当前插件环境如何,通用操作始终应用于插件中的每张卡片。

通过使用通用操作,您可以确保用户始终有权使用某些功能,无论用户与插件的哪个部分互动。以下是通用操作的一些用例示例:

  • 打开设置网页(或显示设置卡片)。
  • 向用户显示帮助信息。
  • 启动新工作流程,例如“添加新客户”。
  • 显示一张卡片,让用户能够发送有关插件的反馈。

每当您的操作不依赖于当前上下文时,就应考虑将其设为通用操作。

使用通用操作

通用操作是在插件的项目清单中配置的。配置通用操作后,插件的用户就可以随时使用该操作。如果用户正在查看卡片,则您定义的一组通用操作会始终显示在卡片菜单中,位于您为该卡片定义的任何卡片操作之后。通用操作在卡片菜单中显示的顺序与在插件清单中定义的顺序相同。

配置通用操作

您可以在插件的清单中配置通用操作;如需了解详情,请参阅清单

对于每项操作,您可以指定应在该操作的菜单中显示的文本。然后,您可以指定一个 openLink 字段,指示该操作应直接在新标签页中打开网页。或者,您也可以指定一个 runFunction 字段,用于指定选择通用操作时要执行的 Apps 脚本回调函数。

使用 runFunction 时,指定的回调函数通常会执行以下某项操作:

  • 通过返回已构建的 UniversalActionResponse 对象,构建要立即显示的界面卡片。
  • 通过返回构建的 UniversalActionResponse 对象打开一个网址,可能在执行其他任务后打开。
  • 执行不会切换到新卡片或打开网址的后台任务。 在这种情况下,回调函数不会返回任何内容。

被调用时,系统会向该回调函数传递一个事件对象,其中包含有关打开的卡片和插件上下文的信息。

示例

以下代码段展示了在扩展 Gmail 时使用通用操作的 Google Workspace 插件的清单摘录示例。该代码会明确设置元数据范围,以便插件可以确定是谁发送了打开的消息。

  "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.
}