LoginSignup
5
4

More than 3 years have passed since last update.

Box UI ElementsのContent OpenWithでBox Editをつかってみた

Last updated at Posted at 2020-05-11

この記事のシリーズ:

Box UI ElementsのContent OpenWithでBox Editをつかってみた ← この記事
Box UI ElementsのContent OpenWithでG Suiteを開いてみた
Box UI ElementsのContent OpenWithでファイルの更新に反応してみた
Box UI ElementsのContent OpenWithでファイルの更新に反応してみた クライアントサイド編

コードは、Githubでも確認いただけます。

Box UI ElementsのContent OpenWithとは

Content OpenWithのまえに、そもそもBox UI Elementsとはなにかという話ですが、これはBox社が公式に提供しているJavascriptとCSSによるWeb用の部品です。自作のウェブサイトに組み込むことで、標準のBoxの様なUIを簡単に実現可能です。

Box UI Elementsには、現時点で以下の6個のコンポーネントが用意されています。

  • Content Explorer

  • Content OpenWith <= 今回利用

  • Content Picker

  • Content Preview

  • Content Sidebar

  • Content Uploader

この中で、Content OpenWithは、一番ややこしかったので、使いかたを備忘録として残します。

Content OpenWithは、カスタムアプリケーション上にボタンを配置し、BoxEdit、Adobe Sign、Google Sheetなどの外部アプリケーションをコールすることができるコンポーネントです。
よくある使いかたとして、Box Editと連携して、ローカルでエディタを開き、保存時にBoxにデータを書き戻すことができるようになります。

このコンポーネントを利用すると、カスタムアプリケーションでもBoxからエディタを開くような滑らかな連携が可能になります。

Box UI Elements自体は、Reactで作られています。Reactを利用している場合、npm経由でライブラリをインストールして使うことが可能です。React以外の場合はCDN経由で提供されているjsファイルを利用できます。

基本的な部分は、ここに書いてあることをなぞっています。
https://ja.developer.box.com/guides/embed/ui-elements/

事前準備

このサンプルでは、以下の環境の利用を前提としています。実際にやってみたい人は予め準備が必要です。

  • Boxのアカウント(管理者権限が利用可能なBoxテナント)
  • Box Toolsのインストール(ローカルでエディタを開きBoxに同期するために必要。Box Toolsは、Box EditとDevice Trustが一緒になったものです。)
  • Herokuのアカウント
  • Heroku CLIのインストール
  • Gitのインストール
  • Nodeおよびパッケージマネージャ(npm or yarn)のインストール

Box環境の準備

Boxの開発者コンソールにアクセスし、「アプリの新規作成」を押します

開発者コンソールへのアクセスが初めての場合は、https://ja.developer.box.com/にアクセスし、マイアプリボタンをクリックすることで、アクセスが可能となります。

アプリの種類はカスタムアプリを選択します。

今回は、認証方法に、「JWTを使用したOAuth 2.0 (サーバー認証)」を選択します。

アプリの名前に任意の文字を入れ、アプリの作成ボタンを押します。

マイアプリ > 構成を開き以下のように設定にします。
(多分こんな感じで大丈夫なはずです・・、アプリケーションアクセスやスコープがたらなければ変えてください)

スクリーンショット 2020-05-11 20.09.52.png

公開キーの追加と管理のセクションで、「公開/秘密キーペアを生成」を押します。

ダウンロードされるファイルは、あとでコードから利用するので、config.jsonとリネームしておきます。

CORSドメインは、公開するHerokuのURLを追加します。

Herokuでのアプリケーション作成が終わったら戻ってきて設定します。

コードの準備

以下の手順で、プロジェクトを用意します。

# 適当な名前のフォルダを作って、フォルダに入る
mkdir box-openwith-sample
cd box-openwith-sample

# Nodeプロジェクトとして初期化 (npmでも)
yarn init -y

# 必要なライブラリの追加 
yarn add box-node-sdk express ejs axios

# 必要なファイルを追加
touch app.js
touch index.ejs

# こちらは事前準備用のファイル(必須ではない)
touch setup.js

package.jsonに、startをスクリプトを追加します。(Herokuがstartを実行します)

package.json
{

  "scripts": {
    "start": "node app.js"
  },

}

Boxでアプリの設定をしたときにダウンロードしたconfig.jsonをプロジェクトフォルダ直下に配置します。

任意のエディタでsetup.jsを作成します。

このファイルは事前に(1)Appユーザーを作成し、(2)ファイルをアップロードし、(3)BoxEditの統合を使えるようにするという操作をするために作成し、実行します。これらの処理が事前にできればいいだけなので、ファイルとしてプロジェクトに追加する必要はありません。

実行前にSample.docxというダミーのMS Wordのファイルを用意しておきます。
config.jsonが同じフォルダにある前提となっています。

ダミーのファイルはとくにWordである必要も、このファイル名である必要も有りませんが、変更する場合コードも修正してください。

setup.js
const boxSDK = require("box-node-sdk");
const config = require("./config.json");
const axios = require("axios");
const fs = require("fs");

