diff options
Diffstat (limited to 'becommands/html.py')
-rw-r--r-- | becommands/html.py | 607 |
1 files changed, 0 insertions, 607 deletions
diff --git a/becommands/html.py b/becommands/html.py deleted file mode 100644 index b0640da..0000000 --- a/becommands/html.py +++ /dev/null @@ -1,607 +0,0 @@ -# Copyright (C) 2009 Gianluca Montecchi <gian@grys.it> -# W. Trevor King <wking@drexel.edu> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -"""Generate a static HTML dump of the current repository status""" -from libbe import cmdutil, bugdir, bug -import codecs, os, os.path, re, string, time -import xml.sax.saxutils, htmlentitydefs - -__desc__ = __doc__ - -def execute(args, manipulate_encodings=True): - """ - >>> import os - >>> bd = bugdir.SimpleBugDir() - >>> os.chdir(bd.root) - >>> execute([], manipulate_encodings=False) - >>> os.path.exists("./html_export") - True - >>> os.path.exists("./html_export/index.html") - True - >>> os.path.exists("./html_export/index_inactive.html") - True - >>> os.path.exists("./html_export/bugs") - True - >>> os.path.exists("./html_export/bugs/a.html") - True - >>> os.path.exists("./html_export/bugs/b.html") - True - >>> bd.cleanup() - """ - parser = get_parser() - options, args = parser.parse_args(args) - complete(options, args, parser) - cmdutil.default_complete(options, args, parser) - - if len(args) > 0: - raise cmdutil.UsageError, 'Too many arguments.' - - bd = bugdir.BugDir(from_disk=True, - manipulate_encodings=manipulate_encodings) - bd.load_all_bugs() - - html_gen = HTMLGen(bd, template=options.template, verbose=options.verbose, - title=options.title, index_header=options.index_header) - if options.exp_template == True: - html_gen.write_default_template(options.exp_template_dir) - return - html_gen.run(options.out_dir) - -def get_parser(): - parser = cmdutil.CmdOptionParser('be html [options]') - parser.add_option('-o', '--output', metavar='DIR', dest='out_dir', - help='Set the output path (%default)', default='./html_export') - parser.add_option('-t', '--template-dir', metavar='DIR', dest='template', - help='Use a different template, defaults to internal templates', - default=None) - parser.add_option('--title', metavar='STRING', dest='title', - help='Set the bug repository title (%default)', - default='BugsEverywhere Issue Tracker') - parser.add_option('--index-header', metavar='STRING', dest='index_header', - help='Set the index page headers (%default)', - default='BugsEverywhere Bug List') - parser.add_option('-v', '--verbose', action='store_true', - metavar='verbose', dest='verbose', - help='Verbose output, default is %default', default=False) - parser.add_option('-e', '--export-template', action='store_true', - dest='exp_template', - help='Export the default template and exit.', default=False) - parser.add_option('-d', '--export-template-dir', metavar='DIR', - dest='exp_template_dir', default='./default-templates/', - help='Set the directory for the template export (%default)') - return parser - -longhelp=""" -Generate a set of html pages representing the current state of the bug -directory. -""" - -def help(): - return get_parser().help_str() + longhelp - -def complete(options, args, parser): - for option, value in cmdutil.option_value_pairs(options, parser): - if "--complete" in args: - raise cmdutil.GetCompletions() # no positional arguments for list - -class HTMLGen (object): - def __init__(self, bd, template=None, verbose=False, encoding=None, - title="Site Title", index_header="Index Header", - ): - self.generation_time = time.ctime() - self.bd = bd - self.verbose = verbose - self.title = title - self.index_header = index_header - if encoding != None: - self.encoding = encoding - else: - self.encoding = self.bd.encoding - if template == None: - self.template = "default" - else: - self.template = os.path.abspath(os.path.expanduser(template)) - self._load_default_templates() - - if template != None: - self._load_user_templates() - - def run(self, out_dir): - if self.verbose == True: - print "Creating the html output in %s using templates in %s" \ - % (out_dir, self.template) - - bugs_active = [] - bugs_inactive = [] - bugs = [b for b in self.bd] - bugs.sort() - bugs_active = [b for b in bugs if b.active == True] - bugs_inactive = [b for b in bugs if b.active != True] - - self._create_output_directories(out_dir) - self._write_css_file() - for b in bugs: - if b.active: - up_link = "../index.html" - else: - up_link = "../index_inactive.html" - self._write_bug_file(b, up_link) - self._write_index_file( - bugs_active, title=self.title, - index_header=self.index_header, bug_type="active") - self._write_index_file( - bugs_inactive, title=self.title, - index_header=self.index_header, bug_type="inactive") - - def _create_output_directories(self, out_dir): - if self.verbose: - print "Creating output directories" - self.out_dir = self._make_dir(out_dir) - self.out_dir_bugs = self._make_dir( - os.path.join(self.out_dir, "bugs")) - - def _write_css_file(self): - if self.verbose: - print "Writing css file" - assert hasattr(self, "out_dir"), \ - "Must run after ._create_output_directories()" - self._write_file(self.css_file, - [self.out_dir,"style.css"]) - - def _write_bug_file(self, bug, up_link): - if self.verbose: - print "\tCreating bug file for %s" % self.bd.bug_shortname(bug) - assert hasattr(self, "out_dir_bugs"), \ - "Must run after ._create_output_directories()" - - bug.load_comments(load_full=True) - comment_entries = self._generate_bug_comment_entries(bug) - filename = "%s.html" % bug.uuid - fullpath = os.path.join(self.out_dir_bugs, filename) - template_info = {'title':self.title, - 'charset':self.encoding, - 'up_link':up_link, - 'shortname':self.bd.bug_shortname(bug), - 'comment_entries':comment_entries, - 'generation_time':self.generation_time} - for attr in ['uuid', 'severity', 'status', 'assigned', 'target', - 'reporter', 'creator', 'time_string', 'summary']: - template_info[attr] = self._escape(getattr(bug, attr)) - self._write_file(self.bug_file % template_info, [fullpath]) - - def _generate_bug_comment_entries(self, bug): - assert hasattr(self, "out_dir_bugs"), \ - "Must run after ._create_output_directories()" - - stack = [] - comment_entries = [] - for depth,comment in bug.comment_root.thread(flatten=False): - while len(stack) > depth: - # pop non-parents off the stack - stack.pop(-1) - # close non-parent <div class="comment... - comment_entries.append("</div>\n") - assert len(stack) == depth - stack.append(comment) - if depth == 0: - comment_entries.append('<div class="comment root">') - else: - comment_entries.append('<div class="comment">') - template_info = {} - for attr in ['uuid', 'author', 'date', 'body']: - value = getattr(comment, attr) - if attr == 'body': - save_body = False - if comment.content_type == 'text/html': - pass # no need to escape html... - elif comment.content_type.startswith('text/'): - value = '<pre>\n'+self._escape(value)+'\n</pre>' - elif comment.content_type.startswith('image/'): - save_body = True - value = '<img src="./%s/%s" />' \ - % (bug.uuid, comment.uuid) - else: - save_body = True - value = '<a href="./%s/%s">Link to %s file</a>.' \ - % (bug.uuid, comment.uuid, comment.content_type) - if save_body == True: - per_bug_dir = os.path.join(self.out_dir_bugs, bug.uuid) - if not os.path.exists(per_bug_dir): - os.mkdir(per_bug_dir) - comment_path = os.path.join(per_bug_dir, comment.uuid) - self._write_file( - '<Files %s>\n ForceType %s\n</Files>' \ - % (comment.uuid, comment.content_type), - [per_bug_dir, '.htaccess'], mode='a') - self._write_file( - comment.body, - [per_bug_dir, comment.uuid], mode='wb') - else: - value = self._escape(value) - template_info[attr] = value - comment_entries.append(self.bug_comment_entry % template_info) - while len(stack) > 0: - stack.pop(-1) - comment_entries.append("</div>\n") # close every remaining <div class="comment... - return '\n'.join(comment_entries) - - def _write_index_file(self, bugs, title, index_header, bug_type="active"): - if self.verbose: - print "Writing %s index file for %d bugs" % (bug_type, len(bugs)) - assert hasattr(self, "out_dir"), "Must run after ._create_output_directories()" - esc = self._escape - - bug_entries = self._generate_index_bug_entries(bugs) - - if bug_type == "active": - filename = "index.html" - elif bug_type == "inactive": - filename = "index_inactive.html" - else: - raise Exception, "Unrecognized bug_type: '%s'" % bug_type - template_info = {'title':title, - 'index_header':index_header, - 'charset':self.encoding, - 'active_class':'tab sel', - 'inactive_class':'tab nsel', - 'bug_entries':bug_entries, - 'generation_time':self.generation_time} - if bug_type == "inactive": - template_info['active_class'] = 'tab nsel' - template_info['inactive_class'] = 'tab sel' - - self._write_file(self.index_file % template_info, - [self.out_dir, filename]) - - def _generate_index_bug_entries(self, bugs): - bug_entries = [] - for bug in bugs: - if self.verbose: - print "\tCreating bug entry for %s" % self.bd.bug_shortname(bug) - template_info = {'shortname':self.bd.bug_shortname(bug)} - for attr in ['uuid', 'severity', 'status', 'assigned', 'target', - 'reporter', 'creator', 'time_string', 'summary']: - template_info[attr] = self._escape(getattr(bug, attr)) - bug_entries.append(self.index_bug_entry % template_info) - return '\n'.join(bug_entries) - - def _escape(self, string): - if string == None: - return "" - chars = [] - for char in string: - codepoint = ord(char) - if codepoint in htmlentitydefs.codepoint2name: - char = "&%s;" % htmlentitydefs.codepoint2name[codepoint] - #else: xml.sax.saxutils.escape(char) - chars.append(char) - return "".join(chars) - - def _load_user_templates(self): - for filename,attr in [('style.css','css_file'), - ('index_file.tpl','index_file'), - ('index_bug_entry.tpl','index_bug_entry'), - ('bug_file.tpl','bug_file'), - ('bug_comment_entry.tpl','bug_comment_entry')]: - fullpath = os.path.join(self.template, filename) - if os.path.exists(fullpath): - setattr(self, attr, self._read_file([fullpath])) - - def _make_dir(self, dir_path): - dir_path = os.path.abspath(os.path.expanduser(dir_path)) - if not os.path.exists(dir_path): - try: - os.makedirs(dir_path) - except: - raise cmdutil.UsageError, "Cannot create output directory '%s'." % dir_path - return dir_path - - def _write_file(self, content, path_array, mode='w'): - f = codecs.open(os.path.join(*path_array), mode, self.encoding) - f.write(content) - f.close() - - def _read_file(self, path_array, mode='r'): - f = codecs.open(os.path.join(*path_array), mode, self.encoding) - content = f.read() - f.close() - return content - - def write_default_template(self, out_dir): - if self.verbose: - print "Creating output directories" - self.out_dir = self._make_dir(out_dir) - if self.verbose: - print "Creating css file" - self._write_css_file() - if self.verbose: - print "Creating index_file.tpl file" - self._write_file(self.index_file, - [self.out_dir, "index_file.tpl"]) - if self.verbose: - print "Creating index_bug_entry.tpl file" - self._write_file(self.index_bug_entry, - [self.out_dir, "index_bug_entry.tpl"]) - if self.verbose: - print "Creating bug_file.tpl file" - self._write_file(self.bug_file, - [self.out_dir, "bug_file.tpl"]) - if self.verbose: - print "Creating bug_comment_entry.tpl file" - self._write_file(self.bug_comment_entry, - [self.out_dir, "bug_comment_entry.tpl"]) - - def _load_default_templates(self): - self.css_file = """ - body { - font-family: "lucida grande", "sans serif"; - color: #333; - width: auto; - margin: auto; - } - - div.main { - padding: 20px; - margin: auto; - padding-top: 0; - margin-top: 1em; - background-color: #fcfcfc; - } - - div.footer { - font-size: small; - padding-left: 20px; - padding-right: 20px; - padding-top: 5px; - padding-bottom: 5px; - margin: auto; - background: #305275; - color: #fffee7; - } - - table { - border-style: solid; - border: 10px #313131; - border-spacing: 0; - width: auto; - } - - tb { border: 1px; } - - tr { - vertical-align: top; - width: auto; - } - - td { - border-width: 0; - border-style: none; - padding-right: 0.5em; - padding-left: 0.5em; - width: auto; - } - - img { border-style: none; } - - h1 { - padding: 0.5em; - background-color: #305275; - margin-top: 0; - margin-bottom: 0; - color: #fff; - margin-left: -20px; - margin-right: -20px; - } - - ul { - list-style-type: none; - padding: 0; - } - - p { width: auto; } - - a, a:visited { - background: inherit; - text-decoration: none; - } - - a { color: #003d41; } - a:visited { color: #553d41; } - .footer a { color: #508d91; } - - /* bug index pages */ - - td.tab { - padding-right: 1em; - padding-left: 1em; - } - - td.sel.tab { - background-color: #afafaf; - border: 1px solid #afafaf; - font-weight:bold; - } - - td.nsel.tab { border: 0px; } - - table.bug_list { - background-color: #afafaf; - border: 2px solid #afafaf; - } - - .bug_list tr { width: auto; } - tr.wishlist { background-color: #B4FF9B; } - tr.minor { background-color: #FCFF98; } - tr.serious { background-color: #FFB648; } - tr.critical { background-color: #FF752A; } - tr.fatal { background-color: #FF3300; } - - /* bug detail pages */ - - td.bug_detail_label { text-align: right; } - td.bug_detail { } - td.bug_comment_label { text-align: right; vertical-align: top; } - td.bug_comment { } - - div.comment { - padding: 20px; - padding-top: 20px; - margin: auto; - margin-top: 0; - } - - div.root.comment { - padding: 0px; - /* padding-top: 0px; */ - padding-bottom: 20px; - } - """ - - self.index_file = """ - <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <head> - <title>%(title)s</title> - <meta http-equiv="Content-Type" content="text/html; charset=%(charset)s" /> - <link rel="stylesheet" href="style.css" type="text/css" /> - </head> - <body> - - <div class="main"> - <h1>%(index_header)s</h1> - <p></p> - <table> - - <tr> - <td class="%(active_class)s"><a href="index.html">Active Bugs</a></td> - <td class="%(inactive_class)s"><a href="index_inactive.html">Inactive Bugs</a></td> - </tr> - - </table> - <table class="bug_list"> - <tbody> - - %(bug_entries)s - - </tbody> - </table> - </div> - - <div class="footer"> - <p>Generated by <a href="http://www.bugseverywhere.org/"> - BugsEverywhere</a> on %(generation_time)s</p> - <p> - <a href="http://validator.w3.org/check?uri=referer">Validate XHTML</a> | - <a href="http://jigsaw.w3.org/css-validator/check?uri=referer">Validate CSS</a> - </p> - </div> - - </body> - </html> - """ - - self.index_bug_entry =""" - <tr class="%(severity)s"> - <td><a href="bugs/%(uuid)s.html">%(shortname)s</a></td> - <td><a href="bugs/%(uuid)s.html">%(status)s</a></td> - <td><a href="bugs/%(uuid)s.html">%(severity)s</a></td> - <td><a href="bugs/%(uuid)s.html">%(summary)s</a></td> - <td><a href="bugs/%(uuid)s.html">%(time_string)s</a></td> - </tr> - """ - - self.bug_file = """ - <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <head> - <title>%(title)s</title> - <meta http-equiv="Content-Type" content="text/html; charset=%(charset)s" /> - <link rel="stylesheet" href="../style.css" type="text/css" /> - </head> - <body> - - <div class="main"> - <h1>BugsEverywhere Bug List</h1> - <h5><a href="%(up_link)s">Back to Index</a></h5> - <h2>Bug: %(shortname)s</h2> - <table> - <tbody> - - <tr><td class="bug_detail_label">ID :</td> - <td class="bug_detail">%(uuid)s</td></tr> - <tr><td class="bug_detail_label">Short name :</td> - <td class="bug_detail">%(shortname)s</td></tr> - <tr><td class="bug_detail_label">Status :</td> - <td class="bug_detail">%(status)s</td></tr> - <tr><td class="bug_detail_label">Severity :</td> - <td class="bug_detail">%(severity)s</td></tr> - <tr><td class="bug_detail_label">Assigned :</td> - <td class="bug_detail">%(assigned)s</td></tr> - <tr><td class="bug_detail_label">Reporter :</td> - <td class="bug_detail">%(reporter)s</td></tr> - <tr><td class="bug_detail_label">Creator :</td> - <td class="bug_detail">%(creator)s</td></tr> - <tr><td class="bug_detail_label">Created :</td> - <td class="bug_detail">%(time_string)s</td></tr> - <tr><td class="bug_detail_label">Summary :</td> - <td class="bug_detail">%(summary)s</td></tr> - </tbody> - </table> - - <hr/> - - %(comment_entries)s - - </div> - <h5><a href="%(up_link)s">Back to Index</a></h5> - - <div class="footer"> - <p>Generated by <a href="http://www.bugseverywhere.org/"> - BugsEverywhere</a> on %(generation_time)s</p> - <p> - <a href="http://validator.w3.org/check?uri=referer">Validate XHTML</a> | - <a href="http://jigsaw.w3.org/css-validator/check?uri=referer">Validate CSS</a> - </p> - </div> - - </body> - </html> - """ - - self.bug_comment_entry =""" - <table> - <tr> - <td class="bug_comment_label">Comment:</td> - <td class="bug_comment"> - --------- Comment ---------<br/> - Name: %(uuid)s<br/> - From: %(author)s<br/> - Date: %(date)s<br/> - <br/> - %(body)s - </td> - </tr> - </table> - """ - - # strip leading whitespace - for attr in ['css_file', 'index_file', 'index_bug_entry', 'bug_file', - 'bug_comment_entry']: - value = getattr(self, attr) - value = value.replace('\n'+' '*12, '\n') - setattr(self, attr, value.strip()+'\n') |