Webサポートクイックスタート

ドキュメントデータのビルド

アプリケーションの中でウェブサポートパッケージを使用する場合は、まずはデータを作る必要があります。データには、pickle化されたドキュメントや検索インデックス、コメントなどがどのドキュメントに付加されたのかを追跡するためのノードデータが含まれます。これを行うためには、 WebSupport クラスのインスタンスを作り、 build() メソッドを呼ぶ必要があります:

from sphinxcontrib.websupport import WebSupport

support = WebSupport(srcdir='/path/to/rst/sources/',
                     builddir='/path/to/build/outdir',
                     search='xapian')

support.build()

This will read reStructuredText sources from srcdir and place the necessary data in builddir. The builddir will contain two sub-directories: one named "data" that contains all the data needed to display documents, search through documents, and add comments to documents. The other directory will be called "static" and contains static files that should be served from "/static".

注釈

もし "/static" 以外のパスから静的ファイルの配信をしたい場合には、 WebSupport オブジェクトを作る時に、 staticdir キーワード引数を指定してください。

Sphinxドキュメントをウェブアプリケーションに統合

データができましたので、次はこれを使う番です。アプリケーションのための WebSupport オブジェクトを作るところから始めます:

from sphinxcontrib.websupport import WebSupport

support = WebSupport(datadir='/path/to/the/data',
                     search='xapian')

次に、個々のドキュメントに対する処理を作っていきます。 get_document() メソッドを呼ぶと、個々のドキュメントにアクセスできます:

contents = support.get_document('contents')

このメソッドは、次のキーを持つ辞書を返します:

  • body: HTML形式のドキュメント本体です。

  • sidebar: HTML形式のサイドバーです。

  • relbar: このdivは、関連ドキュメントへのリンクを含んでいます。

  • title: ドキュメントのタイトルです。

  • css: Sphinxが使用するCSSファイルへのリンクです。

  • script: JavaScriptはコメントオプションを含みます。

この辞書はテンプレートのコンテキストとして利用できます。これを利用することであなたの既存のテンプレートシステムに簡単に統合出来ます。 Jinja2 を使った例は以下の通りです:

{%- extends "layout.html" %}

{%- block title %}
    {{ document.title }}
{%- endblock %}

{% block css %}
    {{ super() }}
    {{ document.css|safe }}
    <link rel="stylesheet" href="/static/websupport-custom.css" type="text/css">
{% endblock %}

{%- block script %}
    {{ super() }}
    {{ document.script|safe }}
{%- endblock %}

{%- block relbar %}
    {{ document.relbar|safe }}
{%- endblock %}

{%- block body %}
    {{ document.body|safe }}
{%- endblock %}

{%- block sidebar %}
    {{ document.sidebar|safe }}
{%- endblock %}

認証

投票のような機能を実装する場合、ユーザ認証ができる必要があります。認証をどのように実装するかはアプリケーションに任されています。ユーザが認証されたら、ユーザの情報を WebSupport のメソッドの usernamemoderator キーワード引数に渡すことができます。ウェブサポートパッケージはユーザ名を、コメントや投票と一緒に保存します。注意点を1つあげるとすれば、もしユーザに対して名前の変更を行えるようにするのであれば、ウェブサポートパッケージの内部のユーザ名のデータも更新する必要があります:

support.update_username(old_username, new_username)

username はユーザを識別できる一意な文字列であるべきで、 moderator はboolean型のユーザが適切な権限を持つかどうかを表すべきです。 moderator のデフォルト値は False です。

例えば、 Flask を使用して、ユーザがログインしているかどうかを確認し、ドキュメントを読めるようにするには、次のようなコードで行えます:

from sphinxcontrib.websupport.errors import *

@app.route('/<path:docname>')
def doc(docname):
    username = g.user.name if g.user else ''
    moderator = g.user.moderator if g.user else False
    try:
        document = support.get_document(docname, username, moderator)
    except DocumentNotFoundError:
        abort(404)
    return render_template('doc.html', document=document)

