mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-09-03 20:52:55 -05:00
Lots of documentation and a few small API changes
This commit is contained in:
+21
-11
@@ -1,22 +1,32 @@
|
|||||||
.. _websupportapi:
|
.. _websupportapi:
|
||||||
|
|
||||||
Web Support API
|
.. currentmodule:: sphinx.websupport
|
||||||
===============
|
|
||||||
|
The WebSupport Class
|
||||||
|
====================
|
||||||
|
|
||||||
.. module:: sphinx.websupport.api
|
|
||||||
.. class:: WebSupport
|
.. class:: WebSupport
|
||||||
|
|
||||||
The :class:`WebSupport` class provides a central interface for
|
The main API class for the web support package. All interactions
|
||||||
working with Sphinx documentation.
|
with the web support package should occur through this class.
|
||||||
|
|
||||||
.. method:: init(srcdir='', outdir='')
|
:param srcdir: the directory containing the reStructuredText files
|
||||||
|
:param outdir: the directory in which to place the built data
|
||||||
|
:param search: the search system to use
|
||||||
|
:param comments: an instance of a CommentBackend
|
||||||
|
|
||||||
|
Methods
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
Initialize attributes.
|
.. automethod:: sphinx.websupport.WebSupport.build
|
||||||
|
|
||||||
.. method:: build()
|
.. automethod:: sphinx.websupport.WebSupport.get_document
|
||||||
|
|
||||||
Build the data used by the web support package.
|
.. automethod:: sphinx.websupport.WebSupport.get_comments
|
||||||
|
|
||||||
.. method:: get_document(docname)
|
.. automethod:: sphinx.websupport.WebSupport.add_comment
|
||||||
|
|
||||||
|
.. automethod:: sphinx.websupport.WebSupport.process_vote
|
||||||
|
|
||||||
|
.. automethod:: sphinx.websupport.WebSupport.get_search_results
|
||||||
|
|
||||||
Retrieve the context dictionary corresponding to the *docname*.
|
|
||||||
|
|||||||
+190
-93
@@ -3,118 +3,215 @@
|
|||||||
Web Support Quick Start
|
Web Support Quick Start
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Getting Started
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To use the :ref:`websupportapi` in your application you must import
|
|
||||||
the :class:`~sphinx.websupport.api.WebSupport` object::
|
|
||||||
|
|
||||||
from sphinx.websupport import support
|
|
||||||
|
|
||||||
This provides a reference to a :class:`~sphinx.websupport.api.WebSupport`
|
|
||||||
object. You will then need to provide some information about your
|
|
||||||
environment::
|
|
||||||
|
|
||||||
support.init(srcdir='/path/to/rst/sources/',
|
|
||||||
outdir='/path/to/build/outdir',
|
|
||||||
search='xapian')
|
|
||||||
|
|
||||||
Note: You only need to provide a srcdir if you are building documentation.
|
|
||||||
|
|
||||||
Building Documentation Data
|
Building Documentation Data
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
In order to use the web support package in a webapp, you will need to
|
To make use of the web support package in your application you will
|
||||||
build the data it uses. This data includes document data used to display
|
need to build that data it uses. This data includes pickle files representing
|
||||||
documentation and search indexes. To build this data, call the build method::
|
documents, search indices, and node data that is used to track where
|
||||||
|
comments and other things are in a document. Do do this you will need
|
||||||
|
to create an instance of the :class:`~sphinx.websupport.api.WebSupport`
|
||||||
|
class and call it's :meth:`~sphinx.websupport.WebSupport.build` method::
|
||||||
|
|
||||||
|
from sphinx.websupport import WebSupport
|
||||||
|
|
||||||
|
support = WebSupport(srcdir='/path/to/rst/sources/',
|
||||||
|
outdir='/path/to/build/outdir',
|
||||||
|
search='xapian')
|
||||||
|
|
||||||
support.build()
|
support.build()
|
||||||
|
|
||||||
This will create the data the web support package needs and place
|
This will read reStructuredText sources from `srcdir` and place the
|
||||||
it in *outdir*.
|
necessary data in `outdir`. This directory contains all the data needed
|
||||||
|
to display documents, search through documents, and add comments to
|
||||||
|
documents.
|
||||||
|
|
||||||
Accessing Document Data
|
Integrating Sphinx Documents Into Your Webapp
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
To access document data, call the get_document(docname) method. For example,
|
Now that you have the data, it's time to use it for something useful.
|
||||||
to retrieve the "contents" document, do this::
|
Start off by creating a :class:`~sphinx.websupport.WebSupport` object
|
||||||
|
for your application::
|
||||||
contents_doc = support.get_document('contents')
|
|
||||||
|
|
||||||
This will return a dictionary containing the context you need to render
|
|
||||||
a document.
|
|
||||||
|
|
||||||
Performing Searches
|
|
||||||
~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To perform a search, call the get_search_results(q) method, with *q* being
|
|
||||||
the string to be searched for::
|
|
||||||
|
|
||||||
q = request.GET['q']
|
|
||||||
search_doc = support.get_search_results(q)
|
|
||||||
|
|
||||||
This will return a dictionary in the same format as get_document() returns.
|
|
||||||
|
|
||||||
Full Example
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
A more useful example, in the form of a `Flask <http://flask.pocoo.org/>`_
|
|
||||||
application is::
|
|
||||||
|
|
||||||
from flask import Flask, render_template
|
|
||||||
from sphinx.websupport import support
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
support.init(outdir='/path/to/sphinx/data')
|
|
||||||
|
|
||||||
@app.route('/docs/<path:docname>')
|
from sphinx.websupport import WebSupport
|
||||||
|
|
||||||
|
support = WebSupport(datadir='/path/to/the/data',
|
||||||
|
search='xapian')
|
||||||
|
|
||||||
|
You'll only need one of these for each set of documentation you will be
|
||||||
|
working with. You can then call it's
|
||||||
|
:meth:`~sphinx.websupport.WebSupport.get_document` method to access
|
||||||
|
individual documents.
|
||||||
|
|
||||||
|
contents = support.get_document('contents')
|
||||||
|
|
||||||
|
This will return a dictionary containing the following items:
|
||||||
|
|
||||||
|
* **body**: The main body of the document as HTML
|
||||||
|
* **sidebar**: The sidebar of the document as HTML
|
||||||
|
* **relbar**: A div containing links to related documents
|
||||||
|
* **title**: The title of the document
|
||||||
|
|
||||||
|
This dict can then be used as context for templates. The goal is to be
|
||||||
|
easy to integrate with your existing templating system. An example using
|
||||||
|
`Jinja2 <http://jinja.pocoo.org/2/>`_ is:
|
||||||
|
|
||||||
|
.. sourcecode:: html+jinja
|
||||||
|
|
||||||
|
{%- extends "layout.html" %}
|
||||||
|
|
||||||
|
{%- block title %}
|
||||||
|
{{ document.title }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{%- block relbar %}
|
||||||
|
{{ document.relbar|safe }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{%- block body %}
|
||||||
|
{{ document.body|safe }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{%- block sidebar %}
|
||||||
|
{{ document.sidebar|safe }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
Most likely you'll want to create one function that can handle all of
|
||||||
|
document requests. An example `Flask <http://flask.pocoo.org/>`_ function
|
||||||
|
that performs this is::
|
||||||
|
|
||||||
|
@app.route('/<path:docname>')
|
||||||
def doc(docname):
|
def doc(docname):
|
||||||
document = support.get_document(docname)
|
document = support.get_document(docname)
|
||||||
return render_template('doc.html', document=document)
|
return render_template('doc.html', document=document)
|
||||||
|
|
||||||
@app.route('/docs/search')
|
This captures the request path, and passes it directly to
|
||||||
|
:meth:`~sphinx.websupport.WebSupport.get_document`, which retrieves
|
||||||
|
the correct document.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
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::
|
||||||
|
|
||||||
|
@app.route('/docs/<path:docname>')
|
||||||
|
|
||||||
|
Performing Searches
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
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
|
||||||
|
to retrieve search results. In `Flask <http://flask.pocoo.org/>`_ that
|
||||||
|
would be like this::
|
||||||
|
|
||||||
|
@app.route('/search')
|
||||||
def search():
|
def search():
|
||||||
document = support.get_search_results(request.args.get('q', ''))
|
q = request.args.get('q')
|
||||||
return render_template('doc.html', document=document)
|
document = support.get_search_results(q)
|
||||||
|
return render_template('doc.html', document=document)
|
||||||
|
|
||||||
In the previous example the doc.html template would look something
|
Note that we used the same template to render our search results as we
|
||||||
like this::
|
did to render our documents. That's because
|
||||||
|
:meth:`~sphinx.websupport.WebSupport.get_search_results` returns a context
|
||||||
|
dict in the same format as
|
||||||
|
:meth:`~sphinx.websupport.WebSupport.get_document`.
|
||||||
|
|
||||||
{% extends "base.html" %}
|
Comments
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
{% block title %}
|
The web support package provides a way to attach comments to some nodes
|
||||||
{{ document.title }}
|
in your document. It marks these nodes by adding a class and id to these
|
||||||
{% endblock %}
|
nodes. A client side script can then locate these nodes, and manipulate
|
||||||
|
them to allow commenting. A jquery script is also being developed that will
|
||||||
|
be included when it's complete. For now you can find the script here:
|
||||||
|
`websupport.js <http://bit.ly/cyaRaF>`_.
|
||||||
|
|
||||||
{% block extra_js %}
|
If you use the script that is included, you will have to define some
|
||||||
<script type="text/javascript" src="/static/jquery.js"></script>
|
simple templates that the script uses to display comments. The first
|
||||||
<script type="text/javascript">
|
template defines the layout for the popup div used to display comments:
|
||||||
<!--
|
|
||||||
$(document).ready(function() {
|
|
||||||
$(".spxcmt").append(' <a href="#" class="sphinx_comment"><img src="/static/comment.png" /></a>');
|
|
||||||
$("a.sphinx_comment").click(function() {
|
|
||||||
id = $(this).parent().attr('id');
|
|
||||||
alert('[ comment stub ' + id + ' ]');
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-->
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block relbar %}
|
.. sourcecode:: guess
|
||||||
{{ document.relbar|safe }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
<script type="text/html" id="popup_template">
|
||||||
{{ document.body|safe }}
|
<div class="popup_comment">
|
||||||
{% endblock %}
|
<a id="comment_close" href="#">x</a>
|
||||||
|
<h1>Comments</h1>
|
||||||
|
<form method="post" id="comment_form" action="/docs/add_comment">
|
||||||
|
<textarea name="comment"></textarea>
|
||||||
|
<input type="submit" value="add comment" id="comment_button" />
|
||||||
|
<input type="hidden" name="parent" />
|
||||||
|
<p class="sort_options">
|
||||||
|
Sort by:
|
||||||
|
<a href="#" class="sort_option" id="rating">top</a>
|
||||||
|
<a href="#" class="sort_option" id="ascage">newest</a>
|
||||||
|
<a href="#" class="sort_option" id="age">oldest</a>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
<h3 id="comment_notification">loading comments... <img src="/static/ajax-loader.gif" alt="" /></h3>
|
||||||
|
<ul id="comment_ul"></ul>
|
||||||
|
</div>
|
||||||
|
<div id="focuser"></div>
|
||||||
|
</script>
|
||||||
|
|
||||||
{% block sidebar %}
|
The next templat is an `li` that contains the form used to
|
||||||
{{ document.sidebar|safe }}
|
reply to a comment:
|
||||||
{% endblock %}
|
|
||||||
|
.. sourcecode:: guess
|
||||||
|
|
||||||
|
<script type="text/html" id="reply_template">
|
||||||
|
<li>
|
||||||
|
<div class="reply_div" id="rd<%id%>">
|
||||||
|
<form id="rf<%id%>">
|
||||||
|
<textarea name="comment"></textarea>
|
||||||
|
<input type="submit" value="add reply" />
|
||||||
|
<input type="hidden" name="parent" value="c<%id%>" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
The final template contains HTML that will be used to display comments
|
||||||
|
in the comment tree:
|
||||||
|
|
||||||
|
.. sourcecode:: guess
|
||||||
|
|
||||||
|
<script type="text/html" id="comment_template">
|
||||||
|
<div id="cd<%id%>" class="spxcdiv">
|
||||||
|
<div class="vote">
|
||||||
|
<div class="arrow">
|
||||||
|
<a href="#" id="uv<%id%>" class="vote">
|
||||||
|
<img src="<%upArrow%>" />
|
||||||
|
</a>
|
||||||
|
<a href="#" id="uu<%id%>" class="un vote">
|
||||||
|
<img src="<%upArrowPressed%>" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="arrow">
|
||||||
|
<a href="#" id="dv<%id%>" class="vote">
|
||||||
|
<img src="<%downArrow%>" id="da<%id%>" />
|
||||||
|
</a>
|
||||||
|
<a href="#" id="du<%id%>" class="un vote">
|
||||||
|
<img src="<%downArrowPressed%>" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="comment_content">
|
||||||
|
<p class="tagline comment">
|
||||||
|
<span class="user_id"><%username%></span>
|
||||||
|
<span class="rating"><%pretty_rating%></span>
|
||||||
|
<span class="delta"><%time.delta%></span>
|
||||||
|
</p>
|
||||||
|
<p class="comment_text comment"><%text%></p>
|
||||||
|
<p class="comment_opts comment">
|
||||||
|
<a href="#" class="reply" id="rl<%id%>">reply</a>
|
||||||
|
<a href="#" class="close_reply" id="cr<%id%>">hide</a>
|
||||||
|
</p>
|
||||||
|
<ul class="children" id="cl<%id%>"></ul>
|
||||||
|
</div>
|
||||||
|
<div class="clearleft"></div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
{% block relbar %}
|
|
||||||
{{ document.relbar|safe }}
|
|
||||||
{% endblock %}
|
|
||||||
+135
-12
@@ -27,18 +27,25 @@ class WebSupportApp(Sphinx):
|
|||||||
Sphinx.__init__(self, *args, **kwargs)
|
Sphinx.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
class WebSupport(object):
|
class WebSupport(object):
|
||||||
def __init__(self, srcdir='', outdir='', search=None,
|
"""The main API class for the web support package. All interactions
|
||||||
|
with the web support package should occur through this class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, srcdir='', outdir='', datadir='', search=None,
|
||||||
comments=None):
|
comments=None):
|
||||||
self.srcdir = srcdir
|
self.srcdir = srcdir
|
||||||
self.outdir = outdir or path.join(self.srcdir, '_build',
|
self.outdir = outdir or path.join(self.srcdir, '_build',
|
||||||
'websupport')
|
'websupport')
|
||||||
self.init_templating()
|
self._init_templating()
|
||||||
if search is not None:
|
|
||||||
self.init_search(search)
|
|
||||||
|
|
||||||
self.init_comments(comments)
|
self.outdir = outdir or datadir
|
||||||
|
|
||||||
|
if search is not None:
|
||||||
|
self._init_search(search)
|
||||||
|
|
||||||
|
self._init_comments(comments)
|
||||||
|
|
||||||
def init_comments(self, comments):
|
def _init_comments(self, comments):
|
||||||
if isinstance(comments, sphinxcomments.CommentBackend):
|
if isinstance(comments, sphinxcomments.CommentBackend):
|
||||||
self.comments = comments
|
self.comments = comments
|
||||||
else:
|
else:
|
||||||
@@ -51,14 +58,14 @@ class WebSupport(object):
|
|||||||
engine = create_engine('sqlite:///%s' % db_path)
|
engine = create_engine('sqlite:///%s' % db_path)
|
||||||
self.comments = SQLAlchemyComments(engine)
|
self.comments = SQLAlchemyComments(engine)
|
||||||
|
|
||||||
def init_templating(self):
|
def _init_templating(self):
|
||||||
import sphinx
|
import sphinx
|
||||||
template_path = path.join(path.dirname(sphinx.__file__),
|
template_path = path.join(path.dirname(sphinx.__file__),
|
||||||
'themes', 'basic')
|
'themes', 'basic')
|
||||||
loader = FileSystemLoader(template_path)
|
loader = FileSystemLoader(template_path)
|
||||||
self.template_env = Environment(loader=loader)
|
self.template_env = Environment(loader=loader)
|
||||||
|
|
||||||
def init_search(self, search):
|
def _init_search(self, search):
|
||||||
mod, cls = search_adapters[search]
|
mod, cls = search_adapters[search]
|
||||||
search_class = getattr(__import__('sphinx.websupport.search.' + mod,
|
search_class = getattr(__import__('sphinx.websupport.search.' + mod,
|
||||||
None, None, [cls]), cls)
|
None, None, [cls]), cls)
|
||||||
@@ -67,24 +74,65 @@ class WebSupport(object):
|
|||||||
self.results_template = \
|
self.results_template = \
|
||||||
self.template_env.get_template('searchresults.html')
|
self.template_env.get_template('searchresults.html')
|
||||||
|
|
||||||
def build(self, **kwargs):
|
def build(self):
|
||||||
doctreedir = kwargs.pop('doctreedir',
|
"""Build the documentation. Places the data into the `outdir`
|
||||||
path.join(self.outdir, 'doctrees'))
|
directory. Use it like this::
|
||||||
|
|
||||||
|
support = WebSupport(srcdir, outdir, search)
|
||||||
|
support.build()
|
||||||
|
|
||||||
|
This will read reStructured text files from `srcdir`. Then it
|
||||||
|
build the pickles and search index, placing them into `outdir`.
|
||||||
|
It will also save node data to the database.
|
||||||
|
"""
|
||||||
|
doctreedir = path.join(self.outdir, 'doctrees')
|
||||||
app = WebSupportApp(self.srcdir, self.srcdir,
|
app = WebSupportApp(self.srcdir, self.srcdir,
|
||||||
self.outdir, doctreedir, 'websupport',
|
self.outdir, doctreedir, 'websupport',
|
||||||
search=self.search,
|
search=self.search,
|
||||||
comments=self.comments)
|
comments=self.comments)
|
||||||
|
# TODO:
|
||||||
|
# Hook comments into Sphinx signals.
|
||||||
self.comments.pre_build()
|
self.comments.pre_build()
|
||||||
app.build()
|
app.build()
|
||||||
self.comments.post_build()
|
self.comments.post_build()
|
||||||
|
|
||||||
def get_document(self, docname):
|
def get_document(self, docname):
|
||||||
|
"""Load and return a document from a pickle. The document will
|
||||||
|
be a dict object which can be used to render a template::
|
||||||
|
|
||||||
|
support = WebSupport(outdir=outdir)
|
||||||
|
support.get_document('index')
|
||||||
|
|
||||||
|
In most cases `docname` will be taken from the request path and
|
||||||
|
passed directly to this function. In Flask, that would be something
|
||||||
|
like this::
|
||||||
|
|
||||||
|
@app.route('/<path:docname>')
|
||||||
|
def index(docname):
|
||||||
|
q = request.args.get('q')
|
||||||
|
document = support.get_search_results(q)
|
||||||
|
render_template('doc.html', document=document)
|
||||||
|
|
||||||
|
The document dict that is returned contains the following items
|
||||||
|
to be used during template rendering.
|
||||||
|
|
||||||
|
:param docname: the name of the document to load.
|
||||||
|
"""
|
||||||
infilename = path.join(self.outdir, docname + '.fpickle')
|
infilename = path.join(self.outdir, docname + '.fpickle')
|
||||||
f = open(infilename, 'rb')
|
f = open(infilename, 'rb')
|
||||||
document = pickle.load(f)
|
document = pickle.load(f)
|
||||||
return document
|
return document
|
||||||
|
|
||||||
def get_search_results(self, q):
|
def get_search_results(self, q):
|
||||||
|
"""Perform a search for the query `q`, and create a set
|
||||||
|
of search results. Then render the search results as html and
|
||||||
|
return a context dict like the one created by
|
||||||
|
:meth:`get_document`::
|
||||||
|
|
||||||
|
document = support.get_search_results(q)
|
||||||
|
|
||||||
|
:param q: the search query
|
||||||
|
"""
|
||||||
results, results_found, results_displayed = self.search.query(q)
|
results, results_found, results_displayed = self.search.query(q)
|
||||||
ctx = {'search_performed': True,
|
ctx = {'search_performed': True,
|
||||||
'search_results': results,
|
'search_results': results,
|
||||||
@@ -94,14 +142,89 @@ class WebSupport(object):
|
|||||||
document['title'] = 'Search Results'
|
document['title'] = 'Search Results'
|
||||||
return document
|
return document
|
||||||
|
|
||||||
def get_comments(self, node_id, user_id):
|
def get_comments(self, node_id, user_id=None):
|
||||||
|
"""Get the comments associated with `node_id`. If `user_id` is
|
||||||
|
given vote information will be included with the returned comments.
|
||||||
|
The default CommentBackend returns a list of dicts. Each dict
|
||||||
|
represents a comment, and has the following items:
|
||||||
|
|
||||||
|
============ ======================================================
|
||||||
|
Key Contents
|
||||||
|
============ ======================================================
|
||||||
|
text The comment text.
|
||||||
|
username The username that was stored with the comment.
|
||||||
|
id The comment's unique identifier.
|
||||||
|
rating The comment's current rating.
|
||||||
|
age The time in seconds since the comment was added.
|
||||||
|
time A dict containing time information. It contains the
|
||||||
|
following keys: year, month, day, hour, minute, second,
|
||||||
|
iso, and delta. `iso` is the time formatted in ISO
|
||||||
|
8601 format. `delta` is a printable form of how old
|
||||||
|
the comment is (e.g. "3 hours ago").
|
||||||
|
vote If `user_id` was given, this will be an integer
|
||||||
|
representing the vote. 1 for an upvote, -1 for a
|
||||||
|
downvote, or 0 if unvoted.
|
||||||
|
node The node that the comment is attached to. If the
|
||||||
|
comment's parent is another comment rather than a
|
||||||
|
node, this will be null.
|
||||||
|
parent The id of the comment that this comment is attached
|
||||||
|
to if it is not attached to a node.
|
||||||
|
children A list of all children, in this format.
|
||||||
|
============ ======================================================
|
||||||
|
|
||||||
|
:param node_id: the id of the node to get comments for.
|
||||||
|
:param user_id: the id of the user viewing the comments.
|
||||||
|
"""
|
||||||
return self.comments.get_comments(node_id, user_id)
|
return self.comments.get_comments(node_id, user_id)
|
||||||
|
|
||||||
def add_comment(self, parent_id, text, displayed=True, username=None,
|
def add_comment(self, parent_id, text, displayed=True, username=None,
|
||||||
rating=0, time=None):
|
rating=0, time=None):
|
||||||
|
"""Add a comment to a node or another comment. `parent_id` will have
|
||||||
|
a one letter prefix, distinguishing between node parents and
|
||||||
|
comment parents, 'c' and 's' respectively. This function will
|
||||||
|
return the comment in the same format as :meth:`get_comments`.
|
||||||
|
Usage is simple::
|
||||||
|
|
||||||
|
comment = support.add_comment(parent_id, text)
|
||||||
|
|
||||||
|
If you would like to store a username with the comment, pass
|
||||||
|
in the optional `username` keyword argument::
|
||||||
|
|
||||||
|
comment = support.add_comment(parent_id, text, username=username)
|
||||||
|
|
||||||
|
:param parent_id: the prefixed id of the comment's parent.
|
||||||
|
:param text: the text of the comment.
|
||||||
|
:param displayed: for future use...
|
||||||
|
:param username: the username of the user making the comment.
|
||||||
|
:param rating: the starting rating of the comment, defaults to 0.
|
||||||
|
:param time: the time the comment was created, defaults to now.
|
||||||
|
"""
|
||||||
return self.comments.add_comment(parent_id, text, displayed,
|
return self.comments.add_comment(parent_id, text, displayed,
|
||||||
username, rating, time)
|
username, rating, time)
|
||||||
|
|
||||||
def process_vote(self, comment_id, user_id, value):
|
def process_vote(self, comment_id, user_id, value):
|
||||||
|
"""Process a user's vote. The web support package relies
|
||||||
|
on the API user to perform authentication. The API user will
|
||||||
|
typically receive a comment_id and value from a form, and then
|
||||||
|
make sure the user is authenticated. A unique integer `user_id`
|
||||||
|
(usually the User primary key) must be passed in, which will
|
||||||
|
also be used to retrieve the user's past voting information.
|
||||||
|
An example, once again in Flask::
|
||||||
|
|
||||||
|
@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"
|
||||||
|
|
||||||
|
:param comment_id: the comment being voted on
|
||||||
|
:param user_id: the unique integer id of the user voting
|
||||||
|
:param value: 1 for an upvote, -1 for a downvote, 0 for an unvote.
|
||||||
|
"""
|
||||||
value = int(value)
|
value = int(value)
|
||||||
self.comments.process_vote(comment_id, user_id, value)
|
self.comments.process_vote(comment_id, user_id, value)
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
sphinx.websupport.api
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
All API functions.
|
|
||||||
|
|
||||||
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
|
||||||
:license: BSD, see LICENSE for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import cPickle as pickle
|
|
||||||
from os import path
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
|
|
||||||
from sphinx.application import Sphinx
|
|
||||||
from sphinx.util.osutil import ensuredir
|
|
||||||
from sphinx.websupport.search import search_adapters
|
|
||||||
from sphinx.websupport import comments as sphinxcomments
|
|
||||||
|
|
||||||
class WebSupportApp(Sphinx):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.search = kwargs.pop('search', None)
|
|
||||||
self.comments = kwargs.pop('comments', None)
|
|
||||||
Sphinx.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
class WebSupport(object):
|
|
||||||
def __init__(self, srcdir='', outdir='', search=None,
|
|
||||||
comments=None):
|
|
||||||
self.srcdir = srcdir
|
|
||||||
self.outdir = outdir or path.join(self.srcdir, '_build',
|
|
||||||
'websupport')
|
|
||||||
self.init_templating()
|
|
||||||
if search is not None:
|
|
||||||
self.init_search(search)
|
|
||||||
|
|
||||||
self.init_comments(comments)
|
|
||||||
|
|
||||||
def init_comments(self, comments):
|
|
||||||
if isinstance(comments, sphinxcomments.CommentBackend):
|
|
||||||
self.comments = comments
|
|
||||||
else:
|
|
||||||
# If a CommentBackend isn't provided, use the default
|
|
||||||
# SQLAlchemy backend with an SQLite db.
|
|
||||||
from sphinx.websupport.comments import SQLAlchemyComments
|
|
||||||
from sqlalchemy import create_engine
|
|
||||||
db_path = path.join(self.outdir, 'comments', 'comments.db')
|
|
||||||
ensuredir(path.dirname(db_path))
|
|
||||||
engine = create_engine('sqlite:///%s' % db_path)
|
|
||||||
self.comments = SQLAlchemyComments(engine)
|
|
||||||
|
|
||||||
def init_templating(self):
|
|
||||||
import sphinx
|
|
||||||
template_path = path.join(path.dirname(sphinx.__file__),
|
|
||||||
'themes', 'basic')
|
|
||||||
loader = FileSystemLoader(template_path)
|
|
||||||
self.template_env = Environment(loader=loader)
|
|
||||||
|
|
||||||
def init_search(self, search):
|
|
||||||
mod, cls = search_adapters[search]
|
|
||||||
search_class = getattr(__import__('sphinx.websupport.search.' + mod,
|
|
||||||
None, None, [cls]), cls)
|
|
||||||
search_path = path.join(self.outdir, 'search')
|
|
||||||
self.search = search_class(search_path)
|
|
||||||
self.results_template = \
|
|
||||||
self.template_env.get_template('searchresults.html')
|
|
||||||
|
|
||||||
def build(self, **kwargs):
|
|
||||||
doctreedir = kwargs.pop('doctreedir',
|
|
||||||
path.join(self.outdir, 'doctrees'))
|
|
||||||
app = WebSupportApp(self.srcdir, self.srcdir,
|
|
||||||
self.outdir, doctreedir, 'websupport',
|
|
||||||
search=self.search,
|
|
||||||
comments=self.comments)
|
|
||||||
self.comments.pre_build()
|
|
||||||
app.build()
|
|
||||||
self.comments.post_build()
|
|
||||||
|
|
||||||
def get_document(self, docname):
|
|
||||||
infilename = path.join(self.outdir, docname + '.fpickle')
|
|
||||||
f = open(infilename, 'rb')
|
|
||||||
document = pickle.load(f)
|
|
||||||
return document
|
|
||||||
|
|
||||||
def get_search_results(self, q):
|
|
||||||
results, results_found, results_displayed = self.search.query(q)
|
|
||||||
ctx = {'search_performed': True,
|
|
||||||
'search_results': results,
|
|
||||||
'q': q}
|
|
||||||
document = self.get_document('search')
|
|
||||||
document['body'] = self.results_template.render(ctx)
|
|
||||||
document['title'] = 'Search Results'
|
|
||||||
return document
|
|
||||||
|
|
||||||
def get_comments(self, node_id, user_id):
|
|
||||||
return self.comments.get_comments(node_id, user_id)
|
|
||||||
|
|
||||||
def add_comment(self, parent_id, text, displayed=True, username=None,
|
|
||||||
rating=0, time=None):
|
|
||||||
return self.comments.add_comment(parent_id, text, displayed,
|
|
||||||
username, rating, time)
|
|
||||||
|
|
||||||
def process_vote(self, comment_id, user_id, value):
|
|
||||||
value = int(value)
|
|
||||||
self.comments.process_vote(comment_id, user_id, value)
|
|
||||||
Reference in New Issue
Block a user