summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sphinx/builders/versioning.py2
-rw-r--r--sphinx/builders/websupport.py95
-rw-r--r--sphinx/themes/basic/layout.html45
-rw-r--r--sphinx/themes/basic/searchresults.html20
-rw-r--r--sphinx/themes/basic/static/websupport.js51
-rw-r--r--sphinx/websupport/__init__.py106
-rw-r--r--sphinx/websupport/errors.py7
-rw-r--r--sphinx/writers/websupport.py2
-rw-r--r--tests/test_websupport.py2
9 files changed, 175 insertions, 155 deletions
diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py
index 6c2bccca4..f0646a829 100644
--- a/sphinx/builders/versioning.py
+++ b/sphinx/builders/versioning.py
@@ -7,7 +7,7 @@
:license: BSD, see LICENSE for details.
"""
import os
-import pickle
+import cPickle as pickle
from docutils.utils import Reporter
diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py
index 7970f8bd7..9ca9f3bb7 100644
--- a/sphinx/builders/websupport.py
+++ b/sphinx/builders/websupport.py
@@ -9,31 +9,43 @@
:license: BSD, see LICENSE for details.
"""
-import cPickle as pickle
from os import path
import posixpath
import shutil
from docutils.io import StringOutput
+from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile
-from sphinx.util.jsonimpl import dumps as dump_json
from sphinx.util.websupport import is_commentable
-from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.builders.html import PickleHTMLBuilder
from sphinx.builders.versioning import VersioningBuilderMixin
from sphinx.writers.websupport import WebSupportTranslator
-class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
+class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin):
"""
Builds documents for the web support package.
"""
name = 'websupport'
- out_suffix = '.fpickle'
def init(self):
- StandaloneHTMLBuilder.init(self)
+ PickleHTMLBuilder.init(self)
VersioningBuilderMixin.init(self)
+ # templates are needed for this builder, but the serializing
+ # builder does not initialize them
+ self.init_templates()
+ if not isinstance(self.templates, BuiltinTemplateLoader):
+ raise RuntimeError('websupport builder must be used with '
+ 'the builtin templates')
+ # add our custom JS
+ self.script_files.append('_static/websupport.js')
+
+ def set_webinfo(self, staticdir, virtual_staticdir, search, storage):
+ self.staticdir = staticdir
+ self.virtual_staticdir = virtual_staticdir
+ self.search = search
+ self.storage = storage
def init_translator_class(self):
self.translator_class = WebSupportTranslator
@@ -46,9 +58,9 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
self.cur_docname = docname
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
- self.imgpath = '/' + posixpath.join(self.app.staticdir, '_images')
+ self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images')
self.post_process_images(doctree)
- self.dlpath = '/' + posixpath.join(self.app.staticdir, '_downloads')
+ self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads')
self.docwriter.write(doctree, destination)
self.docwriter.assemble_parts()
body = self.docwriter.parts['fragment']
@@ -58,11 +70,8 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
self.index_page(docname, doctree, ctx.get('title', ''))
self.handle_page(docname, ctx, event_arg=doctree)
- def get_target_uri(self, docname, typ=None):
- return docname
-
def load_indexer(self, docnames):
- self.indexer = self.app.search
+ self.indexer = self.search
self.indexer.init_indexing(changed=docnames)
def handle_page(self, pagename, addctx, templatename='page.html',
@@ -75,11 +84,13 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
def pathto(otheruri, resource=False,
baseuri=self.get_target_uri(pagename)):
- if not resource:
+ if resource and '://' in otheruri:
+ return otheruri
+ elif not resource:
otheruri = self.get_target_uri(otheruri)
return relative_uri(baseuri, otheruri) or '#'
else:
- return '/' + posixpath.join(self.app.staticdir, otheruri)
+ return '/' + posixpath.join(self.virtual_staticdir, otheruri)
ctx['pathto'] = pathto
ctx['hasdoc'] = lambda name: name in self.env.all_docs
ctx['encoding'] = self.config.html_output_encoding
@@ -90,47 +101,41 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
self.app.emit('html-page-context', pagename, templatename,
ctx, event_arg)
- # Create a dict that will be pickled and used by webapps.
- css = '<link rel="stylesheet" href="%s" type=text/css />' % \
- pathto('_static/pygments.css', 1)
- doc_ctx = {'body': ctx.get('body', ''),
- 'title': ctx.get('title', ''),
- 'css': css,
- 'js': self._make_js(ctx)}
- # Partially render the html template to proved a more useful ctx.
+ # create a dict that will be pickled and used by webapps
+ doc_ctx = {
+ 'body': ctx.get('body', ''),
+ 'title': ctx.get('title', ''),
+ }
+ # partially render the html template to get at interesting macros
template = self.templates.environment.get_template(templatename)
template_module = template.make_module(ctx)
- if hasattr(template_module, 'sidebar'):
- doc_ctx['sidebar'] = template_module.sidebar()
- if hasattr(template_module, 'relbar'):
- doc_ctx['relbar'] = template_module.relbar()
+ for item in ['sidebar', 'relbar', 'script', 'css']:
+ if hasattr(template_module, item):
+ doc_ctx[item] = getattr(template_module, item)()
if not outfilename:
outfilename = path.join(self.outdir, 'pickles',
os_path(pagename) + self.out_suffix)
-
ensuredir(path.dirname(outfilename))
- f = open(outfilename, 'wb')
- try:
- pickle.dump(doc_ctx, f, pickle.HIGHEST_PROTOCOL)
- finally:
- f.close()
+ self.dump_context(doc_ctx, outfilename)
# if there is a source file, copy the source file for the
# "show source" link
if ctx.get('sourcename'):
- source_name = path.join(self.app.builddir, self.app.staticdir,
+ source_name = path.join(self.staticdir,
'_sources', os_path(ctx['sourcename']))
ensuredir(path.dirname(source_name))
copyfile(self.env.doc2path(pagename), source_name)
def handle_finish(self):
- StandaloneHTMLBuilder.handle_finish(self)
+ PickleHTMLBuilder.handle_finish(self)
VersioningBuilderMixin.finish(self)
+
+ # move static stuff over to separate directory
directories = ['_images', '_static']
for directory in directories:
src = path.join(self.outdir, directory)
- dst = path.join(self.app.builddir, self.app.staticdir, directory)
+ dst = path.join(self.staticdir, directory)
if path.isdir(src):
if path.isdir(dst):
shutil.rmtree(dst)
@@ -138,23 +143,3 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
def dump_search_index(self):
self.indexer.finish_indexing()
-
- def _make_js(self, ctx):
- def make_script(file):
- path = ctx['pathto'](file, 1)
- return '<script type="text/javascript" src="%s"></script>' % path
-
- opts = {
- 'URL_ROOT': ctx.get('url_root', ''),
- 'VERSION': ctx['release'],
- 'COLLAPSE_INDEX': False,
- 'FILE_SUFFIX': '',
- 'HAS_SOURCE': ctx['has_source']
- }
- scripts = [make_script(file) for file in ctx['script_files']]
- scripts.append(make_script('_static/websupport.js'))
- return '\n'.join([
- '<script type="text/javascript">'
- 'var DOCUMENTATION_OPTIONS = %s;' % dump_json(opts),
- '</script>'
- ] + scripts)
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index e31e85443..82b40e1af 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -16,7 +16,13 @@
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
(sidebars != []) %}
{%- set url_root = pathto('', 1) %}
+{# XXX necessary? #}
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
+{%- if not embedded and docstitle %}
+ {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
+{%- else %}
+ {%- set titlesuffix = "" %}
+{%- endif %}
{%- macro relbar() %}
<div class="related">
@@ -78,24 +84,7 @@
{%- endif %}
{%- endmacro %}
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
- {{ metatags }}
- {%- if not embedded and docstitle %}
- {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
- {%- else %}
- {%- set titlesuffix = "" %}
- {%- endif %}
- {%- block htmltitle %}
- <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
- {%- endblock %}
- <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
- <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
- {%- for cssfile in css_files %}
- <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
- {%- endfor %}
- {%- if not embedded %}
+{%- macro script() %}
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '{{ url_root }}',
@@ -108,6 +97,26 @@
{%- for scriptfile in script_files %}
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
+{%- endmacro %}
+
+{%- macro css() %}
+ <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+ <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
+ {%- for cssfile in css_files %}
+ <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
+ {%- endfor %}
+{%- endmacro %}
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
+ {{ metatags }}
+ {%- block htmltitle %}
+ <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
+ {%- endblock %}
+ {{ css() }}
+ {%- if not embedded %}
+ {{ script() }}
{%- if use_opensearch %}
<link rel="search" type="application/opensearchdescription+xml"
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html
index 4b5da1a39..d7af48b2f 100644
--- a/sphinx/themes/basic/searchresults.html
+++ b/sphinx/themes/basic/searchresults.html
@@ -1,6 +1,6 @@
{#
basic/searchresults.html
- ~~~~~~~~~~~~~~~~~
+ ~~~~~~~~~~~~~~~~~~~~~~~~
Template for the body of the search results page.
@@ -17,20 +17,20 @@
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
-{% if search_performed %}
-<h2>Search Results</h2>
-{% if not search_results %}
-<p>Your search did not match any results.</p>
-{% endif %}
-{% endif %}
+{%- if search_performed %}
+ <h2>Search Results</h2>
+ {%- if not search_results %}
+ <p>Your search did not match any results.</p>
+ {%- endif %}
+{%- endif %}
<div id="search-results">
- {% if search_results %}
+ {%- if search_results %}
<ul class="search">
{% for href, caption, context in search_results %}
- <li><a href="{{ href }}?highlight={{ q }}">{{ caption }}</a>
+ <li><a href="{{ docroot }}{{ href }}/?highlight={{ q }}">{{ caption }}</a>
<div class="context">{{ context|e }}</div>
</li>
{% endfor %}
</ul>
- {% endif %}
+ {%- endif %}
</div>
diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js
index 51a5a7d7c..f545b5716 100644
--- a/sphinx/themes/basic/static/websupport.js
+++ b/sphinx/themes/basic/static/websupport.js
@@ -214,10 +214,18 @@
* Add a comment via ajax and insert the comment into the comment tree.
*/
function addComment(form) {
- // Disable the form that is being submitted.
- form.find('textarea,input').attr('disabled', 'disabled');
var node_id = form.find('input[name="node"]').val();
var parent_id = form.find('input[name="parent"]').val();
+ var text = form.find('textarea[name="comment"]').val();
+ var proposal = form.find('textarea[name="proposal"]').val();
+
+ if (text == '') {
+ showError('Please enter a comment.');
+ return;
+ }
+
+ // Disable the form that is being submitted.
+ form.find('textarea,input').attr('disabled', 'disabled');
// Send the comment to the server.
$.ajax({
@@ -227,8 +235,8 @@
data: {
node: node_id,
parent: parent_id,
- text: form.find('textarea[name="comment"]').val(),
- proposal: form.find('textarea[name="proposal"]').val()
+ text: text,
+ proposal: proposal
},
success: function(data, textStatus, error) {
// Reset the form.
@@ -311,7 +319,7 @@
$('#cm' + id).fadeOut('fast');
},
error: function(request, textStatus, error) {
- showError("Oops, there was a problem accepting the comment.");
+ showError('Oops, there was a problem accepting the comment.');
}
});
}
@@ -328,7 +336,7 @@
});
},
error: function(request, textStatus, error) {
- showError("Oops, there was a problem rejecting the comment.");
+ showError('Oops, there was a problem rejecting the comment.');
}
});
}
@@ -354,7 +362,7 @@
div.data('comment', comment);
},
error: function(request, textStatus, error) {
- showError("Oops, there was a problem deleting the comment.");
+ showError('Oops, there was a problem deleting the comment.');
}
});
}
@@ -395,7 +403,7 @@
var classes = link.attr('class').split(/\s+/);
for (var i=0; i<classes.length; i++) {
if (classes[i] != 'sort-option') {
- by = classes[i];
+ by = classes[i].substring(2);
}
}
setComparator();
@@ -464,7 +472,7 @@
url: opts.processVoteURL,
data: d,
error: function(request, textStatus, error) {
- showError("Oops, there was a problem casting that vote.");
+ showError('Oops, there was a problem casting that vote.');
}
});
}
@@ -589,7 +597,8 @@
function showError(message) {
$(document.createElement('div')).attr({'class': 'popup-error'})
- .append($(document.createElement('h1')).text(message))
+ .append($(document.createElement('div'))
+ .attr({'class': 'error-header'}).text(message))
.appendTo('body')
.fadeIn("slow")
.delay(2000)
@@ -642,12 +651,12 @@
};
var opts = jQuery.extend({
- processVoteURL: '/process_vote',
- addCommentURL: '/add_comment',
- getCommentsURL: '/get_comments',
- acceptCommentURL: '/accept_comment',
- rejectCommentURL: '/reject_comment',
- deleteCommentURL: '/delete_comment',
+ processVoteURL: '/_process_vote',
+ addCommentURL: '/_add_comment',
+ getCommentsURL: '/_get_comments',
+ acceptCommentURL: '/_accept_comment',
+ rejectCommentURL: '/_reject_comment',
+ deleteCommentURL: '/_delete_comment',
commentImage: '/static/_static/comment.png',
closeCommentImage: '/static/_static/comment-close.png',
loadingImage: '/static/_static/ajax-loader.gif',
@@ -727,7 +736,7 @@
var popupTemplate = '\
<div class="sphinx-comments" id="sc<%id%>">\
- <h1>Comments</h1>\
+ <div class="comment-header">Comments</div>\
<form method="post" id="cf<%id%>" class="comment-form" action="/docs/add_comment">\
<textarea name="comment" cols="80"></textarea>\
<p class="propose-button">\
@@ -744,12 +753,12 @@
<input type="hidden" name="parent" value="" />\
<p class="sort-options">\
Sort by:\
- <a href="#" class="sort-option rating">top</a>\
- <a href="#" class="sort-option ascage">newest</a>\
- <a href="#" class="sort-option age">oldest</a>\
+ <a href="#" class="sort-option byrating">top</a>\
+ <a href="#" class="sort-option byascage">newest</a>\
+ <a href="#" class="sort-option byage">oldest</a>\
</p>\
</form>\
- <h3 id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></h3>\
+ <div class="comment-loading" id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></div>\
<ul id="cl<%id%>" class="comment-ul"></ul>\
</div>';
diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py
index f410e10d3..611c262d5 100644
--- a/sphinx/websupport/__init__.py
+++ b/sphinx/websupport/__init__.py
@@ -24,29 +24,35 @@ from sphinx.websupport.search import BaseSearch, SEARCH_ADAPTERS
from sphinx.websupport.storage import StorageBackend
-class WebSupportApp(Sphinx):
- def __init__(self, *args, **kwargs):
- self.staticdir = kwargs.pop('staticdir', None)
- self.builddir = kwargs.pop('builddir', None)
- self.search = kwargs.pop('search', None)
- self.storage = kwargs.pop('storage', None)
- Sphinx.__init__(self, *args, **kwargs)
-
-
class WebSupport(object):
"""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='', builddir='', datadir='', search=None,
- storage=None, status=sys.stdout, warning=sys.stderr,
- moderation_callback=None, staticdir='static',
- docroot=''):
+ def __init__(self,
+ srcdir=None, # only required for building
+ builddir='', # the dir with data/static/doctrees subdirs
+ datadir=None, # defaults to builddir/data
+ staticdir=None, # defaults to builddir/static
+ doctreedir=None, # defaults to builddir/doctrees
+ search=None, # defaults to no search
+ storage=None, # defaults to SQLite in datadir
+ status=sys.stdout,
+ warning=sys.stderr,
+ moderation_callback=None,
+ docroot='',
+ staticroot='static',
+ ):
+ # directories
self.srcdir = srcdir
self.builddir = builddir
self.outdir = path.join(builddir, 'data')
self.datadir = datadir or self.outdir
- self.staticdir = staticdir.strip('/')
+ self.staticdir = staticdir or path.join(self.builddir, 'static')
+ self.doctreedir = staticdir or path.join(self.builddir, 'doctrees')
+ # web server virtual paths
+ self.staticroot = staticroot.strip('/')
self.docroot = docroot.strip('/')
+
self.status = status
self.warning = warning
self.moderation_callback = moderation_callback
@@ -55,6 +61,8 @@ class WebSupport(object):
self._init_search(search)
self._init_storage(storage)
+ self._globalcontext = None
+
self._make_base_comment_options()
def _init_storage(self, storage):
@@ -103,19 +111,27 @@ class WebSupport(object):
It will also save node data to the database.
"""
if not self.srcdir:
- raise errors.SrcdirNotSpecifiedError( \
- 'No srcdir associated with WebSupport object')
- doctreedir = path.join(self.outdir, 'doctrees')
- app = WebSupportApp(self.srcdir, self.srcdir,
- self.outdir, doctreedir, 'websupport',
- search=self.search, status=self.status,
- warning=self.warning, storage=self.storage,
- staticdir=self.staticdir, builddir=self.builddir)
+ raise RuntimeError('No srcdir associated with WebSupport object')
+ app = Sphinx(self.srcdir, self.srcdir, self.outdir, self.doctreedir,
+ 'websupport', status=self.status, warning=self.warning)
+ app.builder.set_webinfo(self.staticdir, self.staticroot,
+ self.search, self.storage)
self.storage.pre_build()
app.build()
self.storage.post_build()
+ def get_globalcontext(self):
+ """Load and return the "global context" pickle."""
+ if not self._globalcontext:
+ infilename = path.join(self.datadir, 'globalcontext.pickle')
+ f = open(infilename, 'rb')
+ try:
+ self._globalcontext = pickle.load(f)
+ finally:
+ f.close()
+ return self._globalcontext
+
def get_document(self, docname, username='', moderator=False):
"""Load and return a document from a pickle. The document will
be a dict object which can be used to render a template::
@@ -146,28 +162,35 @@ class WebSupport(object):
* **relbar**: A div containing links to related documents
* **title**: The title of the document
* **css**: Links to css files used by Sphinx
- * **js**: Javascript containing comment options
+ * **script**: Javascript containing comment options
This raises :class:`~sphinx.websupport.errors.DocumentNotFoundError`
if a document matching `docname` is not found.
:param docname: the name of the document to load.
"""
- infilename = path.join(self.datadir, 'pickles', docname + '.fpickle')
+ docpath = path.join(self.datadir, 'pickles', docname)
+ if path.isdir(docpath):
+ infilename = docpath + '/index.fpickle'
+ else:
+ infilename = docpath + '.fpickle'
try:
f = open(infilename, 'rb')
except IOError:
raise errors.DocumentNotFoundError(
'The document "%s" could not be found' % docname)
+ try:
+ document = pickle.load(f)
+ finally:
+ f.close()
- document = pickle.load(f)
comment_opts = self._make_comment_options(username, moderator)
comment_metadata = self.storage.get_metadata(docname, moderator)
- document['js'] = '\n'.join([comment_opts,
- self._make_metadata(comment_metadata),
- document['js']])
+ document['script'] = '\n'.join([comment_opts,
+ self._make_metadata(comment_metadata),
+ document['script']])
return document
def get_search_results(self, q):
@@ -181,9 +204,12 @@ class WebSupport(object):
:param q: the search query
"""
results = self.search.query(q)
- ctx = {'search_performed': True,
- 'search_results': results,
- 'q': q}
+ ctx = {
+ 'q': q,
+ 'search_performed': True,
+ 'search_results': results,
+ 'docroot': '../', # XXX
+ }
document = self.get_document('search')
document['body'] = self.results_template.render(ctx)
document['title'] = 'Search Results'
@@ -359,17 +385,17 @@ class WebSupport(object):
if self.docroot != '':
comment_urls = [
- ('addCommentURL', 'add_comment'),
- ('getCommentsURL', 'get_comments'),
- ('processVoteURL', 'process_vote'),
- ('acceptCommentURL', 'accept_comment'),
- ('rejectCommentURL', 'reject_comment'),
- ('deleteCommentURL', 'delete_comment')
+ ('addCommentURL', '_add_comment'),
+ ('getCommentsURL', '_get_comments'),
+ ('processVoteURL', '_process_vote'),
+ ('acceptCommentURL', '_accept_comment'),
+ ('rejectCommentURL', '_reject_comment'),
+ ('deleteCommentURL', '_delete_comment')
]
for key, value in comment_urls:
self.base_comment_opts[key] = \
'/' + posixpath.join(self.docroot, value)
- if self.staticdir != 'static':
+ if self.staticroot != 'static':
static_urls = [
('commentImage', 'comment.png'),
('closeCommentImage', 'comment-close.png'),
@@ -382,7 +408,7 @@ class WebSupport(object):
]
for key, value in static_urls:
self.base_comment_opts[key] = \
- '/' + posixpath.join(self.staticdir, '_static', value)
+ '/' + posixpath.join(self.staticroot, '_static', value)
def _make_comment_options(self, username, moderator):
"""Helper method to create the parts of the COMMENT_OPTIONS
@@ -391,8 +417,6 @@ class WebSupport(object):
:param username: The username of the user making the request.
:param moderator: Whether the user making the request is a moderator.
"""
- # XXX parts is not used?
- #parts = [self.base_comment_opts]
rv = self.base_comment_opts.copy()
if username:
rv.update({
diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py
index 53106dfb8..62309ed20 100644
--- a/sphinx/websupport/errors.py
+++ b/sphinx/websupport/errors.py
@@ -9,18 +9,11 @@
:license: BSD, see LICENSE for details.
"""
-__all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError',
- 'UserNotAuthorizedError', 'CommentNotAllowedError',
- 'NullSearchException']
class DocumentNotFoundError(Exception):
pass
-class SrcdirNotSpecifiedError(Exception):
- pass
-
-
class UserNotAuthorizedError(Exception):
pass
diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py
index bb80fb7ee..224329d7e 100644
--- a/sphinx/writers/websupport.py
+++ b/sphinx/writers/websupport.py
@@ -39,7 +39,7 @@ class WebSupportTranslator(HTMLTranslator):
node.attributes['classes'].append(self.comment_class)
def add_db_node(self, node):
- storage = self.builder.app.storage
+ storage = self.builder.storage
if not storage.has_node(node.uid):
storage.add_node(id=node.uid,
document=self.builder.cur_docname,
diff --git a/tests/test_websupport.py b/tests/test_websupport.py
index 65957378b..84d6f9e0f 100644
--- a/tests/test_websupport.py
+++ b/tests/test_websupport.py
@@ -65,7 +65,7 @@ class NullStorage(StorageBackend):
@with_support(storage=NullStorage())
def test_no_srcdir(support):
"""Make sure the correct exception is raised if srcdir is not given."""
- raises(SrcdirNotSpecifiedError, support.build)
+ raises(RuntimeError, support.build)
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')