编辑器插件授权

对许多基于 Apps 脚本的应用进行授权的过程很简单,因为当有人尝试使用脚本项目时,脚本项目会要求它缺少任何必需的权限。

由于以下几个原因,编辑器插件的授权模型更为复杂:

  • 当用户创建文件时,该用户安装的所有插件都会列在扩展程序菜单中,即使用户尚未对这些插件进行授权也是如此。

  • 这些插件适用于 Google 云端硬盘中可与协作者共享的文件。未安装编辑器插件的协作者会在文件创建者使用该插件的文档中看到该文件。

  • 编辑器插件会在文档打开时自动运行其 onOpen() 函数。

为了保护用户数据,系统会应用授权模式,使 onOpen() 无法使用某些服务。本指南可帮助您了解代码的用途和时机。

授权模型

编辑器插件的授权模式取决于其状态,具体取决于谁在使用该插件:安装了该插件的用户还是协作者。

编辑器插件状态

Extensions 菜单中的编辑器插件已安装和/或已启用。

  • 当特定用户或其管理员从 Google Workspace Marketplace 中获取插件并授权其访问其 Google 数据后,系统会为特定用户安装插件。
  • 只要有人在文档、表单、演示文稿或电子表格中使用某个插件,系统就会启用插件。
  • 当其他人协作处理某个文件时,如果其中一位某位用户使用了插件,则系统会为一位用户安装插件,并对该文件启用插件。

下表总结了已安装和已启用之间的区别。 请注意,在将脚本作为插件进行测试时,您可以在这两种状态下运行测试,也可以同时在这两种状态下运行测试。

已安装 已启用
应用对象 用户级 文档、表单、演示文稿或电子表格
原因 从商店获取插件 在使用相应文档、表单、演示文稿或电子表格时从商店获取插件,或者
使用之前在该文档、表单、演示文稿或电子表格中安装的插件
菜单公开范围 在其打开或创建的所有文档、表单、演示文稿或电子表格中,只有该用户 该文档、表单、演示文稿或电子表格的所有协作者
onOpen()的授权模式 AuthMode.NONE
(除非它也已启用,在这种情况下 AuthMode.LIMITED)
AuthMode.LIMITED

授权模式

当用户打开文档、表单、演示文稿或电子表格时,编辑器插件的 onOpen() 函数会自动运行。为了保护用户数据,Apps 脚本会限制 onOpen() 函数可以执行的操作。编辑器插件状态决定了 onOpen() 函数在哪种授权模式下运行。

如果在文件、表单、演示文稿或电子表格中启用了编辑器插件,则 onOpen() 会在 AuthMode.LIMITED 中运行。如果该插件未启用,并且只安装了,则 onOpen()AuthMode.NONE 中运行。

AuthMode.NONE 中,除非用户通过点击或运行自定义函数与插件进行交互,否则插件无法运行某些服务。如果您的插件尝试在 onOpen()onInstall() 或全局范围内使用这些服务,权限会失败,并且会停止其他调用(例如填充菜单)。“帮助”是唯一受支持的选项。

如需运行受限服务调用,您必须使用 AuthMode.FULL 授权模式。用户互动函数(例如点击菜单选项)仅在此模式下运行。代码在 AuthMode.FULL 模式下运行后,该插件可以使用用户授权的所有范围。

Apps 脚本会将授权模式作为 Apps 脚本事件参数 eauthMode 属性进行传递;e.authMode 的值对应于 Apps 脚本 ScriptApp.AuthMode 枚举中的一个常量。

授权模式适用于所有 Apps 脚本执行方法,包括通过脚本编辑器、菜单项或 Apps 脚本 google.script.run 调用运行。但是,只有当脚本作为触发器(例如 onOpen()onEdit()onInstall())的结果运行时,才能检查 e.authMode 属性。Google 表格中的自定义函数使用自己的授权模式 AuthMode.CUSTOM_FUNCTION,该模式与 LIMITED 类似,但限制略有不同。对于所有其他情况,脚本会在 AuthMode.FULL 中运行,如下表所述。

NONE LIMITED CUSTOM_FUNCTION FULL
发生时间 onOpen()(如果用户已安装插件,但未在文档、表单、演示文稿或电子表格中启用该插件) onOpen()(所有其他时间)
onEdit()(仅限 Google 表格)
自定义函数 所有其他时间,包括:
可安装的触发器
onInstall()
google.script.run
访问用户数据 仅限语言区域 仅限语言区域 仅限语言区域
访问文档、表单、演示文稿或电子表格 是 - 只读
访问界面 添加菜单项 添加菜单项
Properties 的访问权限
有权访问JdbcUrlFetch
其他服务 Logger
Utilities
不访问用户数据的所有服务 不访问用户数据的所有服务 所有服务

