sphinx/doc/web/quickstart.rst

267 lines
9.9 KiB
ReStructuredText
Raw Normal View History

2010-06-04 16:13:53 -05:00
.. _websupportquickstart:
Web Support Quick Start
=======================
Building Documentation Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010-06-26 14:49:30 -05:00
2010-07-12 12:15:01 -05:00
To make use of the web support package in your application you'll
2010-08-09 14:22:31 -05:00
need to build the data it uses. This data includes pickle files representing
documents, search indices, and node data that is used to track where
2010-07-12 12:15:01 -05:00
comments and other things are in a document. To do this you will need
2010-08-10 10:11:11 -05:00
to create an instance of the :class:`~sphinx.websupport.WebSupport`
class and call it's :meth:`~sphinx.websupport.WebSupport.build` method::
2010-06-04 16:13:53 -05:00
from sphinx.websupport import WebSupport
2010-06-04 16:13:53 -05:00
support = WebSupport(srcdir='/path/to/rst/sources/',
2010-08-09 14:22:31 -05:00
builddir='/path/to/build/outdir',
2010-08-10 10:11:11 -05:00
search='xapian')
2010-06-04 16:13:53 -05:00
support.build()
2010-06-26 14:49:30 -05:00
This will read reStructuredText sources from `srcdir` and place the
2010-08-10 10:11:11 -05:00
necessary data in `builddir`. The `builddir` will contain two
2010-08-09 14:22:31 -05:00
sub-directories. One named "data" that contains all the data needed
to display documents, search through documents, and add comments to
2010-08-10 10:11:11 -05:00
documents. The other directory will be called "static" and contains static
2010-08-09 14:22:31 -05:00
files that should be served from "/static".
2010-08-07 17:04:45 -05:00
.. note::
If you wish to serve static files from a path other than "/static", you
can do so by providing the *staticdir* keyword argument when creating
the :class:`~sphinx.websupport.api.WebSupport` object.
2010-06-04 16:13:53 -05:00
Integrating Sphinx Documents Into Your Webapp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010-06-04 16:13:53 -05:00
2010-08-09 14:22:31 -05:00
Now that the data is built, it's time to do something useful with it.
Start off by creating a :class:`~sphinx.websupport.WebSupport` object
for your application::
2010-08-10 10:11:11 -05:00
from sphinx.websupport import WebSupport
2010-06-04 16:13:53 -05:00
support = WebSupport(datadir='/path/to/the/data',
search='xapian')
2010-06-26 14:49:30 -05:00
You'll only need one of these for each set of documentation you will be
2010-08-10 10:11:11 -05:00
working with. You can then call it's
:meth:`~sphinx.websupport.WebSupport.get_document` method to access
2010-07-12 12:15:01 -05:00
individual documents::
2010-06-26 14:49:30 -05:00
contents = support.get_document('contents')
2010-06-04 16:13:53 -05:00
This will return a dictionary containing the following items:
2010-06-04 16:13:53 -05:00
* **body**: The main body of the document as HTML
2010-08-10 10:11:11 -05:00
* **sidebar**: The sidebar of the document as HTML
* **relbar**: A div containing links to related documents
* **title**: The title of the document
2010-08-09 14:22:31 -05:00
* **css**: Links to css files used by Sphinx
* **js**: Javascript containing comment options
2010-06-16 08:33:33 -05:00
This dict can then be used as context for templates. The goal is to be
2010-08-10 10:11:11 -05:00
easy to integrate with your existing templating system. An example using
`Jinja2 <http://jinja.pocoo.org/2/>`_ is:
2010-06-26 14:49:30 -05:00
.. sourcecode:: html+jinja
2010-06-26 14:49:30 -05:00
{%- extends "layout.html" %}
2010-06-26 14:49:30 -05:00
{%- block title %}
2010-08-10 10:11:11 -05:00
{{ document.title }}
{%- endblock %}
2010-06-26 14:49:30 -05:00
2010-08-09 14:22:31 -05:00
{% block css %}
2010-08-10 10:11:11 -05:00
{{ super() }}
{{ document.css|safe }}
<link rel="stylesheet" href="/static/websupport-custom.css" type="text/css">
2010-08-09 14:22:31 -05:00
{% endblock %}
2010-08-07 17:04:45 -05:00
{%- block js %}
2010-08-10 10:11:11 -05:00
{{ super() }}
{{ document.js|safe }}
2010-08-07 17:04:45 -05:00
{%- endblock %}
{%- block relbar %}
2010-08-10 10:11:11 -05:00
{{ document.relbar|safe }}
{%- endblock %}
2010-06-26 14:49:30 -05:00
{%- block body %}
2010-08-10 10:11:11 -05:00
{{ document.body|safe }}
{%- endblock %}
2010-06-04 16:13:53 -05:00
{%- block sidebar %}
2010-08-10 10:11:11 -05:00
{{ document.sidebar|safe }}
{%- endblock %}
2010-06-04 16:13:53 -05:00
2010-08-07 17:04:45 -05:00
Authentication
--------------
To use certain features such as voting it must be possible to authenticate
2010-08-09 14:22:31 -05:00
users. The details of the authentication are left to your application.
2010-08-07 17:04:45 -05:00
Once a user has been authenticated you can pass the user's details to certain
:class:`~sphinx.websupport.WebSupport` methods using the *username* and
*moderator* keyword arguments. The web support package will store the
username with comments and votes. The only caveat is that if you allow users
2010-08-09 14:22:31 -05:00
to change their username you must update the websupport package's data::
2010-08-07 17:04:45 -05:00
2010-08-10 10:11:11 -05:00
support.update_username(old_username, new_username)
2010-08-07 17:04:45 -05:00
*username* should be a unique string which identifies a user, and *moderator*
should be a boolean representing whether the user has moderation
privilieges. The default value for *moderator* is *False*.
An example `Flask <http://flask.pocoo.org/>`_ function that checks whether
2010-08-09 14:22:31 -05:00
a user is logged in and then retrieves a document is::
from sphinx.websupport.errors import *
2010-06-04 16:13:53 -05:00
@app.route('/<path:docname>')
2010-06-04 16:13:53 -05:00
def doc(docname):
2010-08-10 10:11:11 -05:00
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)
2010-06-04 16:13:53 -05:00
return render_template('doc.html', document=document)
2010-08-07 17:04:45 -05:00
The first thing to notice is that the *docname* is just the request path.
2010-08-09 14:22:31 -05:00
This makes accessing the correct document easy from a single view.
2010-08-07 17:04:45 -05:00
If the user is authenticated then the username and moderation status are
2010-08-10 10:11:11 -05:00
passed along with the docname to
2010-08-07 17:04:45 -05:00
:meth:`~sphinx.websupport.WebSupport.get_document`. The web support package
will then add this data to the COMMENT_OPTIONS that are used in the template.
2010-06-26 14:49:30 -05:00
.. note::
2010-06-04 16:13:53 -05:00
2010-08-10 10:11:11 -05:00
This only works works if your documentation is served from your
document root. If it is served from another directory, you will
need to prefix the url route with that directory, and give the `docroot`
keyword argument when creating the web support object::
2010-08-14 14:48:27 -05:00
support = WebSupport(..., docroot='docs')
2010-08-10 10:11:11 -05:00
@app.route('/docs/<path:docname>')
2010-06-26 14:49:30 -05:00
Performing Searches
~~~~~~~~~~~~~~~~~~~
2010-06-16 08:33:33 -05:00
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 :meth:`~sphinx.websupport.WebSupport.get_search_results` method
2010-08-10 10:11:11 -05:00
to retrieve search results. In `Flask <http://flask.pocoo.org/>`_ 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)
Note that we used the same template to render our search results as we
2010-08-10 10:11:11 -05:00
did to render our documents. That's because
:meth:`~sphinx.websupport.WebSupport.get_search_results` returns a context
2010-07-12 12:15:01 -05:00
dict in the same format that
:meth:`~sphinx.websupport.WebSupport.get_document` does.
2010-08-09 14:22:31 -05:00
Comments & Proposals
~~~~~~~~~~~~~~~~~~~~
2010-07-12 12:15:01 -05:00
Now that this is done it's time to define the functions that handle
the AJAX calls from the script. You will need three functions. The first
function is used to add a new comment, and will call the web support method
:meth:`~sphinx.websupport.WebSupport.add_comment`::
@app.route('/docs/add_comment', methods=['POST'])
def add_comment():
parent_id = request.form.get('parent', '')
2010-08-09 14:22:31 -05:00
node_id = request.form.get('node', '')
2010-07-12 12:15:01 -05:00
text = request.form.get('text', '')
2010-08-09 14:22:31 -05:00
proposal = request.form.get('proposal', '')
2010-07-12 12:15:01 -05:00
username = g.user.name if g.user is not None else 'Anonymous'
2010-08-09 14:22:31 -05:00
comment = support.add_comment(text, node_id='node_id',
2010-08-10 10:11:11 -05:00
parent_id='parent_id',
username=username, proposal=proposal)
2010-07-12 12:15:01 -05:00
return jsonify(comment=comment)
2010-08-09 14:22:31 -05:00
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`
2010-08-10 10:11:11 -05:00
will be empty. Then next function handles the retrieval of comments for a
specific node, and is aptly named
2010-08-09 14:22:31 -05:00
:meth:`~sphinx.websupport.WebSupport.get_data`::
2010-07-12 12:15:01 -05:00
@app.route('/docs/get_comments')
def get_comments():
2010-08-09 14:22:31 -05:00
username = g.user.name if g.user else None
2010-08-10 10:11:11 -05:00
moderator = g.user.moderator if g.user else False
node_id = request.args.get('node', '')
2010-08-09 14:22:31 -05:00
data = support.get_data(parent_id, user_id)
return jsonify(**data)
2010-07-12 12:15:01 -05:00
The final function that is needed will call
:meth:`~sphinx.websupport.WebSupport.process_vote`, and will handle user
votes on comments::
@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"
2010-08-09 14:22:31 -05:00
Comment Moderation
~~~~~~~~~~~~~~~~~~
2010-08-10 10:11:11 -05:00
By default all comments added through
2010-08-09 14:22:31 -05:00
:meth:`~sphinx.websupport.WebSupport.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',
2010-08-10 10:11:11 -05:00
parent_id='parent_id',
2010-08-09 14:22:31 -05:00
username=username, proposal=proposal,
2010-08-10 10:11:11 -05:00
displayed=False)
2010-08-09 14:22:31 -05:00
You can then create two new views to handle the moderation of comments. The
first will be called when a moderator decides a comment should be accepted
and displayed::
@app.route('/docs/accept_comment', methods=['POST'])
def accept_comment():
moderator = g.user.moderator if g.user else False
2010-08-10 10:11:11 -05:00
comment_id = request.form.get('id')
support.accept_comment(comment_id, moderator=moderator)
return 'OK'
2010-08-09 14:22:31 -05:00
The next is very similar, but used when rejecting a comment::
@app.route('/docs/reject_comment', methods=['POST'])
def reject_comment():
moderator = g.user.moderator if g.user else False
2010-08-10 10:11:11 -05:00
comment_id = request.form.get('id')
support.reject_comment(comment_id, moderator=moderator)
return 'OK'
2010-08-09 14:22:31 -05:00
To perform a custom action (such as emailing a moderator) when a new comment
is added but not displayed, you can pass callable to the
:class:`~sphinx.websupport.WebSupport` class when instantiating your support
object::
def moderation_callback(comment):
2010-08-14 14:48:27 -05:00
"""Do something..."""
2010-08-09 14:22:31 -05:00
2010-08-14 14:48:27 -05:00
support = WebSupport(..., moderation_callback=moderation_callback)
2010-07-12 12:15:01 -05:00
2010-08-09 14:22:31 -05:00
The moderation callback must take one argument, which will be the same
2010-08-10 10:11:11 -05:00
comment dict that is returned by add_comment.