建立 Classroom 外掛程式

這是 Classroom 外掛程式操作說明系列的第一篇操作說明。

在本逐步操作說明中,您將瞭解如何開發網頁應用程式,並將其發布為 Classroom 外掛程式。日後的逐步操作步驟會擴充這個應用程式。

在本逐步操作說明中,您將完成下列操作:

  • 為外掛程式建立新的 Google Cloud 專案。
  • 建立包含預留位置登入按鈕的骨架網頁應用程式。
  • 發布 Google Workspace Marketplace 商店的產品資訊,讓使用者可以找到您的外掛程式。

完成後,您可以安裝外掛程式,並在 Classroom 外掛程式 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 專案。您可以為新專案提供任何名稱。點選「Create」(建立)

新專案需要幾分鐘才能建立完成。完成後,請務必選取專案;您可以在畫面頂端的專案選取器下拉式選單中選擇,或是按一下右上方的通知選單中的「選取專案」

在 Google Cloud 控制台中選取專案

將 Google Workspace Marketplace SDK 附加至 Google Cloud 專案

前往「API Library」瀏覽器。搜尋 Google Workspace Marketplace SDK。您應該會在結果清單中看到 SDK。

Google Workspace Marketplace SDK 資訊卡

選取「Google Workspace Marketplace SDK」資訊卡,然後按一下「啟用」

設定 Google Workspace Marketplace SDK

Google Workspace Marketplace 提供清單,供使用者和管理員安裝您的外掛程式。請設定市集 SDK 的應用程式設定商店資訊,以及OAuth 同意畫面,即可繼續操作。

應用程式設定

前往 Marketplace SDK 的「應用程式設定」頁面。提供下列資訊:

  • 將「App Visibility」設為 PublicPrivate

    • 公開設定適用於最終發布給使用者的應用程式。公開應用程式必須經過核准程序,才能發布給使用者,但您可以指定可安裝及測試該應用程式做為草稿的使用者。這是預發布狀態,可讓您在送交審查前測試及開發外掛程式。
    • 私人設定適合用於內部測試和開發。私人應用程式只能由與建立專案相同網域的使用者安裝。因此,只有在專案是在已訂閱 Google Workspace for Education 的網域中建立時,才應將瀏覽權限設為「私人」,否則測試使用者將無法啟動 Classroom 外掛程式。
  • 如要將安裝權限限制為網域管理員,請將「安裝設定」設為 Admin Only install

  • 在「應用程式整合」下方,選取「Classroom 外掛程式」。系統會提示您提供安全的附件設定 URI,也就是使用者開啟外掛程式時預期會載入的網址。在本逐步操作說明中,應為 https://<your domain>/addon-discovery

  • 可使用的附件 URI 前置字串可用於使用 courses.*.addOnAttachments.createcourses.*.addOnAttachments.patch 方法,驗證 AddOnAttachment 中設定的 URI。驗證是字面字串前置字元比對,目前不允許使用萬用字元。至少新增內容伺服器的根網域,例如 https://localhost:5000/https://cdn.myedtech.com/

  • 新增與前一個步驟中 OAuth 同意畫面相同的 OAuth 範圍

  • 在「開發人員連結」下方,根據貴機構的情況填寫適當的欄位。

商店資訊

前往 Marketplace SDK 的「商店資訊」頁面。提供下列資訊:

  • 在「應用程式詳細資料」下方,新增語言,或展開已列出的語言旁的下拉式選單。提供應用程式名稱和說明,這些資訊會顯示在外掛程式的 Google Workspace Marketplace 商店資訊頁面。按一下「完成」即可儲存。
  • 為外掛程式選擇類別
  • 在「圖片和影片資源」下方,為必填欄位提供圖片。這些名稱可在日後變更,目前可做為預留位置。
  • 在「支援連結」下方提供要求的網址。如果您在前一個步驟中將應用程式顯示設定設為「私人」,這些網址可做為預留位置。

如果您在前一個步驟中將「應用程式顯示設定」設為「私人」,請按一下「發布」,您的應用程式即可立即供人安裝。如果您將「應用程式瀏覽權限」設為「公開」,請在「草稿測試人員」區域中新增任何測試使用者的電子郵件地址,然後按一下「儲存草稿」

使用者首次授權應用程式時,系統會顯示 OAuth 同意畫面,並根據您啟用的範圍,提示使用者允許應用程式存取個人和帳戶資訊。

前往 OAuth 同意畫面建立頁面。請提供下列資訊:

  • 將「使用者類型」設為「外部」。點選「建立」
  • 在下一頁中,填入必要的應用程式詳細資料和聯絡資訊。在「授權網域」下方,提供代管應用程式的任何網域。按一下「儲存並繼續」
  • 新增網頁應用程式所需的任何 OAuth 範圍。如要深入瞭解範圍及其用途,請參閱 OAuth 設定指南

    您必須要求下列至少一個權限範圍,Google 才能傳送 login_hint 查詢參數。如要進一步瞭解這項行為,請參閱 OAuth 設定指南

    • https://www.googleapis.com/auth/userinfo.email (已納入)
    • https://www.googleapis.com/auth/userinfo.profile (已納入)

    以下範圍專供 Classroom 外掛程式使用:

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

    此外,請一併納入應用程式需要的任何其他 Google API 範圍

    按一下 [儲存並繼續]。

  • 在「測試人員」頁面列出所有測試帳戶的電子郵件地址。按一下「儲存並繼續」

確認設定正確無誤,然後返回資訊主頁。

安裝外掛程式

您現在可以透過市集 SDK「商店資訊」頁面頂端的連結安裝外掛程式。按一下頁面頂端的「應用程式網址」查看清單,然後選擇「安裝」

建構基本網頁應用程式

設定包含兩個路徑的骨架網頁應用程式。後續的逐步操作步驟會擴充這個應用程式,因此目前只需為外掛程式 /addon-discovery 建立到達網頁,以及為「公司網站」建立模擬索引頁面 /

iframe 中的網路應用程式範例

實作這兩個端點:

  • /:顯示歡迎訊息和按鈕,可同時關閉目前分頁和外掛程式 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

接著,建立檔案來處理網頁應用程式的路由。在我們提供的範例中,這個值為 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),以便定義端點。在這個檔案中,從 spring-boot-starter-web 依附元件匯入 @GetMapping 註解。

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 Classroom。前往「課堂作業」分頁,然後建立新的「作業」。從「外掛程式」挑選器中選取外掛程式。iframe 會開啟,外掛程式會載入您在 Marketplace SDK 應用程式設定頁面中指定的附件設定 URI

恭喜!您可以繼續進行下一個步驟:使用 Google 單一登入 (SSO) 服務讓使用者登入