# OxOffice Online 整合手冊

# 簡介

這份文件將協助您整合現有的雲端儲存方案，以便您的使用者可以透過 Web 瀏覽器編輯儲存在雲端硬碟中的文件。

因此，您必須將下列部份設定好，並且組合在一起：

- [OxOffice Online 主機](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-oxoffice-online-%E4%B8%BB%E6%A9%9F)
- [能開出 iframe 的頁面](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-%E9%96%8B%E5%87%BA%E7%B7%A8%E8%BC%AF-iframe-%E9%A0%81%E9%9D%A2%E7%9A%84%E7%B6%B2%E7%AB%99)
- [使用者驗證](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-%E4%BD%BF%E7%94%A8%E8%80%85%E9%A9%97%E8%AD%89)
- [連接雲端硬碟](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-%E9%80%A3%E6%8E%A5%E9%9B%B2%E7%AB%AF%E7%A1%AC%E7%A2%9F)

整合 OxOffice Online 最簡單的方法，就是使用 WOPI 協定。

OxOffice Online 實作了一個名為 WOPI（全名是：Web 應用程式開放平台介面）的協定。OxOffice Online 是 **WOPI 客戶端**，可以和 **WOPI 主機**（也就是您現有的雲端應用和儲存方案）整合在一起。WOPI 是一個開放協議，更多詳細資料，請造訪 [WOPI documentation](https://docs.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/).

# 如何整合

### **OxOffice Online 主機**

儘管可以和 Web 伺服器安裝在同一臺主機，不過，我們建議將 OxOffice Online 安裝在專用的 VM 或伺服器。這部伺服器（WOPI 客戶端），必須能讓外部網際網路連線，而且還要能夠連線到您的 WOPI 伺服器（您的雲端應用系統）。

### **開出編輯 iframe 頁面的網站**

我們假設您希望將編輯功能整合到現有網站中。 因此，在網站上，您需要提供一個 iframe，其中將顯示OxOffice Online 的編輯介面和文件本身。

要設定 iframe，WOPI 主機（您的雲端應用系統）需要從 WOPI 客戶端（OxOffice Online 伺服器）上定義的位置讀取 discovery XML。 位址如下：

<table id="bkmrk-https%3A%2F%2F%3Cwopi-%E5%AE%A2%E6%88%B6%E7%AB%AF-ur" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:99.8765%;"></col></colgroup><tbody><tr><td>https://&lt;WOPI 客戶端 URL&gt;:&lt;port&gt;/hosting/discovery</td></tr></tbody></table>

這會得到 discovery.xml，其中包含各種文件格式的 **urlsrc**。 **urlsrc** 為編輯文件用的 iframe 需要使用的位址。

接著您需要為欲編輯的檔案，提供一個名為 **WOPISrc** 的位址，**WOPISrc** 網址看起來如下：

<table id="bkmrk-https%3A%2F%2F%3Cwopi-%E4%B8%BB%E6%A9%9F-url" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:100%;"></col></colgroup><tbody><tr><td>https://&lt;WOPI 主機 URL&gt;/&lt;...&gt;/wopi/files/&lt;file id&gt;</td></tr></tbody></table>

這裡的 `/wopi/` 實際上可以是任何以 `wopi` 開頭的字串，像是 `/wopifiles/` 或 `/wopi_implementation/` 都可以，但為了簡單起見，我們之後只會使用 `/wopi/`。

`<file id>` 必須以 base64 編碼，也就是說只能有英文 `A-Z` 或 `a-z`、數字 `0-9` 以及 `-` 和 `_` 符號。

### **使用者驗證**

為了能夠安全地存取文件，您的應用程式必須將身份驗證用的 token 傳遞給 OxOffice Online 的 access\_token。對 OxOffice Online 而言，token 可以是任何隨機的數字或字串，它會在存取文件過程中（WOPI 客戶端對 WOPI 主機）當作 URL 的一部份傳遞。

唯一的要求是，對於識別使用者而言，token 必須是唯一的，不能和其他使用者重複，意即，若 WOPI 客戶端對 **WOPISrc** 所指定的 URL 進行存取時時，您必須檢查所傳遞的 token 是否與當初建立 iframe 所傳遞的 token 相符，否則您的應用程序應該拒絕錯誤的 token 存取檔案

目前這是唯一支援的身份驗證方式。

### **連接雲端硬碟**

作為 WOPI 主機，您的應用程式必須為 OxOffice Online （WOPI 客戶端）實作幾個進入點，以便 OxOffice Online 下載使用者想要編輯的檔案，以及傳回更新檔案。

WOPI 客戶端（OxOffice Online）會呼叫上面所建立的 **WOPISrc** 來下載檔案：

```
GET https://<WOPI 主機 URL>/<...>/wopi/files/<file id>/contents?access_token=<token>
```

以及傳回更新檔案：

```none
POST https://<WOPI 主機 URL>/<...>/wopi/files/<file id>/contents?access_token=<token>
```

目前，OxOffice Online 使用 4 個 WOPI 的檔案操作，分別是：**CheckFileInfo**、**GetFile**、**PutFile** 以及 **PutRelativeFile**。

您的應用程式，必須產生參照檔案及使用者的唯一 token，該 token 可以為固定或過期，只要檔案編輯期間，可供辨識即可。該 token 會透過 URL 的 access\_token=&lt;token&gt; 傳遞，您的應用程式也可以在接收的 HTTP header 中，取得 OxOffice Online 傳遞的 token。

```
Authorization: Bearer <token>
```

並且需支援下列 4 個功能：

1. #### **CheckFileInfo**：以下 URL 被呼叫時，至少要以 JSON 格式，傳回 BaseFileName 和 Size。
    
    ```
    GET https://<WOPI 主機 URL>/<...>/wopi/files/<file id>?access_token=<token>
    ```
    
    ##### **OxOffice Online 可用的 CheckFileInfo 屬性列表：**  
    
    
    <table style="border-collapse:collapse;width:102.011%;height:714.469px;"><colgroup><col style="width:23.8926%;"></col><col style="width:14.6309%;"></col><col style="width:9.93289%;"></col><col style="width:51.5436%;"></col></colgroup><tbody><tr style="height:57.7812px;"><td class="align-center" style="height:57.7812px;">屬性</td><td style="height:57.7812px;">型別
    
    （Data type）
    
    </td><td style="height:57.7812px;">必要
    
    Required
    
    </td><td style="height:57.7812px;">說明</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">BaseFileName</td><td style="height:29.7969px;">string</td><td style="height:29.7969px;"><span style="color:rgb(224,62,45);">yes</span></td><td style="height:29.7969px;">不含路徑的檔案名稱，用來顯示在使用者的編輯畫面。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">OwnerId</td><td style="height:29.7969px;">string</td><td style="height:29.7969px;"><span style="color:rgb(224,62,45);">yes</span></td><td style="height:29.7969px;">識別該檔案擁有者的唯一 ID</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">Size</td><td style="height:29.7969px;">number (int64)</td><td style="height:29.7969px;"><span style="color:rgb(224,62,45);">yes</span></td><td style="height:29.7969px;">以 bytes 為單位的檔案大小，64 位元長整數。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">UserId</td><td style="height:29.7969px;">string</td><td style="height:29.7969px;"><span style="color:rgb(224,62,45);">yes</span></td><td style="height:29.7969px;">存取該檔案的使用者 ID。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">UserFriendlyName </td><td style="height:29.7969px;">string</td><td style="height:29.7969px;">no</td><td style="height:29.7969px;">使用者名稱，用來顯示在使用者的編輯畫面。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">UserCanWrite</td><td style="height:29.7969px;">boolean</td><td style="height:29.7969px;">no</td><td style="height:29.7969px;">如果要能夠編輯，必須設為 true，預設為 false。</td></tr><tr><td>UserCanNotWriteRelative</td><td>boolean</td><td>no</td><td>禁止使用者重新命名或另存新檔。預設 true。</td></tr><tr style="height:68.9844px;"><td style="height:68.9844px;">PostMessageOrigin</td><td style="height:68.9844px;">string</td><td style="height:68.9844px;">no</td><td style="height:68.9844px;">這是告訴 OxOffice Online。 [<span class="doc">PostMessage API</span>](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api) 的 WOPI 主機位址。若未指定，將無法接收 OxOffice Online 傳來的狀態。</td></tr><tr style="height:68.9844px;"><td style="height:68.9844px;">HidePrintOption</td><td style="height:68.9844px;">boolean</td><td style="height:68.9844px;">no</td><td style="height:68.9844px;">隱藏 OxOffice Online 的列印按鈕。但可透過 [<span class="doc">PostMessage API</span>](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api) 來實作 WOPI 主機自己的列印 UI。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">DisablePrint</td><td style="height:29.7969px;">boolean</td><td style="height:29.7969px;">no</td><td style="height:29.7969px;">禁止列印文件。此外，也會在 UI 中隱藏列印選項。</td></tr><tr style="height:52.9844px;"><td style="height:52.9844px;">HideSaveOption</td><td style="height:52.9844px;">boolean</td><td style="height:52.9844px;">no</td><td style="height:52.9844px;">隱藏 OxOffice Online 的存檔按鈕。但仍可透過 [<span class="doc">PostMessage API</span>](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api) 觸發，這並不會影響自動存檔功能。</td></tr><tr style="height:57.7812px;"><td style="height:57.7812px;">HideExportOption</td><td style="height:57.7812px;">boolean</td><td style="height:57.7812px;">no</td><td style="height:57.7812px;">隱藏 OxOffice Online「下載為…」 按鈕及選項，但仍可透過 [<span class="doc">PostMessage API</span>](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api) 觸發。
    
    </td></tr><tr><td>DisableInactiveMessages</td><td>boolean</td><td>no</td><td>當停止編輯文件一段時間後，預設會在文件編輯區顯示提示文字。若禁用此功能，您監聽的 JavaScript，就必須在取得 Session\_Closed 或 User\_Idle 的 [<span class="doc">PostMessage API</span>](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api) 時，自行向使用者提供適當的訊息。
    
    </td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">DisableExport</td><td style="height:29.7969px;">boolean</td><td style="height:29.7969px;">no</td><td style="height:29.7969px;">禁止匯出或下載文件。此外，也會在 UI 中隱藏下載選項。</td></tr><tr style="height:46.5938px;"><td style="height:46.5938px;">DisableCopy</td><td style="height:46.5938px;">boolean</td><td style="height:46.5938px;">no</td><td style="height:46.5938px;">禁止複製內容到剪貼簿，但文件內部仍然可以複製以及貼上外部資料。</td></tr><tr><td>DownloadAsPostMessage</td><td>boolean</td><td>no</td><td>WOPI 主機自行接管下載任務，例如使用者觸發列印、權螢幕投影簡報以及其他檔案下載功能時，將觸發名為 Download\_As 的 [<span class="doc">PostMessage API</span>](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api)，傳送的值中，包含以下 JSON：
    
    ```json
    {
    	Type: 'print'|'slideshow'|'export',
    	URL: '...實際下載的 URL...'
    }
    ```
    
    </td></tr><tr style="height:46.5938px;"><td style="height:46.5938px;">EnableOwnerTermination</td><td style="height:46.5938px;">boolean</td><td style="height:46.5938px;">no</td><td style="height:46.5938px;">授予檔案擁有者可以終止其他共編者編輯的權利，意即可在共編時，將其他共編者踢出共編行列。</td></tr><tr style="height:46.5938px;"><td style="height:46.5938px;">LastModifiedTime</td><td style="height:46.5938px;">string</td><td style="height:46.5938px;">no</td><td style="height:46.5938px;">檔案最後修改時間。使用 ISO8601 格式表示之 UTC 日期時間（YYYY-MM-DDThh:mm:ss.nnnnZ）。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">UserExtraInfo</td><td style="height:29.7969px;">object</td><td style="height:29.7969px;">no</td><td style="height:29.7969px;">使用者延伸資訊。請參考：[**UserExtraInfo Details**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-userextrainfo-detail-0)。</td></tr></tbody></table>
    
    ##### **UserExtraInfo Details**：
    
    <table style="border-collapse:collapse;width:100%;height:469.516px;"><colgroup><col style="width:23.8926%;"></col><col style="width:14.6309%;"></col><col style="width:9.93397%;"></col><col style="width:51.5425%;"></col></colgroup><tbody><tr style="height:57.7812px;"><td class="align-center" style="height:57.7812px;">屬性</td><td style="height:57.7812px;">型別
    
    （Data type）
    
    </td><td style="height:57.7812px;">必要
    
    Required
    
    </td><td style="height:57.7812px;">說明</td></tr><tr><td>avatar</td><td>string
    
    </td><td>no
    
    </td><td>使用者頭像位址。</td></tr><tr><td>mail</td><td>string
    
    </td><td>no
    
    </td><td>使用者的電子郵件位址。</td></tr><tr><td>ip</td><td>string
    
    </td><td>no
    
    </td><td>使用者的 IP 位址。由於 OxOffice Online 可能透過 reverse proxy 轉址，所以自行抓取的 IP 可能不正確，此種狀況下，需透過您的應用程式，將使用者 IP 置於此屬性。</td></tr><tr><td>watermark</td><td>object
    
    </td><td>no
    
    </td><td>自訂編輯浮水印。請參考[ **Watermark Details**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-watermark-details%EF%BC%9A)。</td></tr></tbody></table>
    
    ##### **Watermark Details**：
    
    <table style="border-collapse:collapse;width:100%;height:469.516px;"><colgroup><col style="width:23.8926%;"></col><col style="width:14.6309%;"></col><col style="width:9.93397%;"></col><col style="width:51.5425%;"></col></colgroup><tbody><tr style="height:57.7812px;"><td class="align-center" style="height:57.7812px;">屬性</td><td style="height:57.7812px;">型別
    
    （Data type）
    
    </td><td style="height:57.7812px;">必要
    
    Required
    
    </td><td style="height:57.7812px;">說明</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;">editing</td><td style="height:29.7969px;">boolean</td><td style="height:29.7969px;">no</td><td style="height:29.7969px;">編輯時啟用，預設為 false。</td></tr><tr><td>printing</td><td>boolean</td><td>no</td><td>列印時啟用，預設為 false。</td></tr><tr><td>opacity</td><td>number(double)</td><td>no</td><td>不透明度。範圍 0.01-1.00預設為 0.20。此值愈小愈透明。</td></tr><tr><td>familyname</td><td>string</td><td>no</td><td>字型名稱。</td></tr><tr><td>angle</td><td>number(int)</td><td>no</td><td>旋轉角度。範圍 0-360，預設為 45°</td></tr><tr><td>color</td><td>string</td><td>no</td><td>CSS #rrggbb 表示之顏色。預設 #000000
    
    </td></tr><tr><td>bold</td><td>boolean</td><td>no</td><td>粗體。預設為 false。
    
    </td></tr><tr><td>italic</td><td>boolean</td><td>no</td><td>斜體。預設為 false。
    
    </td></tr><tr><td>outline</td><td>boolean</td><td>no</td><td>空心字。預設為 false。
    
    </td></tr><tr><td>shadow</td><td>boolean</td><td>no</td><td>陰影字。預設為 false。
    
    </td></tr><tr><td>text</td><td>string</td><td>no</td><td>浮水印文字，若需多行，各行需以 \\n 分隔。
    
    </td></tr></tbody></table>
2. #### **GetFile**：取得欲編輯的檔案**。**該 URL 被呼叫時，您的應用程式必須回傳檔案，給WOPI 客戶端（OxOffice Online）。
    
    ```
    GET https://<WOPI 主機 URL>/<...>/wopi/files/<id>/contents?access_token=<token>
    ```
3. #### ******PutFile******：更新正在編輯的檔案**。**該 URL 被呼叫時，您的應用程式必須接收 WOPI 客戶端（OxOffice Online）傳來的檔案。
    
    ```
    POST https://<WOPI host URL>/<...>/wopi/files/<id>/contents?access_token=<token>
    ```
    
    WOPI 主機必須檢查 OxOffice Online 傳來的標頭 – `X-WOPI-Override` 是否為英文大寫 **PUT**。
    
    再檢查 – `X-LOOL-WOPI-Timestamp`，這個標頭含有 ISO8601 格式的最近修改時間，您的應用程式需檢查該時間是否與雲端硬碟的最近修改時間一致，若不符合，則 WOPI 主機不應將之儲存到雲端硬碟，須回應給 OxOffice Oline 一個 HTTP 409 的狀態碼，以及一個 OxOffice Online 特定狀態碼：
    
    ```json
    {
    	'ＬOOLStatusCode': 1010
    }
    ```
    
    若時間相符，且應用程式成功儲存更新的檔案後，需回應 JSON 內容，格式同 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-oxoffice-online-%E5%8F%AF%E7%94%A8%E7%9A%84-) 的 LastModifiedTime，以便讓 OxOffice Online 與雲端硬碟之間，保持檔案時間同步：
    
    ```json
    {
      "LastModifiedTime": "YYYY-MM-DDThh:mm:ss.nnnnZ"
    }
    ```
    
    <p class="callout info">此外，**PutFile** 時，OxOffice Online 另外會帶入以下幾個標頭：  
    `X-LOOL-WOPI-IsModifiedByUser` – 使用者是否在存檔之前修改了文件（true），或者他們只是按了存檔（false）卻沒有進行任何修改。  
    `X-LOOL-WOPI-IsAutosave` – 自動存檔（true）或使用者按了「存檔」（false）按鈕。  
    `X-LOOL-WOPI-IsExitSave` – 當所有編輯者（共編狀態）都結束編輯，或離線時，將觸發自動存檔，並且該標頭會被設成 true。  
    </p>
