この記事では、ONLYOFFICEをPythonアプリに統合する方法をご紹介します。
ONLYOFFICEは、あらゆるプログラミング言語で書かれたウェブアプリにドキュメント、スプレッドシート、プレゼンテーションの編集機能をもたらすことができる、強力なオープンソースサービスです。この記事では、ONLYOFFICEをPythonアプリに統合する方法をご紹介します。
そのために、Python上でシンプルな文書管理システムを作り、そこにONLYOFFICE文書エディターを統合しましょう。これは思うより簡単なことです。
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ドキュメントに記載されています。この例では、必須パラメータであるdocumentType
、document.url
、editorConfig.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.id
とeditorConfig.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でご覧ください。