ONLYOFFICE’s diary

OSSドキュメントエディタの使い方やヒントをご紹介します

ONLYOFFICEとPythonアプリを連携させる方法

この記事では、ONLYOFFICEをPythonアプリに統合する方法をご紹介します。

ONLYOFFICEは、あらゆるプログラミング言語で書かれたウェブアプリにドキュメント、スプレッドシート、プレゼンテーションの編集機能をもたらすことができる、強力なオープンソースサービスです。この記事では、ONLYOFFICEをPythonアプリに統合する方法をご紹介します。

そのために、Python上でシンプルな文書管理システムを作り、そこにONLYOFFICE文書エディターを統合しましょう。これは思うより簡単なことです。

https://dz2cdn1.dzone.com/storage/temp/11502861-onlyoffice-integration-tutorial.jpeg

Pythonで作るDMS

このパートでは、ONLYOFFICEとの統合をその例で示すために、Pythonアプリのコードを書きます。エディターを統合しようと思っているアプリには、閲覧/編集のために開く必要のあるファイルのリストがある可能性が高いです。そこで、この機能を持つアプリを作ればいいのですね。また、このアプリでは、ファイルのダウンロードもできるようにする必要があります。

アプリには、Bottleフレームワークを使用します。pip install bottleコマンドで作業ディレクトリにインストールします。次に、main.py(アプリのコード)とindex.tpl(テンプレート)を作成し、main.pyファイルに以下のコードを追加する必要があります。

from bottle import route, run, template, get, static_file # connecting the framework and the necessary components
@route('/') # setting up routing for requests for /
def index():
    return template('index.tpl')  # showing template in response to request


run(host="localhost", port=8080)  # running the application on port 8080

アプリケーションを起動すると、http://localhost:8080 に空のページが表示されます。ドキュメントサーバーは、ゼロから新しいドキュメントを作成できないので、デフォルトのファイルを追加して、その名前のリストをテンプレートに形成する必要があります。そこで、filesというフォルダを作成し、そこに3つのファイル(docx, xlsx, pptx)を置きましょう。

listdirコンポーネントを使用して、それらの名前を読み取ります。

from os import listdir

では、filesというフォルダにあるすべてのファイル名を格納する変数を作成しましょう。

sample_files = [f for f in listdir('files')]

この変数をテンプレートで使用するには、templateメソッドに渡す必要があります。

def index():
    return template('index.tpl', sample_files=sample_files)
この変数をテンプレートに表示させてみましょう。
%for file in sample_files:
    <div>
        <span>{{file}}</span>
    </div>
% end

アプリケーションを再起動すると、ページ上にファイル名の一覧が表示されます。さて、これらのファイルをすべてのアプリ利用者が利用できるようにする必要があります。

ここでは、そのための新しいメソッドをご紹介します。

@get("/files/<filepath:re:.*\.*>")
def show_sample_files(filepath):
    return static_file(filepath, root="files")

Pythonアプリでドキュメントを表示する

ONLYOFFICEエディターを使ってDocument Serverをインストールします。インストール方法はたくさんありますが、Dockerを使用することをお勧めします。

docker run -itd -p 80:80 onlyoffice/documentserver-de

テンプレート内のドキュメントエディターAPIを接続します。

<script type="text/javascript" src="editor_url/web-apps/apps/api/documents/api.js"></script>

editor_url は、ドキュメントエディタへのリンクです。

各ファイルを開いて閲覧するためのボタンです。

<button onclick="view('files/{{file}}')">view</button>

ここで、idを持つdivを追加する必要があります。

<div id="editor"></div>

このdivの中でドキュメントエディタが開かれます。しかし、エディタを開く関数を呼び出した後でなければなりません。

<script>
function view(filename) {
    if (/docx$/.exec(filename)) {
        filetype = "text"
    }
    if (/xlsx$/.exec(filename)) {
        filetype = "spreadsheet"
    }
    if (/pptx$/.exec(filename)) {
        filetype = "presentation",
        title: filename
    }

    new DocsAPI.DocEditor("editor",
        {
            documentType: filetype,
            document: {
                url: "host_url" + '/' + filename,
                title: filename
            },
            editorConfig: {mode: 'view'}
        });
  }
</script>

DocEditor関数の引数は2つで、エディターが開かれる要素のidと、エディターの設定値を含むJSONです。

すべてのパラメータは、公式のAPIドキュメントに記載されています。この例では、必須パラメータであるdocumentTypedocument.urleditorConfig.modeを使用しています。titleも追加しましょう。これはエディタに表示されるファイル名です。

ドキュメントの種類(documentType)は、そのフォーマット(テキストはdocx、スプレッドシートはxlsx、プレゼンテーションはpptx)によって識別されます。

document.urlに注意してください。これはこれから開こうとしているファイルへのリンクです。

これで、Pythonアプリでドキュメントを表示するためのすべてが揃いました。

ファイルの編集

「編集」ボタンを追加してみましょう。

<button onclick="edit('files/{{file}}')">edit</button>