4. #### ****PutRelativeFile：另存新檔或更改檔名。****該 URL 被呼叫時，您的應用程式必須接收 WOPI 客戶端（OxOffice Online）傳來的檔案。  
    
    
    ```
    POST https://<WOPI host URL>/<...>/wopi/files/<id>?access_token=<token>
    ```
    
    WOPI 主機必須檢查 OxOffice Online 傳來的標頭 – `X-WOPI-Override` 是否為英文大寫 **PUT\_RELATIVE**。
    
    如果您的應用程式不想讓使用者另存新檔或重新命名的話，請將 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-oxoffice-online-%E5%8F%AF%E7%94%A8%E7%9A%84-) 中的 UserCanNotWriteRelative 設成 true。  
    否則執行此功能時，OxOffice Online 會帶以下標頭：  
    `X-WOPI-SuggestedTarget` – UTF-7 編碼的新檔名。

# 逐步建立

想要一次把所有事物建立成功是不太可能的，我們將透過一步一步小型的、容易測試的步驟，來完成建置。

1. 在專用伺服器或虛擬機器中安裝 OxOffice Online，並確定瀏覽器可以正確連接到 OxOffice Online。 ```
    https://<WOPI 客戶端 URL(OxOffice Online)>:<port>/hosting/discovery
    ```
