Django

Code

DjangoSpecifications/Docs/ConvertingRestToOtherFormats: build_htmldocs.diff

File build_htmldocs.diff, 20.2 kB (added by mrts, 2 months ago)

The patch that implements conversion from ReST to HTML

  • django/core/management/commands/htmldocs.py

    old new  
     1""" 
     2Command that converts Django documentation from ReST to HTML. 
     3 
     4Based on django_website/apps/docs/builder.py. 
     5""" 
     6 
     7# TODO: 
     8# 1. fix inter-page links 
     9# 2. fetch external images and place them to img directory, fix image links 
     10# 3. add images used in CSS (in notes etc) 
     11# 4. add PDF support, take inspiration from http://code.google.com/p/rst2pdf/ 
     12 
     13import os 
     14import shutil 
     15from optparse import make_option 
     16from django.core.management import BaseCommand, CommandError 
     17 
     18CSS_FILE = "docs.css" 
     19 
     20class Command(BaseCommand): 
     21    option_list = BaseCommand.option_list + ( 
     22            make_option('--single', action='store_true', dest='single', 
     23                default=False, help = "Write a single combined HTML file. " 
     24                "Not recommended, produces output that is harder to navigate."), 
     25    ) 
     26    help = ("Converts Django documentation to HTML. Uses the given Django " 
     27            "documentation directory for reading documentation source and " 
     28            "output directory for writing HTML files. Creates the output " 
     29            "directory if it does not exist.") 
     30    args = "[Django documentation directory] [output directory]" 
     31 
     32    requires_model_validation = False 
     33    can_import_settings = False 
     34 
     35    def handle(self, *paths, **options): 
     36        try: 
     37            from docutils.core import publish_parts 
     38        except ImportError: 
     39            raise CommandError("The docutils module is required to run " 
     40                    "this command.") 
     41 
     42        if len(paths) != 2: 
     43            raise CommandError("Please provide exactly two arguments in the " 
     44                    "following order: %s." % self.args) 
     45 
     46        source, dest = paths 
     47        if not os.path.exists(dest): 
     48            try: 
     49                os.mkdir(dest) 
     50            except OSError, e: 
     51                raise CommandError("Failed to create directory '%s'. " 
     52                        "The error was '%s'." % (dest, e)) 
     53        for p in source, dest: 
     54            if not os.path.isdir(p): 
     55                raise CommandError("'%s' is not a directory." % p) 
     56 
     57        css_path = os.path.join(source, "css", CSS_FILE) 
     58        if not os.path.exists(css_path): 
     59            raise CommandError("'%s' does not appear to be a Django " 
     60                    "documentation directory (stylesheet not found)" % source) 
     61        try: 
     62            shutil.copy(css_path, dest) 
     63        except IOError, e: 
     64            raise CommandError("Unable to write to directory '%s'. " 
     65                    "The error was '%s'." % (dest, e)) 
     66 
     67        opt_single = options.get("single", False) 
     68 
     69        files = [f for f in os.listdir(source) if f.endswith('.txt')] 
     70        files.sort() 
     71 
     72        if opt_single: 
     73            docs = [] 
     74        index = [] 
     75        for file in files: 
     76            out = file[:-4] + ".html" 
     77            doc = publish_parts(open(os.path.join(source, file)).read(), 
     78                    writer=get_django_html_writer(), 
     79                    settings_overrides={'initial_header_level': 2}) 
     80            try: 
     81                index.append([out, doc['title']]) 
     82            except KeyError: 
     83                index.append([out, 'UNKNOWN (%s)' % file]) 
     84         
     85            if opt_single: 
     86                doc['id'] = out 
     87                docs.append(doc) 
     88            else: 
     89                html_out = os.path.join(dest, out) 
     90                print "Writing '%s'... " % html_out, 
     91                open(html_out, 'w').write(render_doc(doc)) 
     92                print "done" 
     93         
     94        # write out either index or the single combined page 
     95        doc = None 
     96        if opt_single: 
     97            doc = { 
     98                'title' : "Documentation", 
     99                'toc' : get_single_toc(index), 
     100                'body' : get_single_body(docs) 
     101            } 
     102        else: 
     103            doc = { 
     104                'title' : "Documentation index", 
     105                'toc' : "<p>This is the automatically genrated documentation " 
     106                            "index. The automatic documentation is a basic " 
     107                            "reference, more resources are available on the " 
     108                            '<a href="http://www.djangoproject.com/documentation/">' 
     109                            "Django website</a>.</p>", 
     110                'body' : '<ul>%s</ul>' % \ 
     111                        '\n'.join(['<li><a href="%s">%s</a></li>' \ 
     112                            % (href, title) for href, title in index ]) 
     113            } 
     114        print "Writing index... ", 
     115        open(os.path.join(dest, "index.html"), 'w').write(render_doc(doc)) 
     116        print "done" 
     117        print "All done" 
     118 
     119def get_single_toc(index): 
     120    """ 
     121    Creates the table of contents for single-document output. 
     122    """ 
     123    toc = '<ul class="toc">%s</ul>' 
     124    line = '<li><a class="reference internal" href="#%s">%s</a></li>' 
     125    return toc % '\n'.join([ line % (name, title) for name, title in index]) 
     126     
     127def get_single_body(docs): 
     128    """ 
     129    Joins individual documents into a single document. 
     130    """ 
     131    chunk = '<h1 id="%(id)s">%(title)s</h1>\n%(body)s' 
     132    return '\n'.join([chunk % doc for doc in docs]) 
     133 
     134def render_doc(doc): 
     135    """ 
     136    Renders the document with a HTML template. 
     137    """ 
     138    return ("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
     139        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
     140<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
     141        <head> 
     142                <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> 
     143                <meta http-equiv="Content-Language" content="en-us" /> 
     144                <title>%(title)s | Django Documentation</title> 
     145                <link href="docs.css" rel="stylesheet" type="text/css" media="screen" /> 
     146    </head> 
     147    <body id="documentation" class="default"> 
     148    <div id="container"> 
     149        <div id="header"> 
     150            <ul id="nav-global"> 
     151                <li id="nav-homepage"><a href="index.html">Documentation index</a></li> 
     152            </ul> 
     153        </div> 
     154        <div id="billboard"><h2><a href="index.html" style="color: white; height:40px; padding-top:20px; text-indent:22px;">Django documentation</a></h2></div> 
     155        <div id="columnwrap"> 
     156            <div id="content-main"> 
     157                <h1>%(title)s</h1> 
     158                %(body)s 
     159            </div> 
     160            <div id="content-related" class="sidebar"> 
     161                <h2>Contents</h2> 
     162                %(toc)s 
     163            </div> 
     164        </div> 
     165        <div id="footer"> 
     166            <p><a href="index.html"><< Back to documentation index</a></p> 
     167        </div> 
     168    </div> 
     169    </body> 
     170</html>""" % doc).encode('utf-8') 
     171 
     172def get_django_html_writer(): 
     173    """ 
     174    Returns the Django HTML writer instance. Note that we need to define the 
     175    classes inside a function to avoid triggering an import error when 
     176    docutils is unavailable. 
     177    """ 
     178    from docutils import nodes 
     179    from docutils.writers import html4css1 
     180 
     181    class DjangoHTMLTranslator(html4css1.HTMLTranslator): 
     182        """ 
     183        reST -> HTML translator subclass that outputs Django-specific markup. 
     184        """ 
     185         
     186        # Prevent name attributes from being generated 
     187        named_tags = [] 
     188         
     189        def __init__(self, document): 
     190            html4css1.HTMLTranslator.__init__(self, document) 
     191            self._in_literal = 0 
     192         
     193        # Remove the default border=1 from <table> 
     194        def visit_table(self, node): 
     195            self.body.append(self.starttag(node, 'table', CLASS='docutils')) 
     196 
     197        # No smartypants conversion 
     198 
     199        # Avoid <blockquote>s around merely indented nodes. 
     200        # Adapted from 
     201        # http://thread.gmane.org/gmane.text.docutils.user/742/focus=804 
     202         
     203        _suppress_blockquote_child_nodes = ( 
     204            nodes.bullet_list, nodes.enumerated_list, nodes.definition_list, 
     205            nodes.literal_block, nodes.doctest_block, nodes.line_block, 
     206            nodes.table 
     207        ) 
     208        def _bq_is_valid(self, node): 
     209            return len(node.children) != 1 \ 
     210                    or not isinstance(node.children[0], 
     211                            self._suppress_blockquote_child_nodes) 
     212                                             
     213        def visit_block_quote(self, node): 
     214            if self._bq_is_valid(node): 
     215                html4css1.HTMLTranslator.visit_block_quote(self, node) 
     216 
     217        def depart_block_quote(self, node): 
     218            if self._bq_is_valid(node): 
     219                html4css1.HTMLTranslator.depart_block_quote(self, node) 
     220 
     221    class DjangoHTMLWriter(html4css1.Writer): 
     222        """ 
     223        HTML writer that adds a "toc" key to the set of doc parts. 
     224        """ 
     225        def __init__(self): 
     226            html4css1.Writer.__init__(self) 
     227            self.translator_class = DjangoHTMLTranslator 
     228 
     229        def translate(self): 
     230            # build the document 
     231            html4css1.Writer.translate(self) 
     232 
     233            # build the contents 
     234            contents = self.build_contents(self.document) 
     235            contents_doc = self.document.copy() 
     236            contents_doc.children = contents 
     237            contents_visitor = self.translator_class(contents_doc) 
     238            contents_doc.walkabout(contents_visitor) 
     239            self.parts['toc'] = "<ul class='toc'>%s</ul>" \ 
     240                    % ''.join(contents_visitor.fragment) 
     241 
     242        def build_contents(self, node, level=0): 
     243            level += 1 
     244            sections = [] 
     245            i = len(node) - 1 
     246            while i >= 0 and isinstance(node[i], nodes.section): 
     247                sections.append(node[i]) 
     248                i -= 1 
     249            sections.reverse() 
     250            entries = [] 
     251            autonum = 0 
     252            depth = 4   # XXX FIXME 
     253            for section in sections: 
     254                title = section[0] 
     255                entrytext = title 
     256                try: 
     257                    reference = nodes.reference('', '', 
     258                            refid=section['ids'][0], *entrytext) 
     259                except IndexError: 
     260                    continue 
     261                ref_id = self.document.set_id(reference) 
     262                entry = nodes.paragraph('', '', reference) 
     263                item = nodes.list_item('', entry) 
     264                if level < depth: 
     265                    subsects = self.build_contents(section, level) 
     266                    item += subsects 
     267                entries.append(item) 
     268            if entries: 
     269                contents = nodes.bullet_list('', *entries) 
     270                return contents 
     271            else: 
     272                return [] 
     273 
     274    return DjangoHTMLWriter() 
  • docs/css/docs.css

    old new  
     1/*  
     2djangoproject.com by Wilson Miner (wilson@lawrence.com) 
     3Copyright (c) 2005 Lawrence Journal-World. Please don't steal. 
     4*/ 
     5 
     6 
     7/* SETUP */ 
     8 
     9body { margin:0; padding:0; background:#092e20; color:white; } 
     10body, th, td { font:12px/1.4em Verdana,sans-serif; } 
     11#container { position:relative; min-width:55em; max-width:100em; } 
     12#homepage #container { max-width:100em; } 
     13 
     14/* LINKS */ 
     15 
     16a {text-decoration: none;} 
     17a img {border: none;} 
     18a:link, a:visited { color:#ffc757; } 
     19#content-main a:link, #content-main a:visited { color:#ab5603; text-decoration:underline; } 
     20#content-secondary a:link, #content-secondary a:visited { color:#ffc757; text-decoration:none; } 
     21a:hover { color:#ffe761; } 
     22#content-main a:hover { background-color:#E0FFB8; color:#234f32; text-decoration:none; } 
     23#content-secondary a:hover { color:#ffe761; background:none; } 
     24#content-main h2 a, #content-main h3 a { text-decoration:none !important; } 
     25 
     26/* HEADER */ 
     27 
     28#header { position:relative; height:6.5em; background:#092e20; } 
     29#header h1#logo { margin:0; width:111px; height:41px; position:absolute; bottom:10px; left:25px; } 
     30 
     31/* NAV */ 
     32 
     33#nav-global { position:absolute; margin:0; bottom:0; right:0; font-family:"Trebuchet MS",sans-serif; white-space:nowrap; } 
     34#nav-global li { display:block; float:left; list-style-type:none; margin:0; padding:0; } 
     35#nav-global a { display:block; float:left; padding:5em 16px 10px 16px; background:#092e20; } 
     36#nav-global a:hover { color:white; background:#234f32; } 
     37#homepage #nav-homepage a, #overview #nav-overview a, #download #nav-download a, #documentation #nav-documentation a, #weblog #nav-weblog a, #community #nav-community a, #blogroll #nav-blogroll a, #code #nav-code a { color:white; background:#092e20 url(../img/site/nav_bg.gif) bottom repeat-x; } 
     38 
     39/* COLUMNS */ 
     40 
     41#columnwrap { background:#234f32; padding-bottom:10px; } 
     42#subwrap { background:#326342; width:73%; float:left; padding-bottom:10px; } 
     43#content-main { float:left; width:70%; background:white; color:black; padding-bottom:10px; } 
     44#generic #content-main, #code #content-main { width:100%; } 
     45#content-main * { margin-left:22px; margin-right:24px; } 
     46#content-main * * { margin-left:0; margin-right:0; } 
     47.sidebar { font-size:92%; } 
     48.sidebar * { margin-left:14px; margin-right:14px; } 
     49.sidebar * * { margin-left:0; margin-right:0; } 
     50#content-extra { float:right; width:27%; } 
     51#content-related { float:right; width:30%;} 
     52#content-secondary { clear:both; background:#487858; margin-left:0; margin-right:0; margin-top:15px; margin-bottom:-10px; padding:10px 24px; color:white; } 
     53.subcol-primary, .subcol-secondary { width:40%; float:left; padding-bottom:1.2em; } 
     54.subcol-primary { margin-right:1%; } 
     55 
     56/* CONTENT */ 
     57 
     58h1,h2,h3 { margin-top:.8em; font-family:"Trebuchet MS",sans-serif; font-weight:normal; } 
     59h1 { font-size:218%; margin-top:.6em; margin-bottom:.6em; color:#092e20; line-height:1.1em; } 
     60h2 { font-size:150%; margin-top:1em; margin-bottom:.2em; line-height:1.2em; color:#092e20; } 
     61#homepage h2 { font-size:140%; } 
     62h3 { font-size:125%; font-weight:bold; margin-bottom:.2em; color:#487858; } 
     63h4 { font-size:100%; font-weight:bold; margin-bottom:-3px; margin-top:1.2em; text-transform:uppercase; letter-spacing:1px; } 
     64h4 pre, h4 tt, h4 .literal { text-transform:none; } 
     65h5 { font-size:1em; font-weight:bold; margin-top:1.5em; margin-bottom:3px; } 
     66p, ul, dl { margin-top:.6em; margin-bottom:.8em; } 
     67hr { color:#ccc; background-color:#ccc; height:1px; border:0; } 
     68p.date { color:#487858; margin-top:-.2em; } 
     69p.more { margin-top:-.4em; } 
     70.sidebar p.date { color:#90ba9e; } 
     71#content-secondary h2, .sidebar h2 { color:white; } 
     72#content-secondary h3, .sidebar h3 { color:#9aef3f; } 
     73#content-secondary h2:first-child { margin-top:.6em; } 
     74.sidebar h2:first-child { margin-top:.8em; } 
     75#content-main h2, #content-main h3 { margin-top:1.2em; } 
     76h2.deck { margin-top:-.5em !important; margin-bottom:.6em; color:#487858; } 
     77ins { text-decoration: none; } 
     78ins a { text-decoration: none; } 
     79 
     80/* LISTS */ 
     81 
     82ul { padding-left:2em; } 
     83ol { padding-left:30px; } 
     84ul li { list-style-type:square; margin-bottom:.4em; } 
     85ul ul { padding-left:1.2em; } 
     86ul ul ul { padding-left:1em; } 
     87ul.linklist, ul.toc { padding-left:0; } 
     88ul.toc ul { margin-left:.6em; } 
     89ul.toc ul li { list-style-type:square; } 
     90ul.toc ul ul li { list-style-type:disc; } 
     91ul.linklist li, ul.toc li { list-style-type:none; } 
     92dt { font-weight:bold; margin-top:.5em; font-size:1.1em; } 
     93dd { margin-bottom:.8em; } 
     94 
     95/*  RSS  */ 
     96 
     97a.rss { font:bold 10px Verdana, sans-serif; padding:0 .2em; border: 1px solid; text-decoration:none; background:#f60;color: #fff; border-color:#ffc8a4 #7d3302 #3f1a01 #ff9a57; margin:0 3px; vertical-align:middle; } 
     98#content-main a.rss { color:#fff; text-decoration:none; } 
     99a.rss:hover, a.rss:link, a.rss:visited { color:#fff; text-decoration:none; } 
     100 
     101/* BLOCKQUOTES */ 
     102 
     103#weblog blockquote { padding-left:0.8em; padding-right:1em; font:125%/1.2em "Trebuchet MS", sans-serif; color:#234f32; border-left:2px solid #94da3a; } 
     104.sidebar blockquote { margin-top:1.5em; margin-bottom:1.5em; } 
     105.sidebar blockquote p { font:italic 175%/1.2em "Trebuchet MS",sans-serif; color:#94da3a; } 
     106.sidebar blockquote cite { display:block; font-style:normal; line-height:1.2em; margin-top:-.8em; color:#94da3a; } 
     107.sidebar cite strong { font-weight:normal; color:white; } 
     108 
     109/* CODE BLOCKS */ 
     110 
     111.literal { white-space:nowrap; } 
     112.literal, .literal-block { color:#234f32; } 
     113.sidebar .literal { color:white; background:transparent; font-size:11px; } 
     114pre, .literal-block { font-size:medium; background:#E0FFB8; border:1px solid #94da3a; border-width:1px 0; margin: 1em 0; padding: .3em .4em; overflow: auto; } 
     115dt .literal, table .literal { background:none; } 
     116textarea.codedump { font-size:10px; color:#234f32; width:100%; background:#E0FFB8; border:1px solid #94da3a; border-width:1px 0; padding: .3em .4em; } 
     117 
     118/* NOTES & ADMONITIONS */ 
     119 
     120.note, .admonition, .caution { padding:.8em 1em .8em; margin: 1em 0; border:1px solid #94da3a; } 
     121.admonition-title { font-weight:bold; margin-top:0 !important; margin-bottom:0 !important;} 
     122.admonition .last { margin-bottom:0 !important; } 
     123.admonition-philosophy { padding-left:65px; background:url(../img/doc/icons/docicons-philosophy.gif) .8em .8em no-repeat;} 
     124.admonition-note, .caution { padding-left:65px; background:url(../img/doc/icons/docicons-note.gif) .8em .8em no-repeat;} 
     125.admonition-behind-the-scenes { padding-left:65px; background:url(../img/doc/icons/docicons-behindscenes.gif) .8em .8em no-repeat;} 
     126 
     127/* DOCS */ 
     128 
     129#documentation h2, #documentation h3, #documentation h4 { margin-top:1.4em; } 
     130#documentation dd { margin-left:1em; } 
     131#content-main table { color:#000; } 
     132table.docutils { border-collapse:collapse; } 
     133table.docutils thead th { border-bottom:2px solid #dfdfdf; text-align:left; } 
     134table.docutils td, table.docutils th { border-bottom:1px solid #dfdfdf; padding:4px 2px;} 
     135table.docutils td p { margin-top:0; margin-bottom:.5em; } 
     136#documentation #content-related .literal { background:transparent !important; } 
     137 
     138/* BILLBOARDS */ 
     139 
     140#billboard { background:#94da3a url(../img/site/bbdsm_bg.gif) repeat-x; border-bottom:6px solid #092e20; } 
     141#billboard h2 { margin:0; } 
     142#generic #billboard { display:none; } 
     143#homepage #billboard { background-image: url(../img/site/bbd_bg.gif); } 
     144#homepage #billboard h2 { margin:0; text-indent:-5000px; height:80px; width:633px; background:url(../img/site/bbd_homepage.gif) no-repeat; } 
     145#overview #billboard h2 { margin:0; text-indent:-5000px; height:60px; width:203px; background:url(../img/site/bbd_overview.gif) no-repeat; } 
     146#download #billboard h2 { margin:0; text-indent:-5000px; height:60px; width:203px; background:url(../img/site/bbd_download.gif) no-repeat; } 
     147#documentation #billboard h2 a { display:block; margin:0; text-indent:-5000px; height:60px; width:226px; background:url(../img/site/bbd_documentation.gif) no-repeat; } 
     148#weblog #billboard h2 a { display:block; margin:0; text-indent:-5000px; height:60px; width:226px; background:url(../img/site/bbd_weblog.gif) no-repeat; } 
     149#community #billboard h2 { display:block; margin:0; text-indent:-5000px; height:60px; width:226px; background:url(../img/site/bbd_community.gif) no-repeat; } 
     150#blogroll #billboard h2 { display:block; margin:0; text-indent:-5000px; height:60px; width:168px; background:url(../img/site/bbd_blogroll.gif) no-repeat; } 
     151#code #billboard h2 a { display:block; margin:0; text-indent:-5000px; height:60px; width:184px; background:url(../img/site/bbd_code.gif) no-repeat; } 
     152 
     153/* FOOTER */ 
     154 
     155#footer { clear:both; color:#487858; padding:10px 20px; font-size:90%; } 
     156 
     157/* COMMENTS */ 
     158 
     159.comment { margin:15px 0; } 
     160div.comment p { margin-left:1em; } 
     161#weblog div.comment p.date { margin-bottom:.2em; color:#94da3a; } 
     162 
     163/* MISC */ 
     164 
     165.small { font-size:90%; } 
     166h3 .small { font-size:80%; } 
     167.quiet { font-weight:normal; } 
     168.clear { clear:both; } 
     169#content-main .quiet { color:#487858; } 
     170#content-secondary .quiet { color:#90ba9e; } 
     171 
     172/*  CLEARFIX KLUDGE */ 
     173 
     174#columnwrap:after { 
     175    content: ".";  
     176    display: block;  
     177    height: 0;  
     178    clear: both;  
     179    visibility: hidden; 
     180} 
     181#columnwrap { display: inline-block; } 
     182 
     183/* Hides from IE-mac \*/ 
     184* html #columnwrap { height: 1%; } 
     185#columnwrap { display: block; } 
     186/* End hide from IE-mac */ 
     187 
     188#subwrap:after { 
     189    content: ".";  
     190    display: block;  
     191    height: 0;  
     192    clear: both;  
     193    visibility: hidden; 
     194} 
     195#subwrap { display: inline-block; } 
     196 
     197/* Hides from IE-mac \*/ 
     198* html #subwrap { height: 1%; } 
     199#subwrap { display: block; } 
     200/* End hide from IE-mac */om IE-mac */