/**
 * app.jsで利用する、AppUserと、サンプルのWordファイルを予め登録する。
 * また、OpenWithを利用するために必要な、統合の設定も行う。
 */
const main = async () => {
  try {
    // 作成済みのJWT認証用のConfigファイルを読み込む
    const sdk = await boxSDK.getPreconfiguredInstance(config);
    // ServiceAccountのClientオブジェクトを作成
    const saClient = sdk.getAppAuthClient("enterprise");

    // app userを作成 (OpenWithはServiceAccountではつかえず、AppUserが必要なため)
    let appUser = await saClient.enterprise.addAppUser("Sample App User");

    // app userのホームフォルダに、サンプルとしてSample.docをアップロードする
    saClient.asUser(appUser.id);
    const stream = fs.createReadStream("./Sample.docx");
    const files = await saClient.files.uploadFile("0", "Sample.docx", stream);
    const file = files.entries[0];
    saClient.asSelf();

    // 念の為、現在利用可能なWebApp統合を一覧する
    // 13418が入っていないなら、設定がまちがっている
    const appIntegs = await saClient.get("/app_integrations");
    console.log("利用可能なWebApp統合一覧", appIntegs.body);
    /*
    {
      next_marker: null,
      entries: [
        { type: "app_integration", id: "10897" },
        { type: "app_integration", id: "1338" },
        { type: "app_integration", id: "13418" },  <= 13418がBox Editの統合
        { type: "app_integration", id: "3282" },
      ],
      limit: 100,
    };
    */

    // 作成したAppUserに、BoxEditのアプリ統合を利用できるようにする。
    // clientオブジェクトから何故かpostの実行(client.post)がうまく機能しなかったので、axiosで実行する
    // Authorizationにつけるアクセストークンは、ServiceAccountのものを利用する必要がある。
    const saTokenInfo = await sdk.getEnterpriseAppAuthTokens();
    const saAxios = axios.create({
      baseURL: "https://api.box.com/2.0",
      headers: {
        Authorization: `Bearer ${saTokenInfo.accessToken}`,
      },
    });

    // ここでは13418(BoxEdit)のみを登録するが、他のものも登録しておくと開くボタンから利用可能になる
    await saAxios.post("/app_integration_assignments", {
      assignee: {
        type: "user",
        id: appUser.id,
      },
      app_integration: {
        type: "app_integration",
        id: "13418",
      },
    });

    // 以下のAppUserとFileのIDをapp.jsで利用する
    console.log(
      "==================== 以下のIDをメモして、app.jsで利用する ===================="
    );
    console.log(`const USER_ID = "${appUser.id}"`);
    console.log(`const FILE_ID = "${file.id}"`);
  } catch (e) {
    console.error(e.toString());
  }
};

main();

config.jsonSample.docxが同じフォルダにあることを確認して、以下のコードを実行し、USER_IDと、FILE_IDをとって置きます。app.jsでは、この部分を書き換えてください。

【注意】config.jsonは、このサンプルでは簡易的にgitに入れていますが、本番環境ではファイルとしてgit等に入れるべきではなく、環境変数等に置くべきです。

任意のエディタでapp.jsを以下の内容で作ります。

app.js
const express = require("express");
const boxSDK = require("box-node-sdk");
const config = require("./config.json");

const app = express();

app.set("views", ".");
app.set("view engine", "ejs");

/**
 * setup.jsで作成したファイルとユーザーを書き換える
 */
const USER_ID = "";
const FILE_ID = "";

app.get("/", async (req, res) => {
  try {
    if (!USER_ID || !FILE_ID) {
      res.send("USER_IDとFILE_IDに値をいれてください。");
      return;
    }
    const sdk = await boxSDK.getPreconfiguredInstance(config);
    // AppUserの権限でClientオブジェクトを作成
    const auClient = await sdk.getAppAuthClient("user", USER_ID);

    // トークンをダウンスコープする
    // APIリファレンスには載っていないが、UI Elementsの説明には書いてあるAPI
    // ここでは、OpenWithで必要なものと、Previewで必要なものを両方スコープにいれてトークンをダウンスコープする
    const downToken = await auClient.exchangeToken(
      ["item_execute_integration", "item_readwrite", "item_preview"],
      `https://api.box.com/2.0/files/${FILE_ID}`
    );

    // テンプレート(index.ejs)にパラメータを渡して、HTMLを返す
    res.render("index", {
      fileId: FILE_ID,
      token: downToken.accessToken,
    });
  } catch (e) {
    console.error(e.toString());
  }
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`express started on port ${port}`);
});

ダウンスコープについて

Box UI Elementsは、Javascriptによりブラウザ内実行され、Box APIを直接実行します。このため、アクセストークンをブラウザに渡す必要があります。

これは、ブラウザ上の開発者用コンソールを利用すると、アクセストークンがエンドユーザーに見えてしまうということを意味しています。セキュリティ上問題があるので、アクセストークンのダウンスコープという手順が必要になります。ダウンスコープをすることで、指定したファイルIDに指定した操作だけを許すというアクセストークンに変換可能です。

