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

ドキュメントデータの構築

アプリケーションで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()

これにより、srcdir から reStructuredText ソースが読み込まれ、必要なデータが builddir に配置されます。builddir には、2つのサブディレクトリが含まれます。1つは「data」という名前で、ドキュメントの表示、ドキュメントの検索、およびドキュメントへのコメントの追加に必要なすべてのデータが含まれます。もう1つのディレクトリは「static」と呼ばれ、「/static」から提供する必要のある静的ファイルが含まれます。

静的ファイルを「/static」以外のパスから提供する場合は、WebSupport オブジェクトを作成するときに、 *staticdir* キーワード引数を指定することで可能です。

SphinxドキュメントのWebアプリへの統合

データが構築されたので、それを使って何か便利なことをする時が来ました。まず、アプリケーション用の WebSupport オブジェクトを作成します。

from sphinxcontrib.websupport import WebSupport

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

作業するドキュメントのセットごとに、これらの1つだけが必要です。次に、その 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 %}

認証

投票などの特定の機能を使用するには、ユーザーを認証できる必要があります。認証の詳細は、アプリケーションに委ねられます。ユーザーが認証されると、ユーザーの詳細を、*username* および *moderator* キーワード引数を使用して、特定の WebSupport メソッドに渡すことができます。Webサポートパッケージは、コメントと投票とともにユーザー名を保存します。唯一の注意点は、ユーザーがユーザー名を変更できるようにする場合は、ウェブサポートパッケージのデータを更新する必要があることです。

support.update_username(old_username, new_username)

username はユーザーを識別する一意の文字列である必要があり、moderator はユーザーがモデレーション権限を持っているかどうかを表すブール値である必要があります。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 がリクエストパスであるということです。これにより、単一のビューから正しいドキュメントに簡単にアクセスできます。ユーザーが認証されている場合は、ユーザー名とモデレーションステータスが docname とともに get_document() に渡されます。Webサポートパッケージは、このデータをテンプレートで使用される COMMENT_OPTIONS に追加します。

これは、ドキュメントがドキュメントルートから提供される場合にのみ機能します。別のディレクトリから提供される場合は、URLルートにそのディレクトリをプレフィックスとして付加し、Webサポートオブジェクトを作成するときに docroot キーワード引数を指定する必要があります。

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

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

検索の実行

Sphinxサイドバーに組み込まれている検索フォームを使用するには、ドキュメントルートからの相対的なURL「search」へのリクエストを処理する関数を作成します。ユーザーの検索クエリは、キー q を持つGETパラメーターにあります。次に、get_search_results() メソッドを使用して検索結果を取得します。Flaskでは、次のようになります。

@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つの関数が必要です。最初の関数は新しいコメントを追加するために使用され、Webサポートメソッド 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)

parent_idnode_id の両方がリクエストとともに送信されることに気づくでしょう。コメントがノードに直接添付されている場合、parent_id は空になります。コメントが別のコメントの子である場合、node_id は空になります。次の関数は、特定のノードのコメントの取得を処理し、適切に 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"

コメントのモデレーション

デフォルトでは、add_comment() を介して追加されたすべてのコメントは自動的に表示されます。何らかの形式のモデレーションが必要な場合は、displayed キーワード引数を渡すことができます。

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'

コメントの却下は、コメントの削除によって行われます。

新しいコメントが追加されても表示されないときにカスタムアクション(モデレーターへのメール送信など)を実行するには、サポートオブジェクトをインスタンス化するときに、WebSupport クラスに呼び出し可能オブジェクトを渡すことができます。

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

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

モデレーションコールバックは、WebSupport.add_comment() によって返されるのと同じコメント辞書である1つの引数を取る必要があります。