DjangoSpecifications/Docs/ConvertingRestToOtherFormats: build_htmldocs.diff
| File build_htmldocs.diff, 20.2 kB (added by mrts, 2 months ago) |
|---|
-
django/core/management/commands/htmldocs.py
old new 1 """ 2 Command that converts Django documentation from ReST to HTML. 3 4 Based 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 13 import os 14 import shutil 15 from optparse import make_option 16 from django.core.management import BaseCommand, CommandError 17 18 CSS_FILE = "docs.css" 19 20 class 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 119 def 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 127 def 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 134 def 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 172 def 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 /* 2 djangoproject.com by Wilson Miner (wilson@lawrence.com) 3 Copyright (c) 2005 Lawrence Journal-World. Please don't steal. 4 */ 5 6 7 /* SETUP */ 8 9 body { margin:0; padding:0; background:#092e20; color:white; } 10 body, 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 16 a {text-decoration: none;} 17 a img {border: none;} 18 a: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; } 21 a: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 58 h1,h2,h3 { margin-top:.8em; font-family:"Trebuchet MS",sans-serif; font-weight:normal; } 59 h1 { font-size:218%; margin-top:.6em; margin-bottom:.6em; color:#092e20; line-height:1.1em; } 60 h2 { font-size:150%; margin-top:1em; margin-bottom:.2em; line-height:1.2em; color:#092e20; } 61 #homepage h2 { font-size:140%; } 62 h3 { font-size:125%; font-weight:bold; margin-bottom:.2em; color:#487858; } 63 h4 { font-size:100%; font-weight:bold; margin-bottom:-3px; margin-top:1.2em; text-transform:uppercase; letter-spacing:1px; } 64 h4 pre, h4 tt, h4 .literal { text-transform:none; } 65 h5 { font-size:1em; font-weight:bold; margin-top:1.5em; margin-bottom:3px; } 66 p, ul, dl { margin-top:.6em; margin-bottom:.8em; } 67 hr { color:#ccc; background-color:#ccc; height:1px; border:0; } 68 p.date { color:#487858; margin-top:-.2em; } 69 p.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; } 76 h2.deck { margin-top:-.5em !important; margin-bottom:.6em; color:#487858; } 77 ins { text-decoration: none; } 78 ins a { text-decoration: none; } 79 80 /* LISTS */ 81 82 ul { padding-left:2em; } 83 ol { padding-left:30px; } 84 ul li { list-style-type:square; margin-bottom:.4em; } 85 ul ul { padding-left:1.2em; } 86 ul ul ul { padding-left:1em; } 87 ul.linklist, ul.toc { padding-left:0; } 88 ul.toc ul { margin-left:.6em; } 89 ul.toc ul li { list-style-type:square; } 90 ul.toc ul ul li { list-style-type:disc; } 91 ul.linklist li, ul.toc li { list-style-type:none; } 92 dt { font-weight:bold; margin-top:.5em; font-size:1.1em; } 93 dd { margin-bottom:.8em; } 94 95 /* RSS */ 96 97 a.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; } 99 a.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; } 114 pre, .literal-block { font-size:medium; background:#E0FFB8; border:1px solid #94da3a; border-width:1px 0; margin: 1em 0; padding: .3em .4em; overflow: auto; } 115 dt .literal, table .literal { background:none; } 116 textarea.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; } 132 table.docutils { border-collapse:collapse; } 133 table.docutils thead th { border-bottom:2px solid #dfdfdf; text-align:left; } 134 table.docutils td, table.docutils th { border-bottom:1px solid #dfdfdf; padding:4px 2px;} 135 table.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; } 160 div.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%; } 166 h3 .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 */
