DjangoSpecifications/Docs/ConvertingRestToOtherFormats: build_htmldocs-links_work.diff
| File build_htmldocs-links_work.diff, 21.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(opt_single), 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(is_single): 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 import re 181 182 class DjangoHTMLTranslator(html4css1.HTMLTranslator): 183 """ 184 reST -> HTML translator subclass that outputs Django-specific markup. 185 """ 186 187 # Prevent name attributes from being generated 188 named_tags = [] 189 190 def __init__(self, document): 191 html4css1.HTMLTranslator.__init__(self, document) 192 self._in_literal = 0 193 194 # Remove the default border=1 from <table> 195 def visit_table(self, node): 196 self.body.append(self.starttag(node, 'table', CLASS='docutils')) 197 198 # No smartypants conversion 199 200 # Avoid <blockquote>s around merely indented nodes. 201 # Adapted from 202 # http://thread.gmane.org/gmane.text.docutils.user/742/focus=804 203 204 _suppress_blockquote_child_nodes = ( 205 nodes.bullet_list, nodes.enumerated_list, nodes.definition_list, 206 nodes.literal_block, nodes.doctest_block, nodes.line_block, 207 nodes.table 208 ) 209 def _bq_is_valid(self, node): 210 return len(node.children) != 1 \ 211 or not isinstance(node.children[0], 212 self._suppress_blockquote_child_nodes) 213 214 def visit_block_quote(self, node): 215 if self._bq_is_valid(node): 216 html4css1.HTMLTranslator.visit_block_quote(self, node) 217 218 def depart_block_quote(self, node): 219 if self._bq_is_valid(node): 220 html4css1.HTMLTranslator.depart_block_quote(self, node) 221 222 # Fix inter-page links 223 224 class MultiPageTranslator(DjangoHTMLTranslator): 225 def visit_reference(self, node, regex=re.compile(r'^../([-\w]+)/')): 226 if node.has_key('refuri') and regex.match(node['refuri']): 227 node['refuri'] = regex.sub(r'\1.html', node['refuri']) 228 html4css1.HTMLTranslator.visit_reference(self, node) 229 230 class SinglePageTranslator(DjangoHTMLTranslator): 231 # Destroys ../foo/#bar style anchors (replacing them with #foo), 232 # but we can live with that 233 def visit_reference(self, node, regex=re.compile(r'^../([-\w]+)/.*')): 234 if node.has_key('refuri') and regex.match(node['refuri']): 235 node['refuri'] = regex.sub(r'#\1.html', node['refuri']) 236 html4css1.HTMLTranslator.visit_reference(self, node) 237 238 class DjangoHTMLWriter(html4css1.Writer): 239 """ 240 HTML writer that adds a "toc" key to the set of doc parts. 241 """ 242 def __init__(self): 243 html4css1.Writer.__init__(self) 244 if is_single: 245 self.translator_class = SinglePageTranslator 246 else: 247 self.translator_class = MultiPageTranslator 248 249 def translate(self): 250 # build the document 251 html4css1.Writer.translate(self) 252 253 # build the contents 254 contents = self.build_contents(self.document) 255 contents_doc = self.document.copy() 256 contents_doc.children = contents 257 contents_visitor = self.translator_class(contents_doc) 258 contents_doc.walkabout(contents_visitor) 259 self.parts['toc'] = "<ul class='toc'>%s</ul>" \ 260 % ''.join(contents_visitor.fragment) 261 262 def build_contents(self, node, level=0): 263 level += 1 264 sections = [] 265 i = len(node) - 1 266 while i >= 0 and isinstance(node[i], nodes.section): 267 sections.append(node[i]) 268 i -= 1 269 sections.reverse() 270 entries = [] 271 autonum = 0 272 depth = 4 # XXX FIXME 273 for section in sections: 274 title = section[0] 275 entrytext = title 276 try: 277 reference = nodes.reference('', '', 278 refid=section['ids'][0], *entrytext) 279 except IndexError: 280 continue 281 ref_id = self.document.set_id(reference) 282 entry = nodes.paragraph('', '', reference) 283 item = nodes.list_item('', entry) 284 if level < depth: 285 subsects = self.build_contents(section, level) 286 item += subsects 287 entries.append(item) 288 if entries: 289 contents = nodes.bullet_list('', *entries) 290 return contents 291 else: 292 return [] 293 294 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 */