次に、ファイルを開いて編集するための関数を新たに作成する必要があります。これはView関数に似ているので、共通部分を別の関数にしましょう。

これで3つの関数ができました。

<script>
    var editor;
    function view(filename) {
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor("editor",
            {
                documentType: get_file_type(filename),
                document: {
                    url: "host_url" + '/' + filename,
                    title: filename
                },
                editorConfig: {mode: 'view'}
            });
    }

    function edit(filename) {
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor("editor",
            {
                documentType: get_file_type(filename),
                document: {
                    url: "host_url" + '/' + filename,
                    title: filename
                }
            });
    }

    function get_file_type(filename) {
        if (/docx$/.exec(filename)) {
            return "text"
        }
        if (/xlsx$/.exec(filename)) {
            return "spreadsheet"
        }
        if (/pptx$/.exec(filename)) {
            return "presentation"
        }
    }
</script>

destroyEditorは、エディタが開かれていた場合、それを閉じます。

デフォルトでは、editorConfigパラメータは、値{"mode": "edit"}という値を持っています。そのため、edit()関数にはこのパラメータが存在しません。

これで、ファイルが編集用に開かれます。

ドキュメントの共同編集

共同編集は、編集者の設定で同じ文書に同じdocument.keyを使用することで実装されます。このキーがない場合、編集者はファイルを開くたびに編集セッションを作成します。

共同編集のために同じ編集セッションに接続させるためには、それぞれのdocにユニークなキーを設定する必要があります。filename + "_key"の形式でキーを使用することにしましょう。これを、documentが存在するすべてのconfigに追加する必要があります。

 document: {
                    url: "host_url" + '/' + filepath,
                    title: filename,
                    key: filename + '_key'
                },

ファイルの保存

ONLYOFFICEは通常、あなたが作業中にドキュメントに加えたすべての変更を保存します。エディタを閉じた後、Document Serverは保存するファイルのバージョンをビルドし、callbackUrlアドレスにリクエストを送信します。このリクエストには、document.keyと、構築されたばかりのファイルへのリンクが含まれます。

実稼働環境では、document.keyを使用して古いバージョンのファイルを見つけ、新しいものに置き換えます。今回のケースでは、データベースがないので、callbackUrlを使ってファイル名を送信するだけです。

editorConfig.callbackUrlの設定にcallbackUrlを指定します。このパラメータを追加すると、edit()メソッドは次のようになります。

function edit(filename) {
        const filepath = 'files/' + filename;
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor("editor",
            {
                documentType: get_file_type(filepath),
                document: {
                    url: "host_url" + '/' + filepath,
                    title: filename, 
                    key: filename + '_key'
                }
                ,
                editorConfig: {
                    mode: 'edit',
                    callbackUrl: "host_url" + '/callback' + '&filename=' + filename  // add file name as a request parameter
                }
            });
    }

次に、/callbackアドレスにPOSTリクエストを取得した後、ファイルを保存するメソッドを記述する必要があります。

@post("/callback") # processing post requests for /callback
def callback():
    if request.json['status'] == 2:
        file = requests.get(request.json['url']).content
        with open('files/' + request.query['filename'], 'wb') as f:
            f.write(file)
    return "{\"error\":0}"

#status 2はビルドされたファイルです。すべてのステータスに関するより詳細な情報は、APIドキュメントに記載されています。

これで、エディタを閉じた後、新しいバージョンのファイルがストレージに保存されるようになりました。

ユーザーの管理

アプリ内にユーザーがいる場合、エディター設定にそのユーザーの識別子(idとname)を記述します。こうすることで、誰がドキュメントを編集しているのかを確認することができます。

例として、インターフェイスでユーザーを選択する機能を追加してみましょう。

<select id="user_selector" onchange="pick_user()">
    <option value="1" selected="selected">JD</option>
    <option value="2">Turk</option>
    <option value="3">Elliot</option>
    <option value="4">Carla</option>
</select>

<script>タグの最初にpick_user()という関数の呼び出しを追加してみましょう。この関数の中で、idとユーザー名を表す変数を初期化します。

function pick_user() {
        const user_selector = document.getElementById("user_selector");
        this.current_user_name = user_selector.options[user_selector.selectedIndex].text;
        this.current_user_id = user_selector.options[user_selector.selectedIndex].value;
    }

次に、editorConfig.user.ideditorConfig.user.nameを使用して、エディタの設定にユーザの設定を追加する必要があります。これらのパラメータを、ファイル編集機能のエディタ設定に追加してみましょう。

function edit(filename) {
        const filepath = 'files/' + filename;
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor("editor",
            {
                documentType: get_file_type(filepath),
                document: {
                    url: "host_url" + '/' + filepath,
                    title: filename
                },
                editorConfig: {
                    mode: 'edit',
                    callbackUrl: "host_url" + '/callback' + '?filename=' + filename,
                    user: {
                        id: this.current_user_id,
                        name: this.current_user_name
                    }
                }
            });
    }

この簡単な例が、ONLYOFFICEを皆さんのPythonアプリに統合する一助となれば幸いです。より多くの統合例はGitHubでご覧ください。