2. 在您的應用程式增加 WOPI REST API 的進入點。目前只須在 GET 請求時，回應「Hello World」即可，您可以透過瀏覽器進行測試。必要的話，需要用伺服器 rewrite 功能，將 REST API 導向您設計的進入點。此時，測試該進入點，應該會得到 Hello World 的回應：
    
    ```
    https://<WOPI 主機 URL>/<...>/wopi/files/<file id>/contents
    ```
3. 實作 CheckFileInfo 進入點。確保以下位址：
    
    ```
    https://<WOPI 主機 URL>/<...>/wopi/files/<file id>
    ```
    
    回應 JSON 內容，類似：
    
    ```json
    {"BaseFileName": "test.txt", "Size": 11}
    ```
4. 您應該可以從步驟 1，取得 OxOffice Online 預先建置的 discovery.xml，其中的 **urlsrc** 即為 OxOffice Online 編輯擋案用的位址，您可以把 `<span class="pre">WOPISrc=https://<WOPI</span> <span class="pre">主機</span> <span class="pre">URL>/<...>/wopi/files/<file id></span>` 加在其後，結果如下：
    
    ```
    https://<WOPI 客戶端 URL>:<port>/loleaflet/<hash>/loleaflet.html?WOPISrc=https://<WOPI 主機 URL>/<...>/wopi/files/<file id>
    ```
    
    接著建立一個名為 test.html 的測試頁，其中包含以下內容：
    
    ```html
    <html><body>
    	<form action="前面所建立的位址" enctype="multipart/form-data" method="post">
        	<input name="access_token" value="test" type="hidden"/>
            <input type="submit" value="Load OxOffice Online"/>
        </form>
    </body></html>
    ```
    
    當您從瀏覽器載入 test.html 並點擊 `<strong>Load OxOffice Online</strong>` 按鈕，將會開啟一份文字文件，內容是步驟 2 WOPI REST API 所提供的「Hello World」。如果您的 WOPI 主機和 OxOffice Online 位於不同的電腦上，請修改位於 OxOffice Online 主機的 /etc/oxool/oxoolwsd.xml，在`<span class="pre"><storage></span>` → `<span class="pre"><wopi></span>`下，新增一筆 `<host>` WOPI 主機的位址及埠號。
5. 到了這裡您已經掌握了基本要領了，然後我們開始擴充 JavaScript 的部份：建立一個包含 OxOffice Online 的 iframe，並且提供一個真正有存取權的 token。對 OxOffice Online 而言，這個存取用的 token 可以是隨機的文字、或是只有數字、字元以及底線所組成。
6. 現在，請更新您的 REST API 進入點，讓它能提供真實的檔案資料，以代替「Hello World」 以及步驟 3 所寫死的 JSON 回應。這裡，您需要送出真實檔案內容，並參考 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-checkfileinfo%EF%BC%9A%E4%BB%A5%E4%B8%8B-url) 表列屬性，送出相應的 JSON 回應。
7. 實作 [**PutFile**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-putfile%EF%BC%9A%E6%9B%B4%E6%96%B0%E6%AD%A3%E5%9C%A8%E7%B7%A8%E8%BC%AF%E7%9A%84%E6%AA%94%E6%A1%88%E3%80%82%E8%A9%B2--0) 進入點，讓編輯結果也能回存雲端硬碟。因此，需實作 POST 請求的進入點，如下：
    
    ```
    https://<WOPI 主機 URL>/<...>/wopi/files/<file id>/contents
    ```
8. 如果您暫時不想實作 [**PutRelativeFile**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-putrelativefile%EF%BC%9A%E5%8F%A6%E5%AD%98%E6%96%B0%E6%AA%94-0) 進入點，請把 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-oxoffice-online-%E5%8F%AF%E7%94%A8%E7%9A%84-) 的 **UserCanNotWriteRelative** 設為 **true**，待前述功能都完成後再完善它。

</body></html>

# 簡單的範例（來自 Collabora）

各種不同語言的簡單範例，不會很複雜，很好入手，您可以隨意地混合使用。

### Node.js 範例

透過 Node.js 中的 iFrame 整合 OxOffice Online 的簡單範例。 我們假設您熟悉 npm 和 node.js 框架。