ダウンスコープ前のアクセストークンをブラウザ側に渡すことは避けるべきでしょう。

任意のエディタでindex.ejsを以下の内容で作ります。

index.ejs

<!DOCTYPE html>
<html lang="en-US">
<head>
    <meta charset="utf-8" />
    <title>Sample</title>

    <link href="https://cdn01.boxcdn.net/platform/elements/11.0.2/ja-JP/openwith.css" rel="stylesheet" type="text/css"></link>
    <link href="https://cdn01.boxcdn.net/platform/preview/2.34.0/ja-JP/preview.css" rel="stylesheet" type="text/css"></link>

    <script src="https://cdn01.boxcdn.net/platform/elements/11.0.2/ja-JP/openwith.js"></script>
    <script src="https://cdn01.boxcdn.net/platform/preview/2.34.0/ja-JP/preview.js"></script>
</head>
<body>
    <div class="openwith" style=""></div>
    <div class="preview-container" style="height:800px; width:100%;"></div>

    <script>
        // app.jsから渡されたパラメータ
        const fileId = "<%= fileId %>"
        // ダウンスコープされたアクセストークン
        const token = "<%= token %>"

        const openWith = new Box.ContentOpenWith();
        openWith.show(fileId, token,  { container: ".openwith"})

        const preview = new Box.Preview();
        preview.show(fileId, token, { container: ".preview-container"});
    </script>
</body>
</html>

Herokuの準備

Content OpenWithはhttpsでしか動かないからherokuにデプロイしているだけです。

ローカルでhttpsで実行でも大丈夫です。

以下のコマンドを実行してHerokuにアプリを作成し、デプロイします。


# herokuにアプリを作成
heroku create

# gitを導入
git init
git add .
git commit -m "init"

# herokuにデプロイ
git push heroku master

Boxの設定変更

CORS設定

Box UI Elementsはブラウザ上でAPIを呼び出すので、CORSの設定が必要になります。
もう一度Boxの開発者コンソールに戻り、CORSの設定をします。
HerokuのURLを登録します。

アプリの承認

JWTアプリは管理者に承認されている必要があるので、以下の手順で承認します。
OAuth 2.0資格情報の、クライアントIDをコピー。
管理者コンソール > アプリ(左ナビ) > カスタムアプリ (上部タブ)に移動し、「新しいアプリケーションを承認」というボタンを押します。
クライアントIDを聞かれるので、コピーしたクライアントIDをペーストして承認します。

ローカルのBoxEditの設定

続いて、ローカルマシンでのBoxEditのセーフリストの設定します。

詳細はこちらをご覧ください。

Macの環境だと、OpenWith.shをダウンロードし、herokuのドメイン:xxx.heorkuapp.comをコマンドから追加するイメージです。

エンドユーザーにContent OpenWithを利用させたい場合は、この操作を1回だけユーザーにやってもらう必要があるので、この点が少しハードルが高いかも、という懸念はあります。

ちなみにこれをやらないとエラーになるとかではなく、OpenWithで描画される開くボタンがdisable状態になるだけです。

実行してみる

(URLがわかっているならわざわざ叩く必要ないですが)以下のコマンドで、ブラウザが立ち上がります。

heroku open

以下のように開くボタンとプレビューが表示され、開くボタンを押すとBoxEditが呼び出され、ローカルのエディタがたちあがります。

openwith.png

MS Wordで内容を変更し、保存ボタンを押した後、Web画面をリロードするとプレビューで変更されていることが確認できます。

感想・まとめ

ちょっとめんどくさいけど、作ると結構スムーズに動くのでカスタムアプリからBoxに連携する際に強力な道具になりそうです。
エンドユーザーのコンピュータにBoxToolsのインストールと、セーフリストの設定をする必要がある部分が少しつらそうです。

あと、OpenWithからBox Editを使う場合、以下が抜けていると動かないので注意。

  • ローカルにBoxToolsが入っている必要がある
  • ローカルでセーフリストにドメインを登録する必要がある
  • HTTPS上でのみOpenWithが動く
  • AppUserで動かす必要がある
  • 統合を事前にAppUserに紐付ける必要あり (これはServiceAccountのトークンでやらないとだめ)
  • BOXの開発者コンソールで適切なスコープとCORSの設定が必要

よくわからなかったこと

上記の一連の手順で基本的にOpenWithの実装ができましたが、以下の点についてはよくわからなかったので、記しておきます。いつか答えがみつかるといいな・・・。

  • MS Word(というか、どんなエディタでも)で保存した後に、標準のBoxであれば内容の変更を察知してRefreshボタンのポップアップが表示されるが、UI Elementではこの動作をしない。このため手動でリロードする必要があるが、ここはなんとかならないものか・・

【2020-05-16 追記】
@daichiiiiiii さんからいただいたコメントをもとに、続編で更新の検知を試しています。

5
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4