まず、 docname が要求されたパスを表すことに気づきます。これにより、正しいドキュメントへのアクセスが行えます。もしユーザの認証が行われていたら、ユーザ名とモデレート権限情報が get_document() に渡されます。ウェブサポートパッケージは、テンプレートの中で使用される COMMENT_OPTIONS をこのデータに付加します。

注釈

このプログラムはドキュメントがルートで提供される場合にのみ動作します。もし、他のディレクトリからドキュメントを提供したい場合には、URLのプリフィックスを指定する必要があります。これは、ウェブサポートオブジェクトを作成する時に、 docroot キーワード引数として与えます:

support = WebSupport(..., docroot='docs')

@app.route('/docs/<path:docname>')

検索の実行

To use the search form built-in to the Sphinx sidebar, create a function to handle requests to the URL 'search' relative to the documentation root. The user's search query will be in the GET parameters, with the key q. Then use the get_search_results() method to retrieve search results. In Flask that would be like this:

@app.route('/search')
def search():
    q = request.args.get('q')
    document = support.get_search_results(q)
    return render_template('doc.html', document=document)

ドキュメントと検索結果の表示には、同じテンプレートを使用しています。これは、 get_search_results() メソッドが、 get_document() と同じ形式のコンテキスト辞書を返すからです。

コメント&提案

それでは、コメントなどをAJAXで処理するための関数を定義します。3つの関数を定義する必要があります。1つ目は、新しいコメントが投稿されたときに、ウェブサポートオブジェクトの add_comment() メソッドを呼び出すものです:

@app.route('/docs/add_comment', methods=['POST'])
def add_comment():
    parent_id = request.form.get('parent', '')
    node_id = request.form.get('node', '')
    text = request.form.get('text', '')
    proposal = request.form.get('proposal', '')
    username = g.user.name if g.user is not None else 'Anonymous'
    comment = support.add_comment(text, node_id='node_id',
                                  parent_id='parent_id',
                                  username=username, proposal=proposal)
    return jsonify(comment=comment)

You'll notice that both a parent_id and node_id are sent with the request. If the comment is being attached directly to a node, parent_id will be empty. If the comment is a child of another comment, then node_id will be empty. Then next function handles the retrieval of comments for a specific node, and is aptly named get_data():

@app.route('/docs/get_comments')
def get_comments():
    username = g.user.name if g.user else None
    moderator = g.user.moderator if g.user else False
    node_id = request.args.get('node', '')
    data = support.get_data(node_id, username, moderator)
    return jsonify(**data)

最後の関数は、 process_vote() を呼び出して、コメントに対するユーザの投票を取り扱う関数です:

@app.route('/docs/process_vote', methods=['POST'])
def process_vote():
    if g.user is None:
        abort(401)
    comment_id = request.form.get('comment_id')
    value = request.form.get('value')
    if value is None or comment_id is None:
        abort(400)
    support.process_vote(comment_id, g.user.id, value)
    return "success"

コメントのモデレート

By default, all comments added through add_comment() are automatically displayed. If you wish to have some form of moderation, you can pass the displayed keyword argument:

comment = support.add_comment(text, node_id='node_id',
                              parent_id='parent_id',
                              username=username, proposal=proposal,
                              displayed=False)

コメントのモデレートを取り扱うビューを追加する必要があります。モデレータがコメントを受け入れて、表示するかどうかを決定するときに、この関数が呼び出されます:

@app.route('/docs/accept_comment', methods=['POST'])
def accept_comment():
    moderator = g.user.moderator if g.user else False
    comment_id = request.form.get('id')
    support.accept_comment(comment_id, moderator=moderator)
    return 'OK'

リジェクトされると、コメントは削除されます。

非表示の新しいコメントが追加されたときに、Eメールによるモデレートなど、カスタムのアクションを行いたい場合には、 WebSupport のインスタンスを作る時に、呼び出し可能なオブジェクトを渡します:

def moderation_callback(comment):
    """Do something..."""

support = WebSupport(..., moderation_callback=moderation_callback)

このコールバック関数は、 add_comment() が返すのと同じ形式の辞書を引数として受け取ります。