[Node.js Example on GitHub](https://github.com/CollaboraOnline/collabora-online-sdk-examples//tree/master/webapp/nodejs)

### PHP 範例

透過 iFrame 在 PHP 中整合 OxOffice Online 的簡單範例。 我們假設您已經在電腦上安裝並啟動了 Apache Web 伺服器，並且 Apache 的 PHP 模組也已安裝並載入。

[PHP Example on GitHub](https://github.com/CollaboraOnline/collabora-online-sdk-examples/tree/master/webapp/php)

<section id="bkmrk-python-%E7%AF%84%E4%BE%8B-%E9%80%8F%E9%81%8E-iframe-">### Python 範例

透過 iFrame 在 Python 中整合 OxOffice Online 的簡單範例。 我們假設您熟悉 Python 和 Django 框架。

[Python Example on GitHub](https://github.com/CollaboraOnline/collabora-online-sdk-examples//tree/master/webapp/python)

</section><section id="bkmrk-reactjs-%E7%AF%84%E4%BE%8B-%E4%B8%80%E5%80%8B%E9%80%8F%E9%81%8E-ifra">### ReactJS 範例

一個透過 iFrame 在 ReactJS 和 Express 中整合 OxOffice Online 的簡單範例。 我們假設您熟悉 npm 和 node.js 框架。

[ReactJS Example on GitHub](https://github.com/CollaboraOnline/collabora-online-sdk-examples//tree/master/webapp/reactjs)

</section><section id="bkmrk-.net-%E7%AF%84%E4%BE%8B-%E9%80%8F%E9%81%8E-iframe-%E5%B0%87-">### .NET 範例

透過 iFrame 將 OxOffice Online 與 .NET 後端整合的簡單範例。

[.NET Example on GitHub](https://github.com/CollaboraOnline/collabora-online-sdk-examples//tree/master/webapp/dotNET)

</section>

# 進階整合

OxOffice Online 使用類似 WOPI 的協定和整合的主機進行互動。

有關該協議的詳細資訊，請參考 [WOPI docs.](https://docs.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/)

# 個人資訊流程

OxOffice Online 與使用者資料互動的唯一地方是從 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-checkfileinfo%EF%BC%9A%E4%BB%A5%E4%B8%8B-url) 取得的資料（包括文件名稱）。 這包含兩個地方：日誌和使用者界面。 透過匿名功能可以去識別化所有個人資訊，而瀏覽器使用者介面只是暫時的，不會做任何儲存。

[![OxOffice Online 個人資訊流程.png](https://docs.ossii.com.tw/uploads/images/gallery/2023-10/scaled-1680-/oxoffice-online.png)](https://docs.ossii.com.tw/uploads/images/gallery/2023-10/oxoffice-online.png)

# Cookies 和使用者本地儲存

OxOffice Online 不使用任何 cookies。 但 OxOffice Online 的後臺管理會使用 session cookie（關閉瀏覽器即消失） 儲存 JSON Web Token (JWT)，用來認證管理員身份。

使用者的偏好設定，會儲存在瀏覽器的 local storage（本地儲存） 中。請見下表：

- 切換暗色模式／亮色模式。
- 切換傳統介面（下拉選單 + 工具列）／分頁式操作介面。
- 關閉／顯示側邊攔。
- 關閉／顯示文件導覽。
- 關閉／顯示狀態列。
- 停用／啟用語法檢查。
- 最近使用的顏色及調色盤。

# PostMessage API

當 WOPI 主機將 OxOffice Online 包在一個框架（ iFrame）時，可以使用 PostMessage API 用來於和 OxOffice Online 互動。這對想要完善整合的 WOPI 主機來講，非常有用。

這個 API 主要基於 [WOPI 規範](https://docs.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/scenarios/postmessage)，很少擴充或修改。 所有送出的訊息，都採用以下形式：

```json
{
      "MessageId": "<MessageId>",
      "SendTime": "<Timestamp when message is sent>",
      "Values": {
             "<key>": "<value>"
      }
}
```

`SendTime` 是瀏覽器的 Date.now() 傳回的時間戳記。從 WOPI 主機發送的 PostMessage 也應該使用相同的格式。

需要注意的是，如 [WOPI 規範](https://docs.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/scenarios/postmessage)中所提到的，如果尚未收到 Host\_PostmessageReady，OxOffice Online 框架將忽略來自 WOPI 主機框架所有訊息。

另外，由於將 OxOffice Online 嵌入 iFrame 中，因此，OxOffice Online 要求 WOPI 主機回應 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-checkfileinfo%EF%BC%9A%E4%BB%A5%E4%B8%8B-url-0) 中的`PostMessageOrigin` 屬性，必須指定，否則 OxOffice Online 將不會送出任何訊息。

### **初始化**

##### 編輯器 🡆 WOPI 主機

<table id="bkmrk-messageid-values-%E8%AA%AA%E6%98%8E-" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:19.1595%;"></col><col style="width:34.6013%;"></col><col style="width:46.2392%;"></col></colgroup><tbody><tr><td>**MessageID**</td><td>**Values**</td><td class="align-center">**說明**</td></tr><tr><td>`<span style="color:rgb(186,55,42);">App_LoadingStatus</span>`</td><td>```json
Status: <String>
DocumentLoadedTime: <Timestamp>
Features: {"VersionStates":true}
```

</td><td>Status 值如下：

- **`<span class="pre">Frame_Ready</span>`**：OxOffice Online 的框架已經載入，並且顯示了。
- **`Features`**：編輯器的能力。支援的值有 - VersionStates，表示編輯器支援文件版本復原的功能。  
    詳請請見：[文件版本復原](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api#bkmrk-%E6%96%87%E4%BB%B6%E7%89%88%E6%9C%AC%E5%BE%A9%E5%8E%9F)
- `<span class="pre"><strong>Document_Loaded</strong></span>`：使用者文件已經完全載入，WOPI 主機可以開始使用 PostMessage API。  
    `DocumentLoadedTime` 代表載入文件所花費的時間。
- `<span class="pre"><strong>Failed</strong></span>`：文件載入失敗，WOPI 主機可以顯示 OxOffice Online 框架，向使用者顯示錯誤訊息。

</td></tr></tbody></table>

##### WOPI 主機 🡆 編輯器

<table id="bkmrk-messageid-values-%E8%AA%AA%E6%98%8E--0" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:22.1238%;"></col><col style="width:31.6463%;"></col><col style="width:46.2299%;"></col></colgroup><tbody><tr><td>**MessageID**</td><td>**Values**</td><td class="align-center">**說明**</td></tr><tr><td>`<span style="color:rgb(186,55,42);">Host_PostmessageReady</span>`</td><td>  
</td><td>通知 OxOffice Online，WOPI 主機已經準備好收送 PostMessage 了。</td></tr></tbody></table>

### **查詢**

WOPI 主機可以透過 PostMessage API 向編輯器查詢資料。所有來自編輯器回應的 MessageID，都會在您所傳遞的 MessageID 後面加上 "\_Resp"。

##### WOPI 主機 🡆 編輯器

<table id="bkmrk-messageid-values-%E8%AA%AA%E6%98%8E--1" style="border-collapse:collapse;width:100%;border-width:1px;"><colgroup><col style="width:20.6409%;"></col><col style="width:33.1291%;"></col><col style="width:46.2299%;"></col></colgroup><tbody><tr style="height:29.7969px;"><td style="height:29.7969px;border-width:1px;">**MessageID**</td><td style="height:29.7969px;border-width:1px;">**Values**</td><td class="align-center" style="height:29.7969px;border-width:1px;">**說明**</td></tr><tr style="height:30.1094px;"><td style="height:30.1094px;border-width:1px;"><span style="color:rgb(186,55,42);">`Get_Views`</span></td><td style="height:30.1094px;border-width:1px;">  
</td><td style="height:30.1094px;border-width:1px;">向編輯器查詢，目前正在編輯文件所有使用者資訊。回應會以 MessageID 為 <span style="color:rgb(186,55,42);">`Get_Views_Resp`</span> 的格式傳回。</td></tr><tr style="height:29.7969px;"><td style="height:29.7969px;border-width:1px;">`<span style="color:rgb(186,55,42);">Get_Export_Formats</span>`</td><td style="height:29.7969px;border-width:1px;">  
</td><td style="height:29.7969px;border-width:1px;">向編輯器查詢，目前開啟的文件，支援哪些匯出格式。</td></tr></tbody></table>

得到的回應。

##### 編輯器 🡆 WOPI 主機

<table id="bkmrk-messageid-values-%E8%AA%AA%E6%98%8E--2" style="border-collapse:collapse;width:100%;border-width:1px;"><colgroup><col style="width:24.7202%;"></col><col style="width:29.0499%;"></col><col style="width:46.2299%;"></col></colgroup><tbody><tr style="height:29.7969px;"><td style="height:29.7969px;border-width:1px;">**MessageID**</td><td style="height:29.7969px;border-width:1px;">**Values**</td><td class="align-center" style="height:29.7969px;border-width:1px;">**說明**</td></tr><tr style="height:46.9062px;"><td style="height:46.9062px;border-width:1px;"><span style="color:rgb(186,55,42);">`Get_Views_Resp`</span></td><td style="height:46.9062px;border-width:1px;">```json
[
  {
    ViewId: <Number>
	UserId: <String>
	UserName: <String>
	Color: <Number>
	ReadOnly: <Boolean>
	IsCurrentView: <Boolean>
  },...
]
```

</td><td style="height:46.9062px;border-width:1px;">使用 `<span style="color:rgb(186,55,42);">Get_Views</span>` 查詢，所得到的所有使用者詳細資訊。</td></tr><tr style="height:30.1094px;"><td style="height:30.1094px;border-width:1px;">`<span style="color:rgb(186,55,42);"><span class="pre">Get_E</span><span class="pre">xport_Formats_Resp</span></span>`</td><td style="height:30.1094px;border-width:1px;">```json
[
  {
    Label: <String>
	Format: <String>
  },...
]
```

</td><td style="height:30.1094px;border-width:1px;">使用 `<span style="color:rgb(186,55,42);">Get_Export_Formats</span>` 查詢，所得到的回應。

- `Label` 是解釋該格式的本地語系內容。
- `Format` 是該格式的副檔名。

</td></tr></tbody></table>

### **編輯者管理**

##### WOPI 主機 🡆 編輯器

<table id="bkmrk-messageid-values-%E8%AA%AA%E6%98%8E--3" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:22.1238%;"></col><col style="width:31.6463%;"></col><col style="width:46.2299%;"></col></colgroup><tbody><tr><td>**MessageID**</td><td>**Values**</td><td class="align-center">**說明**</td></tr><tr><td>`<span style="color:rgb(186,55,42);">Action_RemoveView</span>`</td><td>```
ViewId: <Number>
```

</td><td>移除指定 Id 的編編者，也就是將某位編輯者踢出共編行列。</td></tr></tbody></table>

##### 編輯器 🡆 WOPI 主機

<table id="bkmrk-messageid-values-%E8%AA%AA%E6%98%8E--4" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:22.1238%;"></col><col style="width:31.6463%;"></col><col style="width:46.2299%;"></col></colgroup><tbody><tr><td>**MessageID**</td><td>**Values**</td><td class="align-center">**說明**</td></tr><tr><td>`<span style="color:rgb(186,55,42);">Views_List</span>`</td><td>與 <span style="color:rgb(186,55,42);">`Get_Views_Resp`</span> 相同。</td><td>所有正在參與該文件編輯的使用者詳細資訊。每次有編輯者加入或離開都會觸發此資訊。

</td></tr></tbody></table>

### **各類指令**

<section id="bkmrk-wopi-%E4%B8%BB%E6%A9%9F-%F0%9F%A1%86-%E7%B7%A8%E8%BC%AF%E5%99%A8-messag">##### WOPI 主機 🡆 編輯器

<section id="bkmrk-messageid-values-des"><section id="bkmrk-messageid-values-des-0"><div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-width:1px;border-style:solid;"><thead><tr class="row-odd" style="height:35.3906px;"><th class="head" style="width:22.1247%;height:35.3906px;border-width:1px;">MessageId

</th><th class="head" style="width:31.769%;height:35.3906px;border-width:1px;">Values

</th><th class="head" style="width:46.1063%;height:35.3906px;border-width:1px;">**說明**

</th></tr></thead><tbody><tr class="row-even" style="height:363.047px;"><td style="width:22.1247%;height:363.047px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_Save</span>`</span>

</td><td style="width:31.769%;height:363.047px;border-width:1px;">```json
DontTerminateEdit: <boolean>
DontSaveIfUnmodified: <boolean>
Notify: <boolean>
ExtendedData: <String>
```

</td><td style="width:46.1063%;height:363.047px;border-width:1px;">儲存文件。

- `<span class="pre">DontTerminateEdit</span>` 和試算表相關，這會中斷編輯模式（文字游標消失）。設為 true 的話，就能儲存檔案，而不會終止編輯模式，也不會中斷使用者編輯試算表。
- `<span class="pre">DontSaveIfUnmodified</span>` 如果文件內容沒有變更（只是移動了游標），這會阻止 OxOffice Online 把檔案回存雲端硬碟， 如此可防止不必要的版本修訂。
- `<span class="pre">Notify</span>` 設為 true 時，會在文件儲存完後，通知 WOPI 主機。詳情請參考 <span style="color:rgb(186,55,42);">`<span class="pre">Action_Save_Resp</span>`</span>
- `<span class="pre">ExtendedData</span>` 如果標頭`<span class="pre">X-LOOL-WOPI-ExtendedData</span>` 有資料，則會原封不動地傳給WOPI 主機。  
    資料內容不可是空白、換行或其他不可列印字元。  
    OxOffice Online 可以傳遞多個值，然後 WOPI 主機可以進一步對處理這些值。

</td></tr><tr class="row-odd" style="height:126.953px;"><td style="width:22.1247%;height:126.953px;border-width:1px;">`<span class="pre"><span style="color:rgb(186,55,42);">Action_SaveAs</span></span>`

</td><td style="width:31.769%;height:126.953px;border-width:1px;">```
Filename: <String>
Notify: <boolean>
```

</td><td style="width:46.1063%;height:126.953px;border-width:1px;">建立文件副本，並給予新檔名（另存新檔）。

- `<span class="pre">Filename</span>` 新文件檔名。
- `<span class="pre">Notify</span>` 設為 true 時，會在文件儲存完後，通知 WOPI 主機。詳情請參考 <span style="color:rgb(186,55,42);">`<span class="pre">Action_Save_Resp</span>`</span>

</td></tr><tr class="row-even" style="height:78.0469px;"><td style="width:22.1247%;height:78.0469px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_FollowUser</span>`</span>

</td><td style="width:31.769%;height:78.0469px;border-width:1px;">```
Follow: <Boolean>
ViewId: <Number>
```

</td><td style="width:46.1063%;height:78.0469px;border-width:1px;">開啟或關閉跟隨特定編輯者功能。這會讓編輯畫面和指定對象同步。

- `<span class="pre">Follow</span>` true 開啟/ false 關閉。
- `<span class="pre">ViewId</span>` 要跟隨的編輯者 ID。

</td></tr><tr class="row-odd" style="height:35.3906px;"><td style="width:22.1247%;height:35.3906px;border-width:1px;">`<span class="pre"><span style="color:rgb(186,55,42);">Action_Close</span></span>`

</td><td style="width:31.769%;height:35.3906px;border-width:1px;">  
</td><td style="width:46.1063%;height:35.3906px;border-width:1px;">關閉文件。

</td></tr><tr class="row-even" style="height:35.3906px;"><td style="width:22.1247%;height:35.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_Print</span>`</span>

</td><td style="width:31.769%;height:35.3906px;border-width:1px;">  
</td><td style="width:46.1063%;height:35.3906px;border-width:1px;">列印文件。

</td></tr><tr class="row-odd" style="height:80.1719px;"><td style="width:22.1247%;height:80.1719px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_Export</span>`</span>

</td><td style="width:31.769%;height:80.1719px;border-width:1px;">```
Format: <String>
```

</td><td style="width:46.1063%;height:80.1719px;border-width:1px;">以 `<span class="pre">Format</span>` 指定的格式下載文件。格式只能是 <span style="color:rgb(186,55,42);">`<span class="pre">Get_Export_Formats</span>` </span>所指定列表其中之一。

</td></tr><tr class="row-even" style="height:61.3906px;"><td style="width:22.1247%;height:61.3906px;border-width:1px;">`<span class="pre"><span style="color:rgb(186,55,42);">Action_InsertGraphics</span></span>`

</td><td style="width:31.769%;height:61.3906px;border-width:1px;">```
url: <String>
```

</td><td style="width:46.1063%;height:61.3906px;border-width:1px;">從指定的位址下載圖片並插入文件中。

</td></tr><tr class="row-odd" style="height:61.3906px;"><td style="width:22.1247%;height:61.3906px;border-width:1px;">`<span class="pre"><span style="color:rgb(186,55,42);">Action_ShowBusy</span></span>`

</td><td style="width:31.769%;height:61.3906px;border-width:1px;">```
Label: <String>
```

</td><td style="width:46.1063%;height:61.3906px;border-width:1px;">顯示具有 Lable 文字的忙碌畫面，類似存檔中的畫面。

</td></tr><tr class="row-even" style="height:35.3906px;"><td style="width:22.1247%;height:35.3906px;border-width:1px;">`<span class="pre"><span style="color:rgb(186,55,42);">Action_HideBusy</span></span>`

</td><td style="width:31.769%;height:35.3906px;border-width:1px;">  
</td><td style="width:46.1063%;height:35.3906px;border-width:1px;">如果有忙碌畫面的話，關掉忙碌畫面。

</td></tr><tr class="row-odd" style="height:92.4219px;"><td style="width:22.1247%;height:92.4219px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_ChangeUIMode</span>`</span>

</td><td style="width:31.769%;height:92.4219px;border-width:1px;">```
Mode:'classic' 或 'notebookbar'
```

</td><td style="width:46.1063%;height:92.4219px;border-width:1px;">變更使用者介面：

- classic：傳統下拉選單＋工具列。
- notebookbar：分頁式介面。

</td></tr><tr class="row-even" style="height:102.562px;"><td style="width:22.1247%;height:102.562px;border-width:1px;">`<span class="pre"><span style="color:rgb(186,55,42);">Action_Paste</span></span>`

</td><td style="width:31.769%;height:102.562px;border-width:1px;">```
Mimetype: <string>
Data: <string>
```

</td><td style="width:46.1063%;height:102.562px;border-width:1px;">繞過內部貼上機制，直接貼上資料到文件內。

例如：

`<span class="pre">Values:</span> <span class="pre">{Mimetype:</span> <span class="pre">"text/plain;charset=utf-8",</span> <span class="pre">Data:</span> <span class="pre">"haha"}};</span>`

</td></tr></tbody></table>

</div></section><section id="bkmrk-%E7%B7%A8%E8%BC%AF%E5%99%A8-%F0%9F%A1%86-wopi-%E4%B8%BB%E6%A9%9F%EF%BC%88%E5%9B%9E%E6%87%89%EF%BC%89-me">##### 編輯器 🡆 WOPI 主機（回應）

<div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-width:1px;border-style:solid;"><colgroup><col style="width:25.5885%;"></col><col style="width:28.3052%;"></col><col style="width:46.1063%;"></col></colgroup><thead><tr class="row-odd" style="height:35.3906px;"><th class="head" style="height:35.3906px;border-width:1px;">MessageId

</th><th class="head" style="height:35.3906px;border-width:1px;">Values

</th><th class="head" style="height:35.3906px;border-width:1px;">**說明**

</th></tr></thead><tbody><tr class="row-even" style="height:126.953px;"><td style="height:126.953px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_Load_Resp</span>`</span>

</td><td style="height:126.953px;border-width:1px;">```
success: <boolean>
result:  <string>
errorMsg: <string>
```

</td><td style="height:126.953px;border-width:1px;">文件載入完成。

- `<span class="pre">success</span>` true 成功、false 失敗。
- `<span class="pre">result</span>` 文件未載入的原因。
- `<span class="pre">errorMsg</span>` 載入失敗時的詳細錯誤訊息。也可能包含從 WOPI 主機傳回的錯誤訊息。

</td></tr><tr class="row-odd" style="height:256.641px;"><td style="height:256.641px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_Save_Resp</span>`</span>

</td><td style="height:256.641px;border-width:1px;">```
success: <boolean>
result: <string>
errorMsg: <string>
fileName: <string>
```

</td><td style="height:256.641px;border-width:1px;">文件儲存完成。只有<span style="color:rgb(186,55,42);">`<span class="pre">Action_Save</span>`</span> 或 `<span class="pre"><span style="color:rgb(186,55,42);">Action_SaveAs</span></span>` PostMessage API 將 `<span class="pre">Notify</span>` 參數設為 true 時，才會得到這個回應。

- `<span class="pre">success</span>` true 成功、false 失敗。
- `<span class="pre">result</span>` 文件未儲存的原因。如果文件因未修改而未儲存，則此參數包含字串「unmodified」。 這種情況下，WOPI 主機可以確保不會儲存到位編耕的文件。
- `<span class="pre">errorMsg</span>` 儲存失敗時的詳細錯誤訊息。 也可能包含從 WOPI 主機傳回的錯誤訊息。
- `<span class="pre">fileName</span>` 如果 `success `為 true 則包含已儲存的檔案名稱。

</td></tr><tr class="row-even" style="height:144.062px;"><td style="height:144.062px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">FollowUser_Changed</span>`</span>

</td><td style="height:144.062px;border-width:1px;">```
FollowedViewId: <Number>
IsFollowUser: <Boolean>
IsFollowEditor: <Boolean>
```

</td><td style="height:144.062px;border-width:1px;">跟隨狀態變更。

- `<span class="pre">FollowedViewId</span>` 哪個編輯者被跟隨。
- `<span class="pre">IsFollowUser</span>`是否啟用跟隨編輯者。
- `<span class="pre">IsFollowEditor</span>` 是否啟用跟隨編輯器。
- 如果 `<span class="pre">IsFollowUser</span>` 和 `<span class="pre">IsFollowEditor</span>` 都是 false，表示取消跟隨功能。

</td></tr><tr class="row-odd" style="height:54px;"><td style="height:54px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Action_ChangeUIMode_Resp</span>`</span>

</td><td style="height:54px;border-width:1px;">```
Mode: <string>
```

</td><td style="height:54px;border-width:1px;">變更使用介面。

`<span class="pre">Mode</span>` 使用哪種介面。

</td></tr></tbody></table>

</div></section></section><section id="bkmrk-version-restore-wopi">### **文件版本復原**

<section id="bkmrk-wopi-host-to-editor-">##### WOPI 主機 🡆 編輯器

<div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-style:solid;"><colgroup><col style="width:23.6094%;"></col><col style="width:26.4559%;"></col><col style="width:49.9347%;"></col></colgroup><thead><tr class="row-odd"><th class="head">MessageId

</th><th class="head">Values

</th><th class="head">**說明**

</th></tr></thead><tbody><tr class="row-even"><td><span style="color:rgb(186,55,42);">`<span class="pre">Host_VersionRestore</span>`</span>

</td><td>```
Status: <string>
```

</td><td>復原版本。唯一可能的值是 Pre\_Restore

此訊息由主機在實際復原文件<span style="color:rgb(186,55,42);">之前</span>以及使用者確定復原<span style="color:rgb(186,55,42);">之後</span>傳送。

這樣，如果目前文件，還有未儲存的修改，OxOffice Online 可以在恢復文件之前將它們儲存到雲端硬碟中。

</td></tr></tbody></table>

</div></section><section id="bkmrk-editor-to-wopi-host-">##### 編輯器 🡆 WOPI 主機

<div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-style:solid;"><colgroup><col style="width:23.8566%;"></col><col style="width:26.3143%;"></col><col style="width:49.8291%;"></col></colgroup><thead><tr class="row-odd"><th class="head">MessageId

</th><th class="head">Values

</th><th class="head">說明

</th></tr></thead><tbody><tr class="row-even"><td><span style="color:rgb(186,55,42);">`<span class="pre">App_VersionRestore</span>`</span>

</td><td>```
Status: <string>
```

</td><td>這是對 <span style="color:rgb(186,55,42);">`Host_VersionRestore`</span> 訊息的回應。

目前 `<span class="pre">Status</span>` 的值為 `<span class="pre">Pre_Restore_Ack</span>`

表示 WOPI 主機可以繼續將文件回復到較早的版本。

</td></tr></tbody></table>

</div><p class="callout success">只有當 `<span style="color:rgb(186,55,42);">App_LoadingStatus</span>` 包含功能中的 **VersionStates** 時，才會發出這些訊息。 否則，主機可以立即將版本還原到早期版本。</p>

</section></section><section id="bkmrk-miscellaneous-wopi-h">### **其他**

<section id="bkmrk-wopi-host-to-editor--0">##### WOPI 主機 🡆 編輯器

<div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-width:1px;border-style:solid;"><colgroup><col style="width:24.9691%;"></col><col style="width:25.3399%;"></col><col style="width:49.691%;"></col></colgroup><thead><tr class="row-odd" style="height:35.3906px;"><th class="head" style="height:35.3906px;border-width:1px;">MessageId

</th><th class="head" style="height:35.3906px;border-width:1px;">Values

</th><th class="head" style="height:35.3906px;border-width:1px;">說明

</th></tr></thead><tbody><tr class="row-even" style="height:430.828px;"><td style="height:430.828px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Insert_Button</span>`</span>

</td><td style="height:430.828px;border-width:1px;">```json
id: <string>
imgurl: <string>
hint: <string>
accessKey: <string>
mobile: <boolean>
tablet: <boolean>
insertBefore: <string>
unoCommand: <string>
```

</td><td style="height:430.828px;border-width:1px;">在頂端工具列插入自訂按鈕（僅顯示在傳統介面）。該按鈕被點擊時，若未指定 `unoCommand` 屬性，會觸發 <span style="color:rgb(186,55,42);">`<span class="pre">Clicked_Button</span>`</span> 的事件給 WOPI 主機，讓 WOPI 主機做相應處理。

- `<span class="pre">id</span>` 代表該按鈕的不可重複 ID。為了避免和原先工具列的其他按鈕 ID 衝突，建議在此類 ID 前，加上主機特有的名稱。  
    如果和其他按鈕衝突，則不會新增按鈕。
- `<span class="pre">imgurl</span>` 按鈕圖示位址。建議的大小為 24x24 px。圖片位址應和 WOPI 主機為同一來源，易免違反 `<span class="pre">Content-Security-Policy</span>`。
- `<span class="pre">hint</span>` 按鈕的提示。
- `accessKey` 快捷鍵。使用者可以搭配 Alt 或 Alt+Shift 加上此鍵。注意不要和其他快捷鍵衝突。
- `<span class="pre">mobile</span>` 手機模式時，是否顯示該按鈕。
- `<span class="pre">tablet</span>` 平板模式時，是否顯示該按鈕。
- `<span class="pre">insertBefore</span>` 新增在哪個 按鈕 ID 之前。請參考[查詢工具列按鈕 ID](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api#bkmrk-%E6%9F%A5%E8%A9%A2%E5%B7%A5%E5%85%B7%E5%88%97%E6%8C%89%E9%88%95-id)
- `<span class="pre">unoCommand</span>` UNO 命令（[請參閱此處](https://wiki.documentfoundation.org/Development/DispatchCommands)）。 設置這個屬性，就不會傳送 `Clicked_Button` 訊息給 WOPI 主機，而會交由 LibreOffice 直接執行此命令。

</td></tr><tr class="row-odd" style="height:61.3906px;"><td style="height:61.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Hide_Button</span>`</span>

</td><td style="height:61.3906px;border-width:1px;">```
id: <string>
```

</td><td style="height:61.3906px;border-width:1px;">隱藏工具列按鈕。`<span class="pre">id</span>` 為按鈕 ID（[查詢工具列按鈕 ID](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api#bkmrk-%E6%9F%A5%E8%A9%A2%E5%B7%A5%E5%85%B7%E5%88%97%E6%8C%89%E9%88%95-id)）

</td></tr><tr class="row-even" style="height:61.3906px;"><td style="height:61.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Show_Button</span>`</span>

</td><td style="height:61.3906px;border-width:1px;">```
id: <string>
```

</td><td style="height:61.3906px;border-width:1px;">顯示工具列按鈕。`<span class="pre">id</span>` 為按鈕 ID（[查詢工具列按鈕 ID](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api#bkmrk-%E6%9F%A5%E8%A9%A2%E5%B7%A5%E5%85%B7%E5%88%97%E6%8C%89%E9%88%95-id)）

</td></tr><tr class="row-odd" style="height:61.3906px;"><td style="height:61.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Remove_Button</span>`</span>

</td><td style="height:61.3906px;border-width:1px;">```
id: <string>
```

</td><td style="height:61.3906px;border-width:1px;">移除工具列按鈕。`<span class="pre">id</span>` 為按鈕 ID（[查詢工具列按鈕 ID](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api#bkmrk-%E6%9F%A5%E8%A9%A2%E5%B7%A5%E5%85%B7%E5%88%97%E6%8C%89%E9%88%95-id)）

</td></tr><tr class="row-even" style="height:61.3906px;"><td style="height:61.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Remove_Statusbar_Element</span>`</span>

</td><td style="height:61.3906px;border-width:1px;">```
id: <string>
```

</td><td style="height:61.3906px;border-width:1px;">移除狀態列元件。 `<span class="pre">id</span>` 是狀態列元件 ID（請參考：[查詢狀態列元件 ID](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/postmessage-api#bkmrk-%E6%9F%A5%E8%A9%A2%E7%8B%80%E6%85%8B%E5%88%97%E5%85%83%E4%BB%B6-id)）

</td></tr><tr class="row-odd" style="height:35.3906px;"><td style="height:35.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Hide_Menubar</span>`</span>

</td><td style="height:35.3906px;border-width:1px;">  
</td><td style="height:35.3906px;border-width:1px;">隱藏下拉選單列。

</td></tr><tr class="row-even" style="height:35.3906px;"><td style="height:35.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Show_Menubar</span>`</span>

</td><td style="height:35.3906px;border-width:1px;">  
</td><td style="height:35.3906px;border-width:1px;">顯示下拉選單列。

</td></tr><tr class="row-odd" style="height:108.938px;"><td style="height:108.938px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Grab_Focus</span>`</span>

</td><td style="height:108.938px;border-width:1px;">  
</td><td style="height:108.938px;border-width:1px;">喚醒休眠中的編輯器。通常編輯者在一段時間後沒有任何動作時，編輯器會進入休眠狀態（編輯介面變暗，並有訊息顯示），必要時，會中斷與伺服器連線。<span style="color:rgb(186,55,42);">`<span class="pre">Grab_Focus</span>`</span> 會重新喚醒編輯器，必要時重新連線伺服器。

</td></tr><tr class="row-even" style="height:35.3906px;"><td style="height:35.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Hide_Ruler</span>`</span>

</td><td style="height:35.3906px;border-width:1px;">  
</td><td style="height:35.3906px;border-width:1px;">隱藏水平尺規（僅適用文字文件 Writer）。

</td></tr><tr class="row-odd" style="height:35.3906px;"><td style="height:35.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Show_Ruler</span>`</span>

</td><td style="height:35.3906px;border-width:1px;">  
</td><td style="height:35.3906px;border-width:1px;">隱藏水平尺規（僅適用文字文件 Writer）。

</td></tr><tr class="row-even" style="height:148.391px;"><td style="height:148.391px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Hide_Menu_Item</span>`</span>

</td><td style="height:148.391px;border-width:1px;">```
id: <string>
```

</td><td style="height:148.391px;border-width:1px;">隱藏下拉選單中的選項。`<span class="pre">id</span>` 是指選項 ID ，它們分別定義在：

- 文字文件：[uiconfig/text/menubar.json](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/uiconfig/text/menubar.json)
- 試算表文件：[uiconfig/spreadsheet/menubar.json](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/uiconfig/spreadsheet/menubar.json)
- 簡報文件：[uiconfig/presentation/menubar.json](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/uiconfig/presentation/menubar.json)
- 繪圖文件：[uiconfig/drawing/menubar.json](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/uiconfig/drawing/menubar.json)

</td></tr><tr class="row-odd" style="height:61.3906px;"><td style="height:61.3906px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Show_Menu_Item</span>`</span>

</td><td style="height:61.3906px;border-width:1px;">```
id: <string>
```

</td><td style="height:61.3906px;border-width:1px;">顯示下拉選單中的選項。`<span class="pre">id</span>` 是指選項 ID。請參閱前項 <span style="color:rgb(186,55,42);">`<span class="pre">Hide_Menu_Item</span>`</span>

</td></tr><tr class="row-even" style="height:585.641px;"><td style="height:585.641px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Disable_Default_UIAction</span>`</span>

</td><td style="height:585.641px;border-width:1px;">```
action: <string>
disable: <Boolean>
```

</td><td style="height:585.641px;border-width:1px;">停用或啟用某個 UI 命令的預設行為。

- `<span class="pre">action</span>` 動作名稱。
- `<span class="pre">disable</span>` 表示停用 (**true**) 或啟用 (**false**) 預設行為。

設為 true 時，指定的動作只會發出 `<span class="pre">postMessage</span>` 而不會執行預設行為。這讓 WOPI 主機可以根據 `<span class="pre">postMessage</span>` 事件，進行必要處理。

請注意，某些動作沒有任何預設行為（如 `UI_SaveAs` 和 `UI_Share`），因此，這對它們沒有影響；但無論如何，他們只會發出 `<span class="pre">postMessage</span>` 通知，而不會採取預設行為

舉例來說，`UI_Save` 預設會呼叫儲存命令（透過選單、工具列按鈕或鍵盤快捷鍵）。如果透過 `Disable_Default_UIAction` 停用 `UI_Save`，則不會呼叫儲存命令。 停用 `UI_Save` 的 WOPI 主機，應該在文件需要儲存時，自行發出<span style="color:rgb(186,55,42);"> `Action_Save`</span>。

同樣地，當停用 `UI_Close` 時，呼叫 `UI_Close`，並不會真的文件關閉，而是會發出 postMessage 通知，並且由 WOPI 主機在需要時發出 <span style="color:rgb(186,55,42);">`Action_Close`</span>。

WOPI 主機必須很小心，不要在啟用預設行為的狀態下，發出重複的`Action_xxyyzz` 等指令。相反地，只能在停用預設行為時，才能自行發出操作。 注意：目前也只有 `UI_Save` 和 `UI_Close` 以及 `UI_Hyperlink` 能停用🤣

</td></tr><tr class="row-odd" style="height:76px;"><td style="height:76px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">Send_UNO_Command</span>`</span>

</td><td style="height:76px;border-width:1px;">```json
Command: <string>
Args: <object>
```

</td><td style="height:76px;border-width:1px;">向編輯器傳送 UNO 命令。請參考：[framed.doc.html](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/html/framed.doc.html)

</td></tr></tbody></table>

</div></section><section id="bkmrk-finding-toolbar-butt-0">### **查詢工具列按鈕 ID**

工具列按鈕 ID 在下列程式中的 getToolItems/create 函數定義：

- [Control.TopToolbar.js](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/src/control/Control.TopToolbar.js)：用於桌面或平板電腦上的頂部工具列。
- [Control.MobileTopBar.js](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/src/control/Control.MobileTopBar.js)：用於智慧型手機上的頂部工具列。
- [Control.MobileBottomBar.js](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/src/control/Control.MobileBottomBar.js)：用於智慧型手機底部工具列。
- [Control.StatusBar.js](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/src/control/Control.ReadonlyBar.js)：用於桌面上的狀態列。

這些通常不會改變，但並不保證這些 ID 會穩定存在。

</section><section id="bkmrk-finding-status-bar-e-0">### **查詢狀態列元件 ID**

狀態列元件 ID 定義在 [Control.StatusBar.js](https://github.com/OSSII/oxool-community/blob/v4/loleaflet/src/control/Control.StatusBar.js) 的 `<span class="pre">onDocLayerInit</span>` 函數中。通常不會改變，但並不保證這些 ID 會穩定存在。

### **事件通知**

</section><section id="bkmrk-editor-to-wopi-host--0">##### 編輯器 🡆 WOPI 主機

<div class="wy-table-responsive"><table style="border-collapse:collapse;border-style:solid;width:100%;height:817.672px;"><colgroup><col style="width:21.2608%;"></col><col style="width:30.4064%;"></col><col style="width:48.3328%;"></col></colgroup><thead><tr class="row-odd" style="height:35.3906px;"><th class="head" style="height:35.3906px;">MessageId

</th><th class="head" style="height:35.3906px;">Values

</th><th class="head" style="height:35.3906px;">說明

</th></tr></thead><tbody><tr class="row-even" style="height:53.1719px;"><td style="height:53.1719px;"><span style="color:rgb(186,55,42);">`<span class="pre">Clicked_Button</span>`</span>

</td><td style="height:53.1719px;">```
id: <string>
```

</td><td style="height:53.1719px;">點擊透過前面的 <span style="color:rgb(186,55,42);">`Insert_Button`</span> API 新增的自訂按鈕時會發出此事件。

</td></tr><tr class="row-odd" style="height:147.344px;"><td style="height:147.344px;"><span style="color:rgb(186,55,42);">`<span class="pre">Download_As</span>`</span>

</td><td style="height:147.344px;">```
Type: 'print'|'slideshow'|'export'
URL: <string>
```

</td><td style="height:147.344px;">當使用者選擇「列印」或「全螢幕投影」或「下載為…」，且 WOPI 主機在 [**CheckFileInfo**](https://docs.ossii.com.tw/books/oxoffice-online-8LU/page/8efb1#bkmrk-oxoffice-online-%E5%8F%AF%E7%94%A8%E7%9A%84-) 的`<span class="pre">DownloadAsPostMessage</span>` 屬性中，指定要接管時，就會觸發此事件。

這應用於無法依賴瀏覽器下載的情況，例如手機的 Web view 模式。

</td></tr><tr><td><span style="color:rgb(0,0,0);"><span class="pre"><span style="color:rgb(186,55,42);">`UI_Close`</span></span></span>

</td><td>```json
EverModified: true | false
```

</td><td>當使用者選擇關閉文件，或點擊關閉按鈕，將觸發此事件。`EverModified` 指出此文件是否修改過。

</td></tr><tr><td><span style="color:rgb(186,55,42);">`<span class="pre">UI_Save</span>`</span>

</td><td>```json
source: 'filemenu' | 'notebookbar' | 'toolbar'
```

</td><td>使用者選擇儲存。`source` 指出透過何種方式儲存。

</td></tr><tr class="row-even" style="height:102.562px;"><td style="height:102.562px;"><span style="color:rgb(186,55,42);">`<span class="pre">UI_CreateFile</span>`</span>

</td><td style="height:102.562px;">```json
DocumentType: 'text' | 'spreadsheet' | 'presentation' | 'drawing'
```

</td><td style="height:102.562px;">要求 WOPI 主機開啟新的瀏覽器分頁，並建立新文件。 文件類型以 DocumentType 參數傳遞，可以是「text（文字文件）」、「spreadsheet（試算表）」、「presentation（簡報）」或「drawing（繪圖）」。

</td></tr><tr class="row-odd" style="height:80.1719px;"><td style="height:80.1719px;"><span style="color:rgb(186,55,42);">`<span class="pre">UI_SaveAs</span>`</span>

</td><td style="height:80.1719px;">  
</td><td style="height:80.1719px;">要求 WOPI 主機顯示另存新檔 UI，以便使用者可以選擇路徑和檔案名稱來建立目前檔案的副本。 若使用者確定存檔，則 WOPI 主機需以 <span style="color:rgb(186,55,42);">`Action_SaveAs`</span> 命令編輯器發送檔案內容。

</td></tr><tr class="row-even" style="height:57.7812px;"><td style="height:57.7812px;"><span style="color:rgb(186,55,42);">`<span class="pre">UI_Cancel_Password</span>`</span>

</td><td style="height:57.7812px;">  
</td><td style="height:57.7812px;">通知 WOPI 主機使用者在開啟有密碼保護的檔案時，點擊了「取消」，而不是輸入密碼。

</td></tr><tr class="row-odd" style="height:171.516px;"><td style="height:171.516px;"><span style="color:rgb(186,55,42);">`<span class="pre">UI_Hyperlink</span>`</span>

</td><td style="height:171.516px;">  
</td><td style="height:171.516px;">通知 WOPI 主機只用者點擊了超連結，並確認確實想要離開文件以開啟超連結。這對於將 OxOffice Online 嵌入到行動裝置 iframe 中的 App 特別有用，實際嘗試開啟新視窗，應觸發在 Android 上的 Activity（或在 iOS 上類似的操作）。

WOPI 主機可能也想利用 <span style="color:rgb(186,55,42);">`<span class="pre">Disable_Default_UIAction</span>`</span> 來接管 <span style="color:rgb(186,55,42);">`UI_Hyperlink`</span>

</td></tr><tr class="row-even" style="height:169.734px;"><td style="height:169.734px;"><span style="color:rgb(186,55,42);">`<span class="pre">Doc_ModifiedStatus</span>`</span>

</td><td style="height:169.734px;">```json
Modified: true | false
```

</td><td style="height:169.734px;">更新文件的修改狀態。如果文件自上次儲存後被修改，則 `Modified `將為 **true**，否則，如果文件已儲存，則 `Modified `為 **false**。

請注意，這個通知可能會在不更改先前值的情況下觸發，因此必須檢查 `Modified `的值，不要假設這個通知暗示文件的修改狀態。

</td></tr></tbody></table>

</div></section></section><section id="bkmrk-calling-python-scrip-0"><span id="bkmrk--0"></span>### **呼叫 Python 巨集**

<section id="bkmrk-wopi-host-to-editor--1">##### WOPI 主機 🡆 編輯器

<div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-width:1px;border-style:solid;"><colgroup><col style="width:20.2719%;"></col><col style="width:31.0212%;"></col><col style="width:48.5832%;"></col></colgroup><thead><tr class="row-odd" style="height:35.3906px;"><th class="head" style="height:35.3906px;border-width:1px;">MessageId

</th><th class="head" style="height:35.3906px;border-width:1px;">Values

</th><th class="head" style="height:35.3906px;border-width:1px;">說明

</th></tr></thead><tbody><tr class="row-even" style="height:87.219px;"><td style="height:87.219px;border-width:1px;"><span style="color:rgb(186,55,42);">`<span class="pre">CallPythonScript</span>`</span>

</td><td style="height:87.219px;border-width:1px;">```
ScriptFile: <string>
Function: <string>
Values: <object>
```

</td><td style="height:87.219px;border-width:1px;">執行指定功能的 Python 巨集：

- ScriptFile：巨集檔名
- Function：函數名稱
- Values：參數內容（key: value, ...）

</td></tr></tbody></table>

</div></section><section id="bkmrk-editor-to-wopi-host%EF%83%81-1">##### 編輯器 🡆 WOPI 主機

<div class="wy-table-responsive"><table style="border-collapse:collapse;width:100%;border-style:solid;"><colgroup><col style="width:27.9357%;"></col><col style="width:23.739%;"></col><col style="width:48.3253%;"></col></colgroup><thead><tr class="row-odd"><th class="head">MessageId

</th><th class="head">Values

</th><th class="head">說明

</th></tr></thead><tbody><tr class="row-even"><td><span style="color:rgb(186,55,42);">`<span class="pre">CallPythonScript-Result</span>`</span>

</td><td>```
commandName: <string>
Values: <object>
```

</td><td>傳回 Python 執行結果。`commandName `放的是巨集的 URL。</td></tr></tbody></table>

</div></section></section></section>