编辑器插件的授权生命周期

为当前用户安装插件或在当前文件中启用插件后,系统会在文件打开时为文档、表单、演示文稿或电子表格加载该插件。该插件会列在扩展程序菜单中,并开始监听简单触发器 onInstall()onOpen()onEdit()。如果用户点击某个扩展程序菜单项,该菜单项会运行

已安装编辑器插件

从 Play 商店安装编辑器插件时,其 onInstall() 函数会在 AuthMode.FULL 中运行。在此授权模式下,插件可以运行复杂的设置例程。您还应使用 onInstall() 创建菜单项,因为文档、表单、演示文稿或电子表格已经打开,而您的 onOpen() 函数尚未运行。以下示例展示了如何通过 onInstall() 函数调用 onOpen() 函数:

function onInstall(e) {
  onOpen(e);
  // Perform additional setup as needed.
}

编辑器插件已打开

当文档、表单、演示文稿或电子表格打开时,系统会加载当前用户已安装或任何协作者在文件中启用的每个编辑器插件,并调用其每个 onOpen() 函数。运行 onOpen() 的授权模式取决于已安装还是启用了插件。

如果插件仅创建基本菜单,则该模式无关紧要。以下示例展示了一个基本的 onOpen() 函数:

function onOpen(e) {
  SpreadsheetApp.getUi().createAddonMenu() // Or DocumentApp.
      .addItem('Insert chart', 'insertChart')
      .addItem('Update charts', 'updateCharts')
      .addToUi();
}

要根据存储的 Apps 脚本属性添加动态菜单项、读取当前文件的内容或执行其他高级任务,您必须确定授权模式并对其进行适当处理。

以下示例展示了一个高级 onOpen() 函数,该函数会根据授权模式更改其操作:

function onOpen(e) {
  var menu = SpreadsheetApp.getUi().createAddonMenu(); // Or DocumentApp.
  if (e && e.authMode == ScriptApp.AuthMode.NONE) {
    // Add a normal menu item (works in all authorization modes).
    menu.addItem('Start workflow', 'startWorkflow');
  } else {
    // Add a menu item based on properties (doesn't work in AuthMode.NONE).
    var properties = PropertiesService.getDocumentProperties();
    var workflowStarted = properties.getProperty('workflowStarted');
    if (workflowStarted) {
      menu.addItem('Check workflow status', 'checkWorkflow');
    } else {
      menu.addItem('Start workflow', 'startWorkflow');
    }
  }
  menu.addToUi();
}

请注意,在 AuthMode.LIMITED 中执行时,插件无法打开边栏或对话框。您可以使用菜单项打开边栏和对话框,因为这些项AuthMode.FULL 中运行

用户运行编辑器插件

当用户点击扩展程序菜单项时,Apps 脚本会先检查用户是否已安装该插件,如果没有,则会提示他们安装。如果用户已向插件授权,该脚本会运行与 AuthMode.FULL 中的菜单项对应的函数。在文档、表单、演示文稿或电子表格中启用插件(如果尚未启用)。

排查插件菜单不显示的问题

如果您的代码未正确管理授权模式,您的插件菜单可能不会显示。例如:

  • 插件尝试运行当前授权模式不支持的 Apps 脚本服务。

  • 插件会在用户与插件互动之前尝试运行服务调用。

如需移除或重新排列导致 AuthMode.NONE 中导致权限错误的服务调用,请尝试以下操作:

  1. 打开插件的 Apps 脚本项目,并找到 onOpen() 函数。
  2. onOpen() 函数中搜索提及 Apps 脚本服务或关联的对象,例如 PropertiesServiceSpreadsheetAppGmailApp
  3. 如果服务用于除创建界面元素之外的任何用途,请将其移除或将其封装在注释块中。仅保留以下方法:.getUi().createMenu().addItem().addToUi()。 同时查找并移除函数外部的任何服务。
  4. 找出可能包含在上一步中注释或移除的代码行的函数,尤其是使用它们所生成信息的函数,并将服务调用移至需要它们的函数。重新排列或重写代码库,以适应前面步骤中所做的更改。
  5. 保存代码并创建测试部署。

    创建测试部署时,请确保 Config 字段已针对当前用户安装,并且配置框下方的文本显示AuthMode.None 中测试

  6. 启动测试部署并打开扩展程序菜单。

  7. 如果显示了所有菜单项,则表明问题已解决。 如果您只看到帮助菜单,请返回第 1 步。 您可能错过了服务呼叫。