建立 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 程式庫瀏覽器。搜尋 Google Workspace Marketplace SDK。結果清單中應該會顯示 SDK。

Google Workspace Marketplace SDK 資訊卡

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

設定 Google Workspace Marketplace SDK

Google Workspace Marketplace 會提供清單,列出哪些使用者和管理員可安裝您的外掛程式。設定 Marketplace SDK 的「App Configuration」、「Store List」和「OAuth Consent Screen」並繼續操作。

應用程式設定

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

  • 將「App Visibility」設為 PublicPrivate

    • 公開設定適用於最終將發布給使用者的應用程式。公開應用程式必須先經過核准程序,才能向使用者發布,但您可以指定哪些使用者能將其安裝為草稿,並進行測試。這是發布前的狀態,可讓您先測試及開發外掛程式,再送交核准。
    • 私人設定適用於內部測試和開發。私人應用程式只能由專案建立所在網域的使用者安裝。因此,建議您只有在專案建立於含有 Google Workspace for Education 訂閱的網域時,才將瀏覽權限設為不公開,否則測試使用者將無法啟動 Classroom 外掛程式。
  • 如要限制只有網域管理員才能進行安裝,請將「Installation Settings」(安裝設定) 設為 Admin Only install

  • 在「App Integration」下方,選取「Classroom 外掛程式」。系統會提示您輸入 secure 連結設定 URI,這是當使用者開啟外掛程式時,您希望載入的網址。為方便起見,這應為 https://<your domain>/addon-discovery

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

  • 新增與上一步 OAuth 同意畫面提供的相同的 OAuth 範圍

  • 在「DeveloperLinks」下方,依貴機構需求填寫欄位。

商店資訊

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

  • 在「App Details」下方新增語言,或展開已列出語言旁邊的下拉式選單。請提供應用程式名稱和說明;這些內容會顯示在外掛程式的 Google Workspace Marketplace 商店資訊頁面。按一下「完成」即可儲存。
  • 選擇外掛程式的「類別」
  • 在「圖像資產」下方,為必填欄位提供圖片。這些設定稍後可以變更,不過目前可以是預留位置。
  • 在「支援連結」下方,提供要求的網址。如果您在上個步驟中將「應用程式瀏覽權限」設為「私人」,這些網址就可以設為預留位置。

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

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

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

  • 將「使用者類型」設為「外部」。點選「建立」
  • 在下一頁中,填入必要的應用程式詳細資料和聯絡資訊。在「授權網域」底下提供代管您應用程式的所有網域。按一下「Save and CONTINUE」(儲存並繼續)
  • 新增網頁應用程式所需的任何 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 範圍

    按一下 [儲存並繼續]。

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

確認您的設定正確無誤,然後返回數字面板。

安裝外掛程式

您現在可以使用 Marketplace 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 處理常式範本轉譯回應,並將包含 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 處理常式範本和 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 的「App Configuration」(應用程式設定) 頁面中指定的連結設定 URI

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