Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Vastly improved the djangoproject.com doc system; it now draws from S…

…VN directly (i.e. no more need for a hourly cron builder).

git-svn-id: http://code.djangoproject.com/svn/djangoproject.com@4789 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit adb1f18f46a9a36ed5fb0cdd433bb962cb5958f1 1 parent d854495
jacob authored
View
185 django_website/apps/docs/builder.py
@@ -0,0 +1,185 @@
+"""
+Code to do the ReST --> HTML generation for the docs.
+"""
+
+import re
+import compiler
+import smartypants
+from docutils import nodes
+from docutils.core import publish_parts
+from docutils.writers import html4css1
+
+def build_document(text):
+ """
+ Build a doc file into a dict of HTML bits.
+ """
+ return publish_parts(text, writer=DjangoHTMLWriter(), settings_overrides={'initial_header_level': 2})
+
+docstring_re = re.compile(r"([\"']{3})(.*?)(\1)", re.DOTALL|re.MULTILINE)
+def build_model_document(text):
+ """
+ Build a test example into a dict of HTML bits.
+ """
+ # We need to parse the model file without actually executing it.
+ tree = compiler.parse(text)
+
+ # Get the title and blurb from the module's docstring
+ title, blurb = tree.doc.strip().split('\n', 1)
+ parts = publish_parts(blurb, writer=DjangoHTMLWriter(), settings_overrides={'initial_header_level': 2})
+ parts["title"] = title
+
+ # Walk the tree and parse out the bits we care about.
+ visitor = compiler.walk(tree, ModelSourceVistor())
+ parts["api_usage"] = visitor.doctest
+ parts["models"] = visitor.models
+ parts["newstyle"] = visitor.newstyle
+
+ # Parse out the model source.
+ if visitor.newstyle:
+ model_source = text[:text.index("__test__")]
+ else:
+ model_source = text[:text.index("API_TESTS")]
+ parts["model_source"] = model_source.replace(tree.doc, "").replace('""""""\n', '\n').strip()
+
+ return parts
+
+class ModelSourceVistor:
+ """AST visitor for a model module."""
+
+ def __init__(self):
+ self.doctest = ""
+ self.models = []
+ self.newstyle = True
+
+ def visitAssign(self, node):
+ assname, valtree = node.getChildren()
+ if assname.name == "__test__":
+ self.doctest = valtree.getChildren()[1].value
+ elif assname.name == "API_TESTS":
+ self.newstyle = False
+ self.doctest = valtree.value
+
+ def visitClass(self, node):
+ if node.bases and node.bases[0].attrname == "Model":
+ self.models.append(node.name)
+
+class DjangoHTMLWriter(html4css1.Writer):
+ """
+ HTML writer that adds a "toc" key to the set of doc parts.
+ """
+ def __init__(self):
+ html4css1.Writer.__init__(self)
+ self.translator_class = DjangoHTMLTranslator
+
+ def translate(self):
+ # build the document
+ html4css1.Writer.translate(self)
+
+ # build the contents
+ contents = self.build_contents(self.document)
+ contents_doc = self.document.copy()
+ contents_doc.children = contents
+ contents_visitor = self.translator_class(contents_doc)
+ contents_doc.walkabout(contents_visitor)
+ self.parts['toc'] = "<ul class='toc'>%s</ul>" % ''.join(contents_visitor.fragment)
+
+ def build_contents(self, node, level=0):
+ level += 1
+ sections = []
+ i = len(node) - 1
+ while i >= 0 and isinstance(node[i], nodes.section):
+ sections.append(node[i])
+ i -= 1
+ sections.reverse()
+ entries = []
+ autonum = 0
+ depth = 4 # XXX FIXME
+ for section in sections:
+ title = section[0]
+ entrytext = title
+ try:
+ reference = nodes.reference('', '', refid=section['ids'][0], *entrytext)
+ except IndexError:
+ continue
+ ref_id = self.document.set_id(reference)
+ entry = nodes.paragraph('', '', reference)
+ item = nodes.list_item('', entry)
+ if level < depth:
+ subsects = self.build_contents(section, level)
+ item += subsects
+ entries.append(item)
+ if entries:
+ contents = nodes.bullet_list('', *entries)
+ return contents
+ else:
+ return []
+
+class DjangoHTMLTranslator(html4css1.HTMLTranslator):
+ """
+ reST -> HTML translator subclass that fixes up the parts of docutils I don't like.
+ """
+
+ # Prevent name attributes from being generated
+ named_tags = []
+
+ def __init__(self, document):
+ html4css1.HTMLTranslator.__init__(self, document)
+ self._in_literal = 0
+
+ # Remove the default border=1 from <table>
+ def visit_table(self, node):
+ self.body.append(self.starttag(node, 'table', CLASS='docutils'))
+
+ # Prevent <h3> from becoming <h3><a id>
+ #def visit_title(self, node, move_ids=1):
+ # if isinstance(node.parent, nodes.Admonition):
+ # self.body.append(self.starttag(node, 'p', '', CLASS='admonition-title'))
+ # self.context.append("</p>\n")
+ # else:
+ # html4css1.HTMLTranslator.visit_title(self, node, move_ids=0)
+
+ #
+ # Apply smartypants to content when not inside literals
+ #
+ def visit_literal_block(self, node):
+ self._in_literal += 1
+ html4css1.HTMLTranslator.visit_literal_block(self, node)
+
+ def depart_literal_block(self, node):
+ html4css1.HTMLTranslator.depart_literal_block(self, node)
+ self._in_literal -= 1
+
+ def visit_literal(self, node):
+ self._in_literal += 1
+ try:
+ html4css1.HTMLTranslator.visit_literal(self, node)
+ finally:
+ self._in_literal -= 1
+
+ def encode(self, text):
+ text = html4css1.HTMLTranslator.encode(self, text)
+ if self._in_literal <= 0:
+ text = smartypants.smartyPants(text, "qde")
+ return text
+
+ #
+ # Avoid <blockquote>s around merely indented nodes.
+ # Adapted from http://thread.gmane.org/gmane.text.docutils.user/742/focus=804
+ #
+
+ _suppress_blockquote_child_nodes = (
+ nodes.bullet_list, nodes.enumerated_list, nodes.definition_list,
+ nodes.literal_block, nodes.doctest_block, nodes.line_block, nodes.table
+ )
+ def _bq_is_valid(self, node):
+ return len(node.children) != 1 or not isinstance(node.children[0], self._suppress_blockquote_child_nodes)
+
+ def visit_block_quote(self, node):
+ if self._bq_is_valid(node):
+ html4css1.HTMLTranslator.visit_block_quote(self, node)
+
+ def depart_block_quote(self, node):
+ if self._bq_is_valid(node):
+ html4css1.HTMLTranslator.depart_block_quote(self, node)
+
+
View
57 django_website/apps/docs/models.py
@@ -1,50 +1,19 @@
from django.db import models
-class Document(models.Model):
- title = models.CharField(maxlength=200)
- slug = models.CharField(maxlength=50, unique=True, prepopulate_from=('title',))
- doc_path = models.CharField(maxlength=200,
- help_text="Relative to the docs directory in django SVN. Leave off the file extension.")
- last_updated = models.DateTimeField(auto_now=True)
-
+class DocumentRelease(models.Model):
+ version = models.CharField(maxlength=20, unique=True)
+ repository_path = models.CharField(maxlength=50, help_text="(i.e. 'tags/releases/0.95' or 'branches/0.95-bugfixes')")
+ release_date = models.DateField()
+
class Meta:
- db_table = 'docs_documents'
- ordering = ('title',)
-
+ ordering = ('-release_date',)
+
class Admin:
- fields = (
- (None, {'fields': ('title', 'slug', 'doc_path')}),
- )
- list_display = ('title', 'doc_path')
-
+ list_display = ("version", "repository_path", "release_date")
+
def __str__(self):
- return self.title
-
+ return self.version
+
def get_absolute_url(self):
- return "/documentation/%s/" % self.slug
-
- def get_content(self):
- try:
- return self._doc_content
- except AttributeError:
- import os
- from django.conf import settings
- doc_path = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, "%s.html" % self.doc_path)
- if os.path.exists(doc_path):
- self._doc_content = open(doc_path).read()
- else:
- self._doc_content = ''
- return self._doc_content
-
- def get_toc(self):
- try:
- return self._toc_content
- except AttributeError:
- import os
- from django.conf import settings
- toc_path = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, "%s_toc.html" % self.doc_path)
- if os.path.exists(toc_path):
- self._toc_content = open(toc_path).read()
- else:
- self._toc_content = ''
- return self._toc_content
+ return "/documentation/%s/" % (self.version)
+
View
0  django_website/apps/docs/parts/__init__.py
No changes.
View
242 django_website/apps/docs/parts/build_documentation.py
@@ -1,242 +0,0 @@
-"""
-Script to build the documentation for Django from ReST -> HTML.
-
-Builds each text file in sys.argv (or settings.DJANGO_DOCUMENT_ROOT_PATH) into
-two files: a ".html" file with the document contents and a "_toc.html" file
-with the TOC.
-"""
-
-from django.conf import settings
-from django import template
-from django.db.models import get_app, get_models
-from docutils import nodes, utils
-from docutils.core import publish_parts
-from docutils.writers import html4css1
-import glob, inspect, os, re, sys
-
-SETTINGS = {
- 'initial_header_level': 2
-}
-
-MODEL_DOC_TEMPLATE = """
-<div class="document" id="model-{{ model_name }}">
-
-<h1 class="title">{{ title }}</h1>
-{{ blurb }}
-
-<h2 id="model-source-code">Model source code</h2>
-<pre class="literal-block">{{ model_source|escape }}</pre>
-
-<h2 id="sample-usage">Sample API usage</h2>
-<p>This sample code assumes the above model{{ models|pluralize }} {% if models|pluralize %}have{% else %}has{% endif %}
-been saved in a file <tt class="docutils literal"><span class="pre">mysite/models.py</span></tt>.
-<pre class="literal-block">&gt;&gt;&gt; from mysite.models import {% for model in models %}{{ model.name }}{% if not forloop.last %}, {% endif %}{% endfor %}
-{{ api_usage|escape }}</pre>
-</div>
-"""
-
-MODEL_TOC = """
-<ul>
-<li><a href="#model-source-code">Model source code</a></li>
-<li><a href="#api-reference">API reference</a></li>
-<li><a href="#sample-usage">Sample API usage</a></li>
-</ul>
-"""
-
-def build_documents():
- writer = DjangoHTMLWriter()
- for fname in glob.glob1(settings.DJANGO_DOCUMENT_ROOT_PATH, "*.txt"):
- in_file = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, fname)
- out_file = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, os.path.splitext(fname)[0] + ".html")
- toc_file = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, os.path.splitext(fname)[0] + "_toc.html")
- parts = publish_parts(
- open(in_file).read(),
- source_path=in_file,
- destination_path=out_file,
- writer=writer,
- settings_overrides=SETTINGS,
- )
- open(out_file, 'w').write(parts['html_body'])
- open(toc_file, 'w').write(parts['toc'])
-
-def build_test_documents():
- sys.path.insert(0, settings.DJANGO_TESTS_PATH)
- writer = DjangoHTMLWriter()
- import runtests
-
- # An empty access of the settings to force the default options to be
- # installed prior to assigning to them.
- settings.INSTALLED_APPS
-
- # Manually set INSTALLED_APPS to point to the test models.
- test_apps = [runtests.MODEL_TESTS_DIR_NAME + '.' + app for (loc, app) in runtests.get_test_models() if loc != runtests.REGRESSION_TESTS_DIR_NAME]
- settings.INSTALLED_APPS = runtests.ALWAYS_INSTALLED_APPS + test_apps
-
- # Some of the test models need to know whether the docs are being built.
- settings.BUILDING_DOCS = True
-
- for app_name in test_apps:
- model_name = app_name.split(".")[-1]
- mod = get_app(model_name)
-
- out_file = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, 'model_' + model_name + '.html')
- toc_file = os.path.join(settings.DJANGO_DOCUMENT_ROOT_PATH, 'model_' + model_name + '_toc.html')
-
- # Clean up the title and blurb.
- try:
- title, blurb = mod.__doc__.strip().split('\n', 1)
- except ValueError:
- sys.stderr.write("title and blurb not found in %s model test.\n" % model_name)
- continue
- parts = publish_parts(
- blurb,
- source_path=mod.__file__,
- destination_path=out_file,
- writer=writer,
- settings_overrides=SETTINGS,
- )
- blurb = parts["html_body"]
- try:
- api_usage = mod.API_TESTS
- except AttributeError:
- continue # This model didn't have API_TESTS.
-
- # Get the source code of the model, without the docstring or the
- # API_TESTS variable.
- model_source = inspect.getsource(mod)
- model_source = model_source.replace(mod.__doc__, '')
- model_source = model_source.replace(mod.API_TESTS, '')
- model_source = model_source.replace('""""""\n', '\n')
- model_source = re.sub(r'(?s)API_TESTS = .*', '', model_source)
- model_source = model_source.strip()
-
- models = []
- for m in get_models(mod):
- models.append({
- 'name': m._meta.object_name,
- 'module_name': m._meta.module_name,
- })
-
- # Run this through the template system.
- t = template.Template(MODEL_DOC_TEMPLATE)
- c = template.Context(locals())
- html = t.render(c)
-
- try:
- fp = open(out_file, 'w')
- except IOError:
- sys.stderr.write("Couldn't write to %s.\n" % out_file)
- continue
- else:
- fp.write(html)
- fp.close()
-
- try:
- fp = open(toc_file, 'w')
- except IOError:
- sys.stderr.write("Couldn't write to %s.\n" % toc_file)
- continue
- else:
- fp.write(MODEL_TOC)
- fp.close()
-
-class DjangoHTMLWriter(html4css1.Writer):
- def __init__(self):
- html4css1.Writer.__init__(self)
- self.translator_class = DjangoHTMLTranslator
-
- def translate(self):
- # build the document
- html4css1.Writer.translate(self)
-
- # build the contents
- contents = self.build_contents(self.document)
- contents_doc = self.document.copy()
- contents_doc.children = contents
- contents_visitor = self.translator_class(contents_doc)
- contents_doc.walkabout(contents_visitor)
- self.parts['toc'] = "<ul class='toc'>%s</ul>" % ''.join(contents_visitor.fragment)
-
- def build_contents(self, node, level=0):
- level += 1
- sections = []
- i = len(node) - 1
- while i >= 0 and isinstance(node[i], nodes.section):
- sections.append(node[i])
- i -= 1
- sections.reverse()
- entries = []
- autonum = 0
- depth = 4 # XXX FIXME
- for section in sections:
- title = section[0]
- entrytext = title
- try:
- reference = nodes.reference('', '', refid=section['ids'][0], *entrytext)
- except IndexError:
- continue
- ref_id = self.document.set_id(reference)
- entry = nodes.paragraph('', '', reference)
- item = nodes.list_item('', entry)
- if level < depth:
- subsects = self.build_contents(section, level)
- item += subsects
- entries.append(item)
- if entries:
- contents = nodes.bullet_list('', *entries)
- return contents
- else:
- return []
-
-class DjangoHTMLTranslator(html4css1.HTMLTranslator):
- def visit_table(self, node):
- """Remove the damn border=1 from the standard HTML writer"""
- self.body.append(self.starttag(node, 'table', CLASS='docutils'))
-
- def visit_title(self, node):
- """Coppied from html4css1.Writer wholesale just to get rid of the <a name=> crap. Fun, eh?"""
- check_id = 0
- close_tag = '</p>\n'
- if isinstance(node.parent, nodes.topic):
- self.body.append(
- self.starttag(node, 'p', '', CLASS='topic-title first'))
- check_id = 1
- elif isinstance(node.parent, nodes.sidebar):
- self.body.append(
- self.starttag(node, 'p', '', CLASS='sidebar-title'))
- check_id = 1
- elif isinstance(node.parent, nodes.Admonition):
- self.body.append(
- self.starttag(node, 'p', '', CLASS='admonition-title'))
- check_id = 1
- elif isinstance(node.parent, nodes.table):
- self.body.append(
- self.starttag(node, 'caption', ''))
- check_id = 1
- close_tag = '</caption>\n'
- elif isinstance(node.parent, nodes.document):
- self.body.append(self.starttag(node, 'h1', '', CLASS='title'))
- self.context.append('</h1>\n<h2 class="deck">This covers Django version 0.95 and the development version. Old docs: <a href="/documentation/0_90/">0.90</a>, <a href="/documentation/0_91/">0.91</a></h2>\n')
- self.in_document_title = len(self.body)
- else:
- assert isinstance(node.parent, nodes.section)
- h_level = self.section_level + self.initial_header_level - 1
- atts = {}
- if (len(node.parent) >= 2 and
- isinstance(node.parent[1], nodes.subtitle)):
- atts['CLASS'] = 'with-subtitle'
- node.ids = node.parent['ids']
- self.body.append(self.starttag(node, 'h%s' % h_level, '', **atts))
- self.context.append('</h%s>\n' % (h_level))
- if check_id:
- if node.parent['ids']:
- self.body.append(
- self.starttag({}, 'a', '', name=node.parent['ids'][0]))
- self.context.append('</a>' + close_tag)
- else:
- self.context.append(close_tag)
-
-
-if __name__ == "__main__":
- build_documents()
- build_test_documents()
View
20 django_website/apps/docs/parts/flatten_docs.py
@@ -1,20 +0,0 @@
-"""
-Utility that converts all current Django documents to flat pages.
-
-We use this to flatten/freeze the current Django documentation for
-a particular version.
-"""
-
-from django_website.apps.docs.models import Document
-from django.contrib.flatpages.models import FlatPage
-
-for doc in Document.objects.all():
- f = FlatPage(
- url='/documentation/0_91/%s/' % doc.slug,
- title='Documentation (version 0.91) | %s' % doc.title,
- content='%s</div><div id="content-related" class="sidebar"><h2>Contents</h2>%s' % (doc.get_content(), doc.get_toc()),
- enable_comments=False,
- template_name='flatfiles/legacy_docs',
- registration_required=False)
- f.save()
- f.site_set = [1]
View
879 django_website/apps/docs/smartypants.py
@@ -0,0 +1,879 @@
+r"""
+==============
+smartypants.py
+==============
+
+----------------------------
+SmartyPants ported to Python
+----------------------------
+
+Ported by `Chad Miller`_
+Copyright (c) 2004 Chad Miller
+
+original `SmartyPants`_ by `John Gruber`_
+Copyright (c) 2003 John Gruber
+
+
+Synopsis
+========
+
+A smart-quotes plugin for Pyblosxom_.
+
+The priginal "SmartyPants" is a free web publishing plug-in for Movable Type,
+Blosxom, and BBEdit that easily translates plain ASCII punctuation characters
+into "smart" typographic punctuation HTML entities.
+
+This software, *smartypants.py*, endeavours to be a functional port of
+SmartyPants to Python, for use with Pyblosxom_.
+
+
+Description
+===========
+
+SmartyPants can perform the following transformations:
+
+- Straight quotes ( " and ' ) into "curly" quote HTML entities
+- Backticks-style quotes (\`\`like this'') into "curly" quote HTML entities
+- Dashes (``--`` and ``---``) into en- and em-dash entities
+- Three consecutive dots (``...`` or ``. . .``) into an ellipsis entity
+
+This means you can write, edit, and save your posts using plain old
+ASCII straight quotes, plain dashes, and plain dots, but your published
+posts (and final HTML output) will appear with smart quotes, em-dashes,
+and proper ellipses.
+
+SmartyPants does not modify characters within ``<pre>``, ``<code>``, ``<kbd>``,
+``<math>`` or ``<script>`` tag blocks. Typically, these tags are used to
+display text where smart quotes and other "smart punctuation" would not be
+appropriate, such as source code or example markup.
+
+
+Backslash Escapes
+=================
+
+If you need to use literal straight quotes (or plain hyphens and
+periods), SmartyPants accepts the following backslash escape sequences
+to force non-smart punctuation. It does so by transforming the escape
+sequence into a decimal-encoded HTML entity:
+
+(FIXME: table here.)
+
+.. comment It sucks that there's a disconnect between the visual layout and table markup when special characters are involved.
+.. comment ====== ===== =========
+.. comment Escape Value Character
+.. comment ====== ===== =========
+.. comment \\\\\\\\ &#92; \\\\
+.. comment \\\\" &#34; "
+.. comment \\\\' &#39; '
+.. comment \\\\. &#46; .
+.. comment \\\\- &#45; \-
+.. comment \\\\` &#96; \`
+.. comment ====== ===== =========
+
+This is useful, for example, when you want to use straight quotes as
+foot and inch marks: 6'2" tall; a 17" iMac.
+
+Options
+=======
+
+For Pyblosxom users, the ``smartypants_attributes`` attribute is where you
+specify configuration options.
+
+Numeric values are the easiest way to configure SmartyPants' behavior:
+
+"0"
+ Suppress all transformations. (Do nothing.)
+"1"
+ Performs default SmartyPants transformations: quotes (including
+ \`\`backticks'' -style), em-dashes, and ellipses. "``--``" (dash dash)
+ is used to signify an em-dash; there is no support for en-dashes.
+
+"2"
+ Same as smarty_pants="1", except that it uses the old-school typewriter
+ shorthand for dashes: "``--``" (dash dash) for en-dashes, "``---``"
+ (dash dash dash)
+ for em-dashes.
+
+"3"
+ Same as smarty_pants="2", but inverts the shorthand for dashes:
+ "``--``" (dash dash) for em-dashes, and "``---``" (dash dash dash) for
+ en-dashes.
+
+"-1"
+ Stupefy mode. Reverses the SmartyPants transformation process, turning
+ the HTML entities produced by SmartyPants into their ASCII equivalents.
+ E.g. "&#8220;" is turned into a simple double-quote ("), "&#8212;" is
+ turned into two dashes, etc.
+
+
+The following single-character attribute values can be combined to toggle
+individual transformations from within the smarty_pants attribute. For
+example, to educate normal quotes and em-dashes, but not ellipses or
+\`\`backticks'' -style quotes:
+
+``py['smartypants_attributes'] = "1"``
+
+"q"
+ Educates normal quote characters: (") and (').
+
+"b"
+ Educates \`\`backticks'' -style double quotes.
+
+"B"
+ Educates \`\`backticks'' -style double quotes and \`single' quotes.
+
+"d"
+ Educates em-dashes.
+
+"D"
+ Educates em-dashes and en-dashes, using old-school typewriter shorthand:
+ (dash dash) for en-dashes, (dash dash dash) for em-dashes.
+
+"i"
+ Educates em-dashes and en-dashes, using inverted old-school typewriter
+ shorthand: (dash dash) for em-dashes, (dash dash dash) for en-dashes.
+
+"e"
+ Educates ellipses.
+
+"w"
+ Translates any instance of ``&quot;`` into a normal double-quote character.
+ This should be of no interest to most people, but of particular interest
+ to anyone who writes their posts using Dreamweaver, as Dreamweaver
+ inexplicably uses this entity to represent a literal double-quote
+ character. SmartyPants only educates normal quotes, not entities (because
+ ordinarily, entities are used for the explicit purpose of representing the
+ specific character they represent). The "w" option must be used in
+ conjunction with one (or both) of the other quote options ("q" or "b").
+ Thus, if you wish to apply all SmartyPants transformations (quotes, en-
+ and em-dashes, and ellipses) and also translate ``&quot;`` entities into
+ regular quotes so SmartyPants can educate them, you should pass the
+ following to the smarty_pants attribute:
+
+The ``smartypants_forbidden_flavours`` list contains pyblosxom flavours for
+which no Smarty Pants rendering will occur.
+
+
+Caveats
+=======
+
+Why You Might Not Want to Use Smart Quotes in Your Weblog
+---------------------------------------------------------
+
+For one thing, you might not care.
+
+Most normal, mentally stable individuals do not take notice of proper
+typographic punctuation. Many design and typography nerds, however, break
+out in a nasty rash when they encounter, say, a restaurant sign that uses
+a straight apostrophe to spell "Joe's".
+
+If you're the sort of person who just doesn't care, you might well want to
+continue not caring. Using straight quotes -- and sticking to the 7-bit
+ASCII character set in general -- is certainly a simpler way to live.
+
+Even if you I *do* care about accurate typography, you still might want to
+think twice before educating the quote characters in your weblog. One side
+effect of publishing curly quote HTML entities is that it makes your
+weblog a bit harder for others to quote from using copy-and-paste. What
+happens is that when someone copies text from your blog, the copied text
+contains the 8-bit curly quote characters (as well as the 8-bit characters
+for em-dashes and ellipses, if you use these options). These characters
+are not standard across different text encoding methods, which is why they
+need to be encoded as HTML entities.
+
+People copying text from your weblog, however, may not notice that you're
+using curly quotes, and they'll go ahead and paste the unencoded 8-bit
+characters copied from their browser into an email message or their own
+weblog. When pasted as raw "smart quotes", these characters are likely to
+get mangled beyond recognition.
+
+That said, my own opinion is that any decent text editor or email client
+makes it easy to stupefy smart quote characters into their 7-bit
+equivalents, and I don't consider it my problem if you're using an
+indecent text editor or email client.
+
+
+Algorithmic Shortcomings
+------------------------
+
+One situation in which quotes will get curled the wrong way is when
+apostrophes are used at the start of leading contractions. For example:
+
+``'Twas the night before Christmas.``
+
+In the case above, SmartyPants will turn the apostrophe into an opening
+single-quote, when in fact it should be a closing one. I don't think
+this problem can be solved in the general case -- every word processor
+I've tried gets this wrong as well. In such cases, it's best to use the
+proper HTML entity for closing single-quotes (``&#8217;``) by hand.
+
+
+Bugs
+====
+
+To file bug reports or feature requests (other than topics listed in the
+Caveats section above) please send email to: mailto:smartypantspy@chad.org
+
+If the bug involves quotes being curled the wrong way, please send example
+text to illustrate.
+
+To Do list
+----------
+
+- Provide a function for use within templates to quote anything at all.
+
+
+Version History
+===============
+
+1.5_1.5: Sat, 13 Aug 2005 15:50:24 -0400
+ - Fix bogus magical quotation when there is no hint that the
+ user wants it, e.g., in "21st century". Thanks to Nathan Hamblen.
+ - Be smarter about quotes before terminating numbers in an en-dash'ed
+ range.
+
+1.5_1.4: Thu, 10 Feb 2005 20:24:36 -0500
+ - Fix a date-processing bug, as reported by jacob childress.
+ - Begin a test-suite for ensuring correct output.
+ - Removed import of "string", since I didn't really need it.
+ (This was my first every Python program. Sue me!)
+
+1.5_1.3: Wed, 15 Sep 2004 18:25:58 -0400
+ - Abort processing if the flavour is in forbidden-list. Default of
+ [ "rss" ] (Idea of Wolfgang SCHNERRING.)
+ - Remove stray virgules from en-dashes. Patch by Wolfgang SCHNERRING.
+
+1.5_1.2: Mon, 24 May 2004 08:14:54 -0400
+ - Some single quotes weren't replaced properly. Diff-tesuji played
+ by Benjamin GEIGER.
+
+1.5_1.1: Sun, 14 Mar 2004 14:38:28 -0500
+ - Support upcoming pyblosxom 0.9 plugin verification feature.
+
+1.5_1.0: Tue, 09 Mar 2004 08:08:35 -0500
+ - Initial release
+
+Version Information
+-------------------
+
+Version numbers will track the SmartyPants_ version numbers, with the addition
+of an underscore and the smartypants.py version on the end.
+
+New versions will be available at `http://wiki.chad.org/SmartyPantsPy`_
+
+.. _http://wiki.chad.org/SmartyPantsPy: http://wiki.chad.org/SmartyPantsPy
+
+Authors
+=======
+
+`John Gruber`_ did all of the hard work of writing this software in Perl for
+`Movable Type`_ and almost all of this useful documentation. `Chad Miller`_
+ported it to Python to use with Pyblosxom_.
+
+
+Additional Credits
+==================
+
+Portions of the SmartyPants original work are based on Brad Choate's nifty
+MTRegex plug-in. `Brad Choate`_ also contributed a few bits of source code to
+this plug-in. Brad Choate is a fine hacker indeed.
+
+`Jeremy Hedley`_ and `Charles Wiltgen`_ deserve mention for exemplary beta
+testing of the original SmartyPants.
+
+`Rael Dornfest`_ ported SmartyPants to Blosxom.
+
+.. _Brad Choate: http://bradchoate.com/
+.. _Jeremy Hedley: http://antipixel.com/
+.. _Charles Wiltgen: http://playbacktime.com/
+.. _Rael Dornfest: http://raelity.org/
+
+
+Copyright and License
+=====================
+
+SmartyPants_ license::
+
+ Copyright (c) 2003 John Gruber
+ (http://daringfireball.net/)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name "SmartyPants" nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ This software is provided by the copyright holders and contributors "as
+ is" and any express or implied warranties, including, but not limited
+ to, the implied warranties of merchantability and fitness for a
+ particular purpose are disclaimed. In no event shall the copyright
+ owner or contributors be liable for any direct, indirect, incidental,
+ special, exemplary, or consequential damages (including, but not
+ limited to, procurement of substitute goods or services; loss of use,
+ data, or profits; or business interruption) however caused and on any
+ theory of liability, whether in contract, strict liability, or tort
+ (including negligence or otherwise) arising in any way out of the use
+ of this software, even if advised of the possibility of such damage.
+
+
+smartypants.py license::
+
+ smartypants.py is a derivative work of SmartyPants.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ This software is provided by the copyright holders and contributors "as
+ is" and any express or implied warranties, including, but not limited
+ to, the implied warranties of merchantability and fitness for a
+ particular purpose are disclaimed. In no event shall the copyright
+ owner or contributors be liable for any direct, indirect, incidental,
+ special, exemplary, or consequential damages (including, but not
+ limited to, procurement of substitute goods or services; loss of use,
+ data, or profits; or business interruption) however caused and on any
+ theory of liability, whether in contract, strict liability, or tort
+ (including negligence or otherwise) arising in any way out of the use
+ of this software, even if advised of the possibility of such damage.
+
+
+
+.. _John Gruber: http://daringfireball.net/
+.. _Chad Miller: http://web.chad.org/
+
+.. _Pyblosxom: http://roughingit.subtlehints.net/pyblosxom
+.. _SmartyPants: http://daringfireball.net/projects/smartypants/
+.. _Movable Type: http://www.movabletype.org/
+
+"""
+
+default_smartypants_attr = "1"
+
+import re
+
+tags_to_skip_regex = re.compile("<(/)?(?:pre|code|kbd|script|math)[^>]*>")
+
+
+def verify_installation(request):
+ return 1
+ # assert the plugin is functional
+
+
+def cb_story(args):
+ global default_smartypants_attr
+
+ try:
+ forbidden_flavours = args["entry"]["smartypants_forbidden_flavours"]
+ except KeyError:
+ forbidden_flavours = [ "rss" ]
+
+ try:
+ attributes = args["entry"]["smartypants_attributes"]
+ except KeyError:
+ attributes = default_smartypants_attr
+
+ if attributes is None:
+ attributes = default_smartypants_attr
+
+ entryData = args["entry"].getData()
+
+ try:
+ if args["request"]["flavour"] in forbidden_flavours:
+ return
+ except KeyError:
+ if "&lt;" in args["entry"]["body"][0:15]: # sniff the stream
+ return # abort if it looks like escaped HTML. FIXME
+
+ # FIXME: make these configurable, perhaps?
+ args["entry"]["body"] = smartyPants(entryData, attributes)
+ args["entry"]["title"] = smartyPants(args["entry"]["title"], attributes)
+
+
+### interal functions below here
+
+def smartyPants(text, attr=default_smartypants_attr):
+ convert_quot = False # should we translate &quot; entities into normal quotes?
+
+ # Parse attributes:
+ # 0 : do nothing
+ # 1 : set all
+ # 2 : set all, using old school en- and em- dash shortcuts
+ # 3 : set all, using inverted old school en and em- dash shortcuts
+ #
+ # q : quotes
+ # b : backtick quotes (``double'' only)
+ # B : backtick quotes (``double'' and `single')
+ # d : dashes
+ # D : old school dashes
+ # i : inverted old school dashes
+ # e : ellipses
+ # w : convert &quot; entities to " for Dreamweaver users
+
+ do_dashes = "0"
+ do_backticks = "0"
+ do_quotes = "0"
+ do_ellipses = "0"
+ do_stupefy = "0"
+
+ if attr == "0":
+ # Do nothing.
+ return text
+ elif attr == "1":
+ do_quotes = "1"
+ do_backticks = "1"
+ do_dashes = "1"
+ do_ellipses = "1"
+ elif attr == "2":
+ # Do everything, turn all options on, use old school dash shorthand.
+ do_quotes = "1"
+ do_backticks = "1"
+ do_dashes = "2"
+ do_ellipses = "1"
+ elif attr == "3":
+ # Do everything, turn all options on, use inverted old school dash shorthand.
+ do_quotes = "1"
+ do_backticks = "1"
+ do_dashes = "3"
+ do_ellipses = "1"
+ elif attr == "-1":
+ # Special "stupefy" mode.
+ do_stupefy = "1"
+ else:
+ for c in attr:
+ if c == "q": do_quotes = "1"
+ elif c == "b": do_backticks = "1"
+ elif c == "B": do_backticks = "2"
+ elif c == "d": do_dashes = "1"
+ elif c == "D": do_dashes = "2"
+ elif c == "i": do_dashes = "3"
+ elif c == "e": do_ellipses = "1"
+ elif c == "w": convert_quot = "1"
+ else:
+ pass
+ # ignore unknown option
+
+ tokens = _tokenize(text)
+ result = []
+ in_pre = False
+
+ prev_token_last_char = ""
+ # This is a cheat, used to get some context
+ # for one-character tokens that consist of
+ # just a quote char. What we do is remember
+ # the last character of the previous text
+ # token, to use as context to curl single-
+ # character quote tokens correctly.
+
+ for cur_token in tokens:
+ if cur_token[0] == "tag":
+ # Don't mess with quotes inside tags.
+ result.append(cur_token[1])
+ close_match = tags_to_skip_regex.match(cur_token[1])
+ # if close_match is not None and close_match.group(1) == "":
+ if close_match is not None:
+ in_pre = True
+ else:
+ in_pre = False
+ else:
+ t = cur_token[1]
+ last_char = t[-1:] # Remember last char of this token before processing.
+ if not in_pre:
+ oldstr = t
+ t = processEscapes(t)
+
+ if convert_quot != "0":
+ t = re.sub('&quot;', '"', t)
+
+ if do_dashes != "0":
+ if do_dashes == "1":
+ t = educateDashes(t)
+ if do_dashes == "2":
+ t = educateDashesOldSchool(t)
+ if do_dashes == "3":
+ t = educateDashesOldSchoolInverted(t)
+
+ if do_ellipses != "0":
+ t = educateEllipses(t)
+
+ # Note: backticks need to be processed before quotes.
+ if do_backticks != "0":
+ t = educateBackticks(t)
+
+ if do_backticks == "2":
+ t = educateSingleBackticks(t)
+
+ if do_quotes != "0":
+ if t == "'":
+ # Special case: single-character ' token
+ if re.match("\S", prev_token_last_char):
+ t = "&#8217;"
+ else:
+ t = "&#8216;"
+ elif t == '"':
+ # Special case: single-character " token
+ if re.match("\S", prev_token_last_char):
+ t = "&#8221;"
+ else:
+ t = "&#8220;"
+
+ else:
+ # Normal case:
+ t = educateQuotes(t)
+
+ if do_stupefy == "1":
+ t = stupefyEntities(t)
+
+ prev_token_last_char = last_char
+ result.append(t)
+
+ return "".join(result)
+
+
+def educateQuotes(str):
+ """
+ Parameter: String.
+
+ Returns: The string, with "educated" curly quote HTML entities.
+
+ Example input: "Isn't this fun?"
+ Example output: &#8220;Isn&#8217;t this fun?&#8221;
+ """
+
+ oldstr = str
+ punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
+
+ # Special case if the very first character is a quote
+ # followed by punctuation at a non-word-break. Close the quotes by brute force:
+ str = re.sub(r"""^'(?=%s\\B)""" % (punct_class,), r"""&#8217;""", str)
+ str = re.sub(r"""^"(?=%s\\B)""" % (punct_class,), r"""&#8221;""", str)
+
+ # Special case for double sets of quotes, e.g.:
+ # <p>He said, "'Quoted' words in a larger quote."</p>
+ str = re.sub(r""""'(?=\w)""", """&#8220;&#8216;""", str)
+ str = re.sub(r"""'"(?=\w)""", """&#8216;&#8220;""", str)
+
+ # Special case for decade abbreviations (the '80s):
+ str = re.sub(r"""\b'(?=\d{2}s)""", r"""&#8217;""", str)
+
+ close_class = r"""[^\ \t\r\n\[\{\(\-]"""
+ dec_dashes = r"""&#8211;|&#8212;"""
+
+ # Get most opening single quotes:
+ opening_single_quotes_regex = re.compile(r"""
+ (
+ \s | # a whitespace char, or
+ &nbsp; | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ %s | # or decimal entities
+ &\#x201[34]; # or hex
+ )
+ ' # the quote
+ (?=\w) # followed by a word character
+ """ % (dec_dashes,), re.VERBOSE)
+ str = opening_single_quotes_regex.sub(r"""\1&#8216;""", str)
+
+ closing_single_quotes_regex = re.compile(r"""
+ (%s)
+ '
+ (?!\s | s\b | \d)
+ """ % (close_class,), re.VERBOSE)
+ str = closing_single_quotes_regex.sub(r"""\1&#8217;""", str)
+
+ closing_single_quotes_regex = re.compile(r"""
+ (%s)
+ '
+ (\s | s\b)
+ """ % (close_class,), re.VERBOSE)
+ str = closing_single_quotes_regex.sub(r"""\1&#8217;\2""", str)
+
+ # Any remaining single quotes should be opening ones:
+ str = re.sub(r"""'""", r"""&#8216;""", str)
+
+ # Get most opening double quotes:
+ opening_double_quotes_regex = re.compile(r"""
+ (
+ \s | # a whitespace char, or
+ &nbsp; | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ %s | # or decimal entities
+ &\#x201[34]; # or hex
+ )
+ " # the quote
+ (?=\w) # followed by a word character
+ """ % (dec_dashes,), re.VERBOSE)
+ str = opening_double_quotes_regex.sub(r"""\1&#8220;""", str)
+
+ # Double closing quotes:
+ closing_double_quotes_regex = re.compile(r"""
+ #(%s)? # character that indicates the quote should be closing
+ "
+ (?=\s)
+ """ % (close_class,), re.VERBOSE)
+ str = closing_double_quotes_regex.sub(r"""&#8221;""", str)
+
+ closing_double_quotes_regex = re.compile(r"""
+ (%s) # character that indicates the quote should be closing
+ "
+ """ % (close_class,), re.VERBOSE)
+ str = closing_double_quotes_regex.sub(r"""\1&#8221;""", str)
+
+ # Any remaining quotes should be opening ones.
+ str = re.sub(r'"', r"""&#8220;""", str)
+
+ return str
+
+
+def educateBackticks(str):
+ """
+ Parameter: String.
+ Returns: The string, with ``backticks'' -style double quotes
+ translated into HTML curly quote entities.
+ Example input: ``Isn't this fun?''
+ Example output: &#8220;Isn't this fun?&#8221;
+ """
+
+ str = re.sub(r"""``""", r"""&#8220;""", str)
+ str = re.sub(r"""''""", r"""&#8221;""", str)
+ return str
+
+
+def educateSingleBackticks(str):
+ """
+ Parameter: String.
+ Returns: The string, with `backticks' -style single quotes
+ translated into HTML curly quote entities.
+
+ Example input: `Isn't this fun?'
+ Example output: &#8216;Isn&#8217;t this fun?&#8217;
+ """
+
+ str = re.sub(r"""`""", r"""&#8216;""", str)
+ str = re.sub(r"""'""", r"""&#8217;""", str)
+ return str
+
+
+def educateDashes(str):
+ """
+ Parameter: String.
+
+ Returns: The string, with each instance of "--" translated to
+ an em-dash HTML entity.
+ """
+
+ str = re.sub(r"""---""", r"""&#8211;""", str) # en (yes, backwards)
+ str = re.sub(r"""--""", r"""&#8212;""", str) # em (yes, backwards)
+ return str
+
+
+def educateDashesOldSchool(str):
+ """
+ Parameter: String.
+
+ Returns: The string, with each instance of "--" translated to
+ an en-dash HTML entity, and each "---" translated to
+ an em-dash HTML entity.
+ """
+
+ str = re.sub(r"""---""", r"""&#8212;""", str) # em (yes, backwards)
+ str = re.sub(r"""--""", r"""&#8211;""", str) # en (yes, backwards)
+ return str
+
+
+def educateDashesOldSchoolInverted(str):
+ """
+ Parameter: String.
+
+ Returns: The string, with each instance of "--" translated to
+ an em-dash HTML entity, and each "---" translated to
+ an en-dash HTML entity. Two reasons why: First, unlike the
+ en- and em-dash syntax supported by
+ EducateDashesOldSchool(), it's compatible with existing
+ entries written before SmartyPants 1.1, back when "--" was
+ only used for em-dashes. Second, em-dashes are more
+ common than en-dashes, and so it sort of makes sense that
+ the shortcut should be shorter to type. (Thanks to Aaron
+ Swartz for the idea.)
+ """
+ str = re.sub(r"""---""", r"""&#8211;""", str) # em
+ str = re.sub(r"""--""", r"""&#8212;""", str) # en
+ return str
+
+
+
+def educateEllipses(str):
+ """
+ Parameter: String.
+ Returns: The string, with each instance of "..." translated to
+ an ellipsis HTML entity.
+
+ Example input: Huh...?
+ Example output: Huh&#8230;?
+ """
+
+ str = re.sub(r"""\.\.\.""", r"""&#8230;""", str)
+ str = re.sub(r"""\. \. \.""", r"""&#8230;""", str)
+ return str
+
+
+def stupefyEntities(str):
+ """
+ Parameter: String.
+ Returns: The string, with each SmartyPants HTML entity translated to
+ its ASCII counterpart.
+
+ Example input: &#8220;Hello &#8212; world.&#8221;
+ Example output: "Hello -- world."
+ """
+
+ str = re.sub(r"""&#8211;""", r"""-""", str) # en-dash
+ str = re.sub(r"""&#8212;""", r"""--""", str) # em-dash
+
+ str = re.sub(r"""&#8216;""", r"""'""", str) # open single quote
+ str = re.sub(r"""&#8217;""", r"""'""", str) # close single quote
+
+ str = re.sub(r"""&#8220;""", r'''"''', str) # open double quote
+ str = re.sub(r"""&#8221;""", r'''"''', str) # close double quote
+
+ str = re.sub(r"""&#8230;""", r"""...""", str)# ellipsis
+
+ return str
+
+
+def processEscapes(str):
+ r"""
+ Parameter: String.
+ Returns: The string, with after processing the following backslash
+ escape sequences. This is useful if you want to force a "dumb"
+ quote or other character to appear.
+
+ Escape Value
+ ------ -----
+ \\ &#92;
+ \" &#34;
+ \' &#39;
+ \. &#46;
+ \- &#45;
+ \` &#96;
+ """
+ str = re.sub(r"""\\\\""", r"""&#92;""", str)
+ str = re.sub(r'''\\"''', r"""&#34;""", str)
+ str = re.sub(r"""\\'""", r"""&#39;""", str)
+ str = re.sub(r"""\\\.""", r"""&#46;""", str)
+ str = re.sub(r"""\\-""", r"""&#45;""", str)
+ str = re.sub(r"""\\`""", r"""&#96;""", str)
+
+ return str
+
+
+def _tokenize(str):
+ """
+ Parameter: String containing HTML markup.
+ Returns: Reference to an array of the tokens comprising the input
+ string. Each token is either a tag (possibly with nested,
+ tags contained therein, such as <a href="<MTFoo>">, or a
+ run of text between tags. Each element of the array is a
+ two-element array; the first is either 'tag' or 'text';
+ the second is the actual value.
+
+ Based on the _tokenize() subroutine from Brad Choate's MTRegex plugin.
+ <http://www.bradchoate.com/past/mtregex.php>
+ """
+
+ pos = 0
+ length = len(str)
+ tokens = []
+
+ depth = 6
+ nested_tags = "|".join(['(?:<(?:[^<>]',] * depth) + (')*>)' * depth)
+ #match = r"""(?: <! ( -- .*? -- \s* )+ > ) | # comments
+ # (?: <\? .*? \?> ) | # directives
+ # %s # nested tags """ % (nested_tags,)
+ tag_soup = re.compile(r"""([^<]*)(<[^>]*>)""")
+
+ token_match = tag_soup.search(str)
+
+ previous_end = 0
+ while token_match is not None:
+ if token_match.group(1) != "":
+ tokens.append(['text', token_match.group(1)])
+
+ tokens.append(['tag', token_match.group(2)])
+
+ previous_end = token_match.end()
+ token_match = tag_soup.search(str, token_match.end())
+
+ if previous_end < len(str):
+ tokens.append(['text', str[previous_end:]])
+
+ return tokens
+
+
+
+if __name__ == "__main__":
+
+ import locale
+
+ try:
+ locale.setlocale(locale.LC_ALL, '')
+ except:
+ pass
+
+ from docutils.core import publish_string
+ docstring_html = publish_string(__doc__, writer_name='html')
+
+ print docstring_html
+
+
+ # Unit test output goes out stderr. No worries.
+ import unittest
+ sp = smartyPants
+
+ class TestSmartypantsAllAttributes(unittest.TestCase):
+ # the default attribute is "1", which means "all".
+
+ def test_dates(self):
+ self.assertEqual(sp("1440-80's"), "1440-80&#8217;s")
+ self.assertEqual(sp("1440-'80s"), "1440-&#8216;80s")
+ self.assertEqual(sp("1440---'80s"), "1440&#8211;&#8216;80s")
+ self.assertEqual(sp("1960s"), "1960s") # no effect.
+ self.assertEqual(sp("1960's"), "1960&#8217;s")
+ self.assertEqual(sp("one two '60s"), "one two &#8216;60s")
+ self.assertEqual(sp("'60s"), "&#8216;60s")
+
+ def test_ordinal_numbers(self):
+ self.assertEqual(sp("21st century"), "21st century") # no effect.
+ self.assertEqual(sp("3rd"), "3rd") # no effect.
+
+ def test_educated_quotes(self):
+ self.assertEqual(sp('''"Isn't this fun?"'''), '''&#8220;Isn&#8217;t this fun?&#8221;''')
+
+ unittest.main()
+
+
+
+
+__author__ = "Chad Miller <smartypantspy@chad.org>"
+__version__ = "1.5_1.5: Sat, 13 Aug 2005 15:50:24 -0400"
+__url__ = "http://wiki.chad.org/SmartyPantsPy"
+__description__ = "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries in pyblosxom"
View
19 django_website/apps/docs/urls.py
@@ -1,11 +1,12 @@
from django.conf.urls.defaults import *
-from models import Document # relative import
-info_dict = {
- 'queryset': Document.objects.all(),
- 'slug_field': 'slug',
-}
-
-urlpatterns = patterns('django.views.generic.list_detail',
- (r'^(?P<slug>[\w\/]+)/$', 'object_detail', info_dict),
-)
+urlpatterns = patterns('django_website.apps.docs.views',
+ (r'^$', 'doc_index'),
+ (r'^(?P<version>[\d.]+)/$', 'doc_index'),
+ (r'^models/$', 'model_index'),
+ (r'^models/(?P<slug>\w+)/$', 'model_detail'),
+ (r'^(?P<version>[\d.]+)/models/$', 'model_index'),
+ (r'^(?P<version>[\d.]+)/models/(?P<slug>\w+)/$', 'model_detail'),
+ (r'^(?P<slug>[\w-]+)/$', 'doc_detail'),
+ (r'^(?P<version>[\d.]+)/(?P<slug>[\w-]+)/$', 'doc_detail'),
+)
View
106 django_website/apps/docs/views.py
@@ -0,0 +1,106 @@
+import os
+import re
+import urlparse
+from django.conf import settings
+from django.core.cache import cache
+from django.http import Http404, HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render_to_response
+from django.template import RequestContext
+from django_website.apps.docs.models import DocumentRelease
+from django_website.apps.docs import builder
+import pysvn
+
+def doc_index(request, version=None):
+ client, version, docroot = _get_svnroot(version, "docs/")
+ doclist = client.ls(docroot, recurse=False)
+
+ # Convert list of URLs to list of document slugs.
+ doclist = [os.path.splitext(os.path.basename(doc.name))[0] for doc in doclist]
+ doclist.sort()
+
+ return render_to_response(
+ ["docs/%s_index.html" % version, "docs/index.html"],
+ {"version" : version, "document_list" : doclist, "all_versions" : DocumentRelease.objects.all()},
+ RequestContext(request, {})
+ )
+
+def doc_detail(request, slug, version=None):
+ client, version, docroot = _get_svnroot(version, "docs/")
+
+ docpath = urlparse.urljoin(docroot, slug+".txt")
+ try:
+ name, info = client.info2(docpath)[0]
+ except pysvn.ClientError:
+ raise Http404("Invalid doc: %r (version %r)" % (slug, version))
+
+ cache_key = "djangowebsite:docs:%s:%s:%s" % (version, slug, info.rev.number)
+ parts = cache.get(cache_key)
+ if parts is None:
+ parts = builder.build_document(client.cat(docpath))
+ cache.set(cache_key, parts, 60*60)
+
+ return render_to_response(
+ ["docs/%s_detail.html" % version, "docs/detail.html"],
+ {"doc" : parts, "version" : version, "all_versions" : DocumentRelease.objects.all(), "slug" : slug},
+ RequestContext(request, {})
+ )
+
+docstring_re = re.compile(r"([\"']{3})(.*?)(\1)", re.DOTALL|re.MULTILINE)
+def model_index(request, version=None):
+ client, version, testroot = _get_svnroot(version, "tests/modeltests/")
+
+ cache_key = "djangowebsite:docs:modelindex:%s" % version
+ model_docs = cache.get(cache_key, [])
+ if not model_docs:
+ for testdir in client.ls(testroot):
+ try:
+ content = client.cat(os.path.join(testdir.name, "models.py"))
+ except pysvn.ClientError:
+ continue
+ title, blurb = docstring_re.match(content).group(2).strip().split('\n', 1)
+ try:
+ number, title = title.split(". ", 1)
+ number = int(number)
+ except ValueError:
+ number = None
+ model_docs.append({"title" : title, "link" : os.path.basename(testdir.name), "number" : number})
+ cache.set(cache_key, model_docs, 60*60)
+
+ return render_to_response(
+ ["docs/%s_model_index.html" % version, "docs/model_index.html"],
+ {"example_list" : model_docs, "version" : version, "all_versions" : DocumentRelease.objects.all()},
+ RequestContext(request, {})
+ )
+
+def model_detail(request, slug, version=None):
+ client, version, modelfile = _get_svnroot(version, "tests/modeltests/%s/models.py" % slug)
+ name, info = client.info2(modelfile)[0]
+
+ cache_key = "djangowebsite:docs:model:%s:%s:%s" % (version, slug, info.rev.number)
+ parts = cache.get(cache_key)
+ if parts is None:
+ parts = builder.build_model_document(client.cat(modelfile))
+ cache.set(cache_key, parts, 60*60)
+
+ return render_to_response(
+ ["docs/%s_model_detail.html" % version, "docs/model_detail.html"],
+ {"doc" : parts, "version" : version, "all_versions" : DocumentRelease.objects.all(), "slug" : slug},
+ )
+
+def _get_svnroot(version, subpath):
+ client = pysvn.Client()
+
+ if version is None:
+ version = "trunk"
+ subpath = os.path.join("trunk/", subpath)
+ else:
+ rel = get_object_or_404(DocumentRelease, version=version)
+ subpath = os.path.join(rel.repository_path, subpath)
+ docroot = urlparse.urljoin(settings.DJANGO_SVN_ROOT, subpath)
+
+ try:
+ client.info2(docroot, recurse=False)
+ except pysvn.ClientError:
+ raise Http404("Bad SVN path: %s" % docroot)
+
+ return client, version, docroot
View
37 django_website/settings.py
@@ -1,4 +1,7 @@
-# from worldonline.settings.default import *
+import os, platform
+
+# Far too clever trick to know if we're running on the deployment server.
+DEVELOPMENT_MODE = ("servers.ljworld.com" not in platform.node())
ADMINS = (('Adrian Holovaty','holovaty@gmail.com'), ('Jacob Kaplan-Moss', 'jacob@lawrence.com'))
TIME_ZONE = 'America/Chicago'
@@ -6,19 +9,27 @@
SERVER_EMAIL = 'root@pam.servers.ljworld.com'
MANAGERS = (('Wilson Miner','wminer@ljworld.com'),)
-DEBUG = False
-PREPEND_WWW = True
-
-DATABASE_ENGINE = 'postgresql'
-DATABASE_NAME = 'djangoproject'
-DATABASE_USER = 'apache'
-DATABASE_PASSWORD = ''
-DATABASE_HOST = '10.0.0.80' # set to empty string for localhost
+if DEVELOPMENT_MODE:
+ DEBUG = True
+ PREPEND_WWW = False
+ DATABASE_ENGINE = "sqlite3"
+ DATABASE_NAME = "/tmp/djangoproject.db"
+ CACHE_BACKEND = "file:///tmp/djangoprojectcache/"
+ TEMPLATE_DIRS = [os.path.join(os.path.dirname(__file__), "templates")]
+ DJANGO_SVN_ROOT = "http://code.djangoproject.com/svn/django/"
+else:
+ DEBUG = False
+ PREPEND_WWW = True
+ DATABASE_ENGINE = 'postgresql'
+ DATABASE_NAME = 'djangoproject'
+ DATABASE_USER = 'apache'
+ DATABASE_PASSWORD = ''
+ DATABASE_HOST = '10.0.0.80' # set to empty string for localhost
+ CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
+ TEMPLATE_DIRS = ['/home/html/templates/djangoproject.com/']
+ DJANGO_SVN_ROOT = "file:///home/svn/django/django/"
SITE_ID = 1
-TEMPLATE_DIRS = (
- '/home/html/templates/djangoproject.com/',
-)
ROOT_URLCONF = 'django_website.urls'
INSTALLED_APPS = (
'django.contrib.sites',
@@ -42,8 +53,6 @@
DJANGO_DOCUMENT_ROOT_PATH = "/home/html/djangoproject.com/docs/"
DJANGO_TESTS_PATH = "/home/html/djangoproject.com/tests/"
-CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
-
CACHE_MIDDLEWARE_SECONDS = 60 * 60 * 1 # 1 hour
CACHE_MIDDLEWARE_KEY_PREFIX = 'djangoproject'
CACHE_MIDDLEWARE_GZIP = True
View
10 django_website/sitemaps.py
@@ -1,6 +1,5 @@
from django.contrib.sitemaps import Sitemap
from django_website.apps.blog.models import Entry
-from django_website.apps.docs.models import Document
from django.contrib.flatpages.models import FlatPage
import datetime
@@ -38,12 +37,3 @@ def items(self):
# lastmod is not implemented, because weblog pages contain comments.
# We'd rather not look up the date of the latest comment -- not worth the overhead.
-class DocumentationSitemap(Sitemap):
- changefreq = 'weekly'
- priority = 0.8
-
- def items(self):
- return Document.objects.all()
-
- # lastmod is not implemented, because documentation contains comments.
- # We'd rather not look up the date of the latest comment -- not worth the overhead.
View
42 django_website/templates/docs/detail.html
@@ -0,0 +1,42 @@
+{% extends "base_docs.html" %}
+
+{% block title %}{{ doc.title }} | Django Documentation{% endblock %}
+
+{% block content %}
+<h1>{{ doc.title }}</h1>
+
+<h2 class="deck">
+{% ifequal version "trunk" %}
+ This document is for Django's SVN release, which can be
+ significantly different than previous releases. Get old docs here:
+ {% for r in all_versions %}
+ <a href="../{{ r.version }}/{{ slug }}/">{{ r.version }}</a>{% if forloop.last %}.{% else %},{% endif %}
+ {% endfor %}
+{% else %}
+ This document describes Django version {{ version }}. For current documentation,
+ <a href="/documentation/{{ slug }}/">go here</a>.
+{% endifequal %}
+</h2>
+
+{{ doc.body }}
+
+<div id="content-secondary">
+ <h2 id="comments">Questions/Feedback</h2>
+ <p>
+ If you notice errors with this documentmentation, please <a
+ href="http://code.djangoproject.com/simpleticket?component=Documentation">
+ open a ticket</a> and let us know!
+ </p>
+ <p>
+ Please only use the ticket tracker for criticisms and improvements on the
+ docs. For tech support, ask in the IRC channel or post to the <a
+ href="http://groups-beta.google.com/group/django-users"> django-users
+ list</a>.
+ </p>
+</div>
+{% endblock %}
+
+{% block content-related %}
+ <h2>Contents</h2>
+ {{ doc.toc }}
+{% endblock %}
View
34 django_website/templates/docs/document_detail.html
@@ -1,34 +0,0 @@
-{% extends "base_docs.html" %}
-
-{% block title %}Documentation | {{ object.title }}{% endblock %}
-
-{% block content %}
-{{ object.get_content }}
-
-{% load comments %}
-{% get_free_comment_list for docs.document object.id as comment_list %}
-
-<div id="content-secondary">
-<h2 id="comments">Comments</h2>
-
-{% for comment in comment_list %}
-<div class="comment" id="c{{ comment.id }}">
- <h3>{{ comment.person_name|escape }} <span class="small quiet">{{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}</span></h3>
- {{ comment.comment|escape|urlizetrunc:"40"|linebreaks }}
-</div>
-{% endfor %}
-
-<h2>Post a comment</h2>
-
-<p class="small quiet"><strong>Note:</strong> Please only use the comments for
-questions/criticisms/suggestions on the docs. This is <strong>not</strong> the place
-to ask technical support questions. For tech support, ask in the IRC
-channel or post to the <a href="http://groups-beta.google.com/group/django-users">
-django-users list</a>. Comments will be periodically reviewed, integrated into
-the documentation proper and removed.</p>
-
-{% free_comment_form for docs.document object.id %}
-</div>
-{% endblock %}
-
-{% block content-related %}<h2>Contents</h2>{{ object.get_toc }}{% endblock %}
View
124 django_website/templates/docs/index.html
@@ -0,0 +1,124 @@
+{% extends "base_docs.html" %}
+
+{% block content %}
+<h1>Documentation</h1>
+
+<h2 class="deck">
+{% ifequal version "trunk" %}
+ These docs are for Django's SVN release, which can be
+ significantly different than previous releases. Get old docs here:
+ {% for r in all_versions %}
+ <a href="{{ r.version }}/">{{ r.version }}</a>{% if forloop.last %}.{% else %},{% endif %}
+ {% endfor %}
+{% else %}
+ These docs are for Django version {{ version }}. For current documentation,
+ <a href="/documentation/">go here</a>.
+{% endifequal %}
+</h2>
+
+<h2>The Django Book</h2>
+
+<p>We're in the process of writing the official Django book. Follow its progress at <a href="http://www.djangobook.com/">djangobook.com</a>. Note that it assumes you're using the Django development version rather than version 0.95.</p>
+
+<h2>The essential documentation</h2>
+
+<p>Make sure to read the following documentation. The rest (in the "Reference" section below) can be read in any particular order, as you need the various functionality.</p>
+
+<ul>
+<li><a href="overview/">Django overview</a></li>
+<li><a href="install/">Installation guide</a></li>
+<li>Tutorial: Writing your first Django app
+ <ul>
+ <li><a href="tutorial1/">Part 1: Initialization, creating models, the database API</a></li>
+ <li><a href="tutorial2/">Part 2: Exploring the automatically-generated admin site</a></li>
+ <li><a href="tutorial3/">Part 3: Creating the public interface views</a></li>
+ <li><a href="tutorial4/">Part 4: Simple form processing and generic views</a></li>
+ </ul>
+</li>
+<li><a href="faq/">Frequently asked questions (FAQ)</a></li>
+<li><a href="documentation/">How to read this documentation</a></li>
+</ul>
+
+<h2 id="reference">Reference</h2>
+
+<ul>
+<li><a href="django_admin/">The django-admin.py and manage.py utilities</a></li>
+<li>Models: <a href="model_api/">Creating models</a> | <a href="models/">Examples</a> | <a href="db_api/">The database API</a> | <a href="transactions/">Transactions</a></li>
+
+<li>Templates: <a href="templates/">Guide for HTML authors</a> | <a href="templates_python/">Guide for Python programmers</a></li>
+
+<li><a href="forms/">Forms and manipulators</a> | <strong>New:</strong> <a href="http://www.djangoproject.com/documentation/newforms/">The <code>newforms</code> library</a></li>
+<li><strong>New:</strong> <a href="testing/">Testing Django applications</a></li>
+<li><a href="sessions/">Sessions</a></li>
+<li><a href="cache/">Caching</a></li>
+<li><a href="i18n/">Internationalization</a></li>
+<li><a href="middleware/">Middleware</a></li>
+<li><a href="settings/">Settings files</a></li>
+<li><a href="url_dispatch/">URL configuration</a></li>
+<li><a href="request_response/">Request and response objects</a></li>
+<li><a href="generic_views/">Generic views</a></li>
+<li><a href="authentication/">Authentication</a></li>
+
+<li><a href="add_ons/">Add-on applications (contrib)</a>
+ <ul>
+ <li><a href="syndication/">Syndication feeds (RSS and Atom)</a></li>
+ <li><a href="flatpages/">Flatpages</a></li>
+ <li><a href="redirects/">Redirects</a></li>
+ <li><a href="sites/">Sites</a></li>
+ <li><a href="sitemaps/">Sitemaps</a></li>
+ </ul>
+</li>
+</ul>
+
+<h2>Deployment</h2>
+
+<ul>
+<li><a href="modpython/">Using Django with mod_python</a></li>
+<li><a href="fastcgi/">Using Django with FastCGI</a></li>
+</ul>
+
+<h2>Solving specific problems</h2>
+
+<ul>
+<li><a href="apache_auth/">Authenticating against Django's user database from Apache</a></li>
+<li><a href="static_files/">Serving static/media files</a></li>
+<li><a href="email/">Sending e-mail</a></li>
+<li><a href="legacy_databases/">Integrating with (introspecting) a legacy database</a></li>
+<li><a href="outputting_pdfs/">Outputting PDFs dynamically</a></li>
+<li><a href="outputting_csv/">Outputting CSV dynamically</a></li>
+</ul>
+
+<h2>Et cetera</h2>
+<ul>
+<li><a href="design_philosophies/">Design philosophies</a></li>
+<li><a href="contributing/">How to contribute to Django</a></li>
+<li><a href="admin_css/">Django admin CSS guide</a></li>
+<li><a href="api_stability/">API stability</a></li>
+<li><a href="http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges">Backwards-incompatible changes</a></li>
+</ul>
+
+<h2>Release notes</h2>
+<ul>
+<li><a href="095_release_notes/">Version 0.95</a></li>
+</ul>
+{% endblock %}
+
+{% block content-related %}
+
+<h2>Search docs via Google</h2>
+<form action="http://www.google.com/search" method="get">
+<p><input type="text" name="as_q" /> <input type="submit" value="Go" /></p>
+<input type="hidden" name="as_sitesearch" value="www.djangoproject.com" />
+</form>
+
+<h2>Getting help</h2>
+<ul class="toc">
+ <li><a href="irc://irc.freenode.net/django">#django IRC channel</a></li>
+ <li><a href="http://simon.bofh.ms/logger/django/">#django IRC logs</a></li>
+ <li><a href="http://groups-beta.google.com/group/django-users">Django-users mailing list</a></li>
+ <li><a href="http://groups-beta.google.com/group/django-developers">Django-developers mailing list</a></li>
+ <li><a href="http://code.djangoproject.com/simpleticket">Report a bug</a></li>
+ <li><a href="/comments/">Recent comments posted to djangoproject.com</a></li>
+</ul>
+
+{% endblock %}
View
44 django_website/templates/docs/model_detail.html
@@ -0,0 +1,44 @@
+{% extends "base_docs.html" %}
+
+{% block title %}{{ doc.title }} | Django Documentation{% endblock %}
+
+{% block content %}
+<h1>{{ doc.title }}</h1>
+
+<h2 class="deck">
+{% ifequal version "trunk" %}
+ This example is for Django's SVN release, which can be
+ significantly different than previous releases. Get old examples here:
+ {% for r in all_versions %}
+ <a href="/documentation/{{ r.version }}/models/{{ slug }}/">{{ r.version }}</a>{% if forloop.last %}.{% else %},{% endif %}
+ {% endfor %}
+{% else %}
+ This example describes Django version {{ version }}. For the current example,
+ <a href="/documentation/models/{{ slug }}/">go here</a>.
+{% endifequal %}
+</h2>
+
+{{ doc.fragment }}
+
+<h2 id="model-source-code">Model source code</h2>
+<pre class="literal-block">{{ doc.model_source|escape }}</pre>
+
+<h2 id="sample-usage">Sample API usage</h2>
+{% if doc.newstyle %}
+<p>This sample code assumes the above model{{ doc.models|pluralize }} {% if doc.models|pluralize %}have{% else %}has{% endif %}
+been saved in a file <tt class="docutils literal"><span class="pre">mysite/models.py</span></tt>.
+<pre class="literal-block">&gt;&gt;&gt; from mysite.models import {% for model in doc.models %}{{ model }}{% if not forloop.last %}, {% endif %}{% endfor %}
+{{ doc.api_usage|escape }}</pre>
+{% else %}
+<pre class="literal-block">{{ doc.api_usage|escape }}</pre>
+{% endif %}
+
+{% endblock %}
+
+{% block content-related %}
+ <h2>Contents</h2>
+ <ul>
+ <li><a href="#model-source-code">Model source code</a></li>
+ <li><a href="#sample-usage">Sample API usage</a></li>
+ </ul>
+{% endblock %}
View
26 django_website/templates/docs/model_index.html
@@ -0,0 +1,26 @@
+{% extends "base_docs.html" %}
+
+{% block content %}
+ <h1>Model examples</h1>
+
+ <h2 class="deck">
+ {% ifequal version "trunk" %}
+ These examples are from Django's SVN release, which can be
+ significantly different than previous releases. Get old examples here:
+ {% for r in all_versions %}
+ <a href="{{ r.version }}/models/">{{ r.version }}</a>{% if forloop.last %}.{% else %},{% endif %}
+ {% endfor %}
+ {% else %}
+ These examples are from Django version {{ version }}. For current documentation,
+ <a href="/documentation/models/">go here</a>.
+ {% endifequal %}
+ </h2>
+
+ <p>This is a set of example models.</p>
+
+ <ol>
+ {% for ex in example_list %}
+ <li><a href="{{ ex.link }}/">{{ ex.title }}</a></li>
+ {% endfor %}
+ </ol>
+{% endblock %}
View
4 django_website/templates/flatfiles/docs.html
@@ -3,6 +3,10 @@
{% block content %}
<h1>{{ flatpage.title }}</h1>
+{% ifequal version "trunk" %}
+
+{% endifequal %}
+
{{ flatpage.content }}
{% endblock %}
View
3  django_website/urls.py
@@ -5,7 +5,7 @@
from django_website.apps.aggregator.feeds import CommunityAggregatorFeed
from django_website.apps.aggregator.models import FeedItem
from django_website.apps.blog.feeds import WeblogEntryFeed
-from django_website.sitemaps import FlatPageSitemap, WeblogSitemap, DocumentationSitemap
+from django_website.sitemaps import FlatPageSitemap, WeblogSitemap
from django.views.decorators.cache import cache_page
comments_info_dict = {
@@ -26,7 +26,6 @@
sitemaps = {
'weblog': WeblogSitemap,
- 'docs': DocumentationSitemap,
'flatpages': FlatPageSitemap,
}
Please sign in to comment.
Something went wrong with that request. Please try again.