创建 Google 课堂插件

这是关于 Google 课堂插件的第一个演示 演示系列视频

在本演示中,您将为开发 Web 应用奠定基础, 将其作为 Google 课堂插件发布。后续演示步骤 展开此应用。

在本演示中,您完成了以下内容:

  • 为您的插件创建新的 Google Cloud 项目。
  • 使用占位符登录按钮创建一个框架式 Web 应用。
  • 为您的插件发布 Google Workspace Marketplace 商品详情。

安装完毕后,您就可以安装插件,并将其加载到 课堂插件 iframe。

前提条件

请选择语言以查看相应的前提条件:

Python

我们的 Python 示例使用的是 Flask 框架。您可以下载完整的 源代码。此代码段的代码 特定演示可在 /flask/01-basic-app/ 目录中找到。

如有必要,请安装 Python 3.7 及更高版本,并确保 pip 可用。

python -m ensurepip --upgrade

我们还建议您设置和激活新的 Python 虚拟设备 环境

python3 -m venv .classroom-addon-env
source .classroom-addon-env/bin/activate

下载的示例中的每个演示子目录都包含一个 requirements.txt。您可以使用以下命令快速安装所需的库: pip。使用以下命令安装此操作所需的库 演示。

cd flask/01-basic-app
pip install -r requirements.txt

Node.js

我们的 Node.js 示例使用的是 Express 框架。您可以下载 完整的源代码

如有必要,请安装 NodeJS v16.13 及更高版本

使用 npm 安装所需的节点模块。

npm install

Java

我们的 Java 示例使用的是 Spring Boot 框架。您可以下载 “概览”页中所有演示的完整源代码

如果您尚未在机器上安装 Java 11 及更高版本,请进行安装。

Spring Boot 应用可以使用 Gradle 或 Maven 来处理构建和管理 依赖项此示例包含 Maven 封装容器,该封装容器可确保 无需安装 Maven 即可成功构建。

为了能够运行我们提供的示例,请在 作为您下载项目的目录,以确保 运行项目的前提条件。

java --version
./mvnw --version

在 Windows 系统中,运行以下命令:

java -version
mvnw.cmd --version

设置 Google Cloud 项目

访问 Classroom API 和所需的身份验证方法 由 Google Cloud 项目控制。以下说明将引导您完成 创建和配置新项目以与您的 插件。

创建项目

访问项目创建页面,创建新的 Google Cloud 项目。您可以 为新项目提供任意名称。点击创建

完全创建新项目需要一些时间。完成后,请 请务必选择项目;你可以在项目选择器中选择 下拉菜单或在屏幕顶部点击选择项目, “通知”菜单。

在 Google Cloud 中选择项目
控制台

将 Google Workspace Marketplace SDK 关联到 Google Cloud 项目

前往 API 库浏览器。搜索 Google Workspace Marketplace SDK。您应该会看到 SDK 显示在 结果。

Google Workspace Marketplace SDK
卡片

选择“Google Workspace Marketplace SDK”卡片,然后点击启用

配置 Google Workspace Marketplace SDK

Google Workspace Marketplace 提供了商品详情, 管理员会安装您的插件。配置 Marketplace SDK 的应用程序配置商店 列表OAuth 同意屏幕,以继续操作。

应用配置

前往 Marketplace SDK 的应用配置页面。 请提供以下信息:

  • App Visibility(应用可见性)设置为 PublicPrivate

    • 公开设置适用于最终将发布的应用 最终用户公开应用必须先经过审批流程 但您可以指定哪些用户可以安装和 将其作为草稿进行测试。这是一种预发布状态, 测试和开发插件,然后再送交审批。
    • 不公开设置适用于内部测试和开发。答 只能由与 项目已创建。因此,您应将公开范围设置为“不公开” 仅当项目是在使用 Google Workspace 教育版的网域中创建的 订阅,否则您的测试用户将无法发布应用 课堂插件。
  • 如果您要将安装设置设置为 Admin Only install, 只允许域管理员进行安装。

  • 应用集成下,选择 Google 课堂插件。您 提示输入安全附件设置 URI;这是您 预计在用户打开您的插件时加载。在此 应设置为 https://<your domain>/addon-discovery

  • 允许的附件 URI 前缀用于验证在 AddOnAttachment(使用 courses.*.addOnAttachments.createcourses.*.addOnAttachments.patch 方法。验证是一个字面量 字符串前缀匹配,并且不允许在此位置使用通配符 。请至少添加内容服务器的根网域,例如 https://localhost:5000/https://cdn.myedtech.com/

  • 添加 OAuth 权限请求页面中提供的相同 OAuth 范围,请 上一步。

  • 开发者 链接

商品详情

转到 Marketplace SDK 的商品详情页面。 请提供以下信息:

  • 应用详情下,添加语言或展开 语言已列出。提供应用名称和说明;这些内容 会显示在插件的 Google Workspace Marketplace 商品详情页面中。 点击完成进行保存。
  • 为您的插件选择类别
  • 图形资源下的必填字段中提供图片。这些可以 可以在稍后更改,并且暂时可以作为占位符。
  • 支持链接下,提供所需的网址。这些可以是 如果您在之前的部分中将“应用公开范围”设为不公开,请替换占位符 操作。

如果您在上一步中将“应用公开范围”设置为不公开,请点击 发布;您的应用立即可供安装。如果您将 应用公开范围已设为公开,请在草稿测试人员区域添加电子邮件地址 针对任何测试用户,点击保存草稿

当用户首次授权您的应用时,系统会显示 OAuth 同意屏幕。它会提示 允许您的应用访问他们的个人信息和账号信息, 取决于您启用的范围

前往 OAuth 同意屏幕创建页面。请提供以下内容 信息:

  • 用户类型设置为外部。点击创建
  • 在下一页中,填写所需的应用详细信息和联系信息。 提供在已获授权的网域下托管您的应用的所有网域。点击 保存并继续
  • 添加您的 Web 应用所需的任何 OAuth 范围。有关详情,请参阅 OAuth 配置指南,了解有关范围及其用途的深入讨论。

    您必须请求以下至少一个范围,Google 才能 发送 login_hint 查询参数。对此进行更详细的说明 OAuth 配置指南中会有所不同:

    • https://www.googleapis.com/auth/userinfo.email(已包含)
    • https://www.googleapis.com/auth/userinfo.profile(已包含)

    以下范围仅适用于 Google 课堂插件:

    • https://www.googleapis.com/auth/classroom.addons.teacher
    • https://www.googleapis.com/auth/classroom.addons.student

    还应包括您的应用从头到尾所需的任何其他 Google API 范围 用户。

    点击保存并继续

  • 测试用户页面上,列出所有测试账号的电子邮件地址。 点击保存并继续

确认您的设置正确无误,然后返回信息中心。

安装插件

现在,您可以使用 Marketplace SDK 的商品详情页面。点击应用 网址 查看商品详情,然后选择安装

构建基本 Web 应用

设置具有两个路由的框架 Web 应用。后续演示步骤 展开此应用,现在只需为该插件创建着陆页 /addon-discovery 以及我们“公司网站”的模拟索引页 /

iframe 中的 Web 应用示例

实现以下两个端点:

  • /:显示欢迎辞和用于关闭当前标签页的按钮 和插件 iframe。
  • /addon-discovery:显示欢迎辞和两个按钮:一个用于关闭 插件 iframe,另一个用于在新标签页中打开网站。

请注意,我们添加了用于创建和关闭窗口或 iframe 的按钮。这些 演示了一种可将用户安全地带入新标签页的方法, 授权。

创建实用程序脚本

创建一个 static/scripts 目录。创建一个新文件 addon-utils.js。将 以下两个函数。

/**
 *   Opens a given destination route in a new window. This function uses
 *   window.open() so as to force window.opener to retain a reference to the
 *   iframe from which it was called.
 *   @param {string} destinationURL The endpoint to open, or "/" if none is
 *   provided.
 */
function openWebsiteInNewTab(destinationURL = '/') {
  window.open(destinationURL, '_blank');
}

/**
 *   Close the iframe by calling postMessage() in the host Classroom page. This
 *   function can be called directly when in a Classroom add-on iframe.
 *
 *   Alternatively, it can be used to close an add-on iframe in another window.
 *   For example, if an add-on iframe in Window 1 opens a link in a new Window 2
 *   using the openWebsiteInNewTab function, you can call
 *   window.opener.closeAddonIframe() from Window 2 to close the iframe in Window
 *   1.
 */
function closeAddonIframe() {
  window.parent.postMessage({
    type: 'Classroom',
    action: 'closeIframe',
  }, '*');
};

创建路由

实现 /addon-discovery/ 端点。

Python

设置应用目录

在本示例中,将应用逻辑的结构设计为 Python 模块。这就是我们所提供的示例中的 webapp 目录。

为服务器模块创建一个目录,例如 webapp。将 static 目录复制到模块目录中。创建 template 目录 位于模块目录中;您的 HTML 文件会放在此处

构建服务器模块*

在模块目录中创建 __init__.py 文件,并添加以下代码 导入和声明。

from flask import Flask
import config

app = Flask(__name__)
app.config.from_object(config.Config)

# Load other module script files. This import statement refers to the
# 'routes.py' file described below.
from webapp import routes

然后,创建一个文件来处理 Web 应用的路由。这是 webapp/routes.py。在此代码中实现两个路由 文件。

from webapp import app
import flask

@app.route("/")
def index():
    return flask.render_template("index.html",
                                message="You've reached the index page.")

@app.route("/classroom-addon")
def classroom_addon():
    return flask.render_template(
        "addon-discovery.html",
        message="You've reached the addon discovery page.")

请注意,我们的路由都会将 message 变量传递给各自的 Jinja 模板。这有助于确定用户访问的网页。

创建配置文件和启动文件

在应用的根目录中,创建 main.pyconfig.py。 文件。在 config.py 中配置您的密钥。

import os

class Config(object):
    # Note: A secret key is included in the sample so that it works.
    # If you use this code in your application, replace this with a truly secret
    # key. See https://flask.palletsprojects.com/quickstart/#sessions.
    SECRET_KEY = os.environ.get(
        'SECRET_KEY') or "REPLACE ME - this value is here as a placeholder."

main.py 文件中,导入模块并启动 Flask 服务器。

from webapp import app

if __name__ == "__main__":
    # Run the application over HTTPs with a locally stored certificate and key.
    # Defaults to https://localhost:5000.
    app.run(
        host="localhost",
        ssl_context=("localhost.pem", "localhost-key.pem"),
        debug=True)

Node.js

请使用以下行在 app.js 文件中注册路由。

const websiteRouter = require('./routes/index');
const addonRouter = require('./routes/classroom-addon');

app.use('/', websiteRouter);
app.use('/addon-discovery', addonRouter);

打开 /01-basic-app/routes/index.js 并查看代码。此路线为 在最终用户访问公司网站时被覆盖。该路线会呈现 使用 index Handlebars 模板进行响应,并向模板传递一个 包含 titlemessage 变量的数据对象。

router.get('/', function (req, res, next) {
  res.render('index', {
    title: 'Education Technology',
    message: 'Welcome to our website!'
  });
});

打开第二条路线/01-basic-app/routes/classroom-addon.js并查看 代码。当最终用户访问此插件时,便会到达此路由。通知 此路线使用了 discovery Handlebars 模板 addon.hbs 布局,以便以与公司不同的方式呈现网页 网站。

router.get('/', function (req, res, next) {
  res.render('discovery', {
    layout: 'addon.hbs',
    title: 'Education Technology Classroom add-on',
    message: `Welcome.`
  });
});

Java

Java 代码示例使用模块将序列演示打包到一起 步骤。由于这是第一个演示,因此代码位于 step_01_basic_app 模块。不要实现 使用模块的项目建议您在单个项目中构建 。

在此示例项目中创建一个控制器类 Controller.java, 定义端点在此文件中,从以下代码中导入 @GetMapping 注解: spring-boot-starter-web 依赖项。

import org.springframework.web.bind.annotation.GetMapping;

在类上方添加 Spring 框架控制器注释 定义,以指明类的用途。

@org.springframework.stereotype.Controller
public class Controller {

然后,实现这两个路由和一个用于错误处理的额外路由。

/** Returns the index page that will be displayed when the add-on opens in a
*   new tab.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the index page template if successful, or the onError method to
*   handle and display the error message.
*/
@GetMapping(value = {"/"})
public String index(Model model) {
  try {
    return "index";
  } catch (Exception e) {
    return onError(e.getMessage(), model);
  }
}

/** Returns the add-on discovery page that will be displayed when the iframe
*   is first opened in Classroom.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the addon-discovery page.
*/
@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(Model model) {
  try {
    return "addon-discovery";
  } catch (Exception e) {
    return onError(e.getMessage(), model);
  }
}

/** Handles application errors.
*   @param errorMessage message to be displayed on the error page.
*   @param model the Model interface to pass error information to display on
*   the error page.
*   @return the error page.
*/
@GetMapping(value = {"/error"})
public String onError(String errorMessage, Model model) {
  model.addAttribute("error", errorMessage);
  return "error";
}

测试插件

启动服务器。然后,以用户身份登录 Google 课堂 您的 教师测试用户。请转到课业标签并新建一项 转让。从插件选择器中选择您的插件。iframe 将会打开 之后,该插件会加载您在 Marketplace SDK 的应用配置页面。

恭喜!您可以继续执行下一步:登录用户 使用 Google SSO