- 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 libbe.command.UserError(
- 'Cannot create output directory "%s".' % dir_path)
- return dir_path
-
- def _write_file(self, content, path_array, mode='w'):
- return libbe.util.encoding.set_file_contents(
- os.path.join(*path_array), content, mode, self.encoding)
-
- def _read_file(self, path_array, mode='r'):
- return libbe.util.encoding.get_file_contents(
- os.path.join(*path_array), mode, self.encoding, decode=True)
-
- def write_default_template(self, out_dir):
- if self.verbose:
- print >> self.stdout, 'Creating output directories'
- self.out_dir = self._make_dir(out_dir)
- if self.verbose:
- print >> self.stdout, 'Creating css file'
- self._write_css_file()
- if self.verbose:
- print >> self.stdout, 'Creating index_file.tpl file'
- self._write_file(self.index_file,
- [self.out_dir, 'index_file.tpl'])
- if self.verbose:
- print >> self.stdout, 'Creating index_bug_entry.tpl file'
- self._write_file(self.index_bug_entry,
- [self.out_dir, 'index_bug_entry.tpl'])
- if self.verbose:
- print >> self.stdout, 'Creating bug_file.tpl file'
- self._write_file(self.bug_file,
- [self.out_dir, 'bug_file.tpl'])
- if self.verbose:
- print >> self.stdout, '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;
+ return xml.sax.saxutils.escape(string)
+
+ def _strip_email(self, string):
+ if self.strip_email:
+ name,address = email.utils.parseaddr(string)
+ if name:
+ return name
+ return address
+ return string
+
+ def _load_templates(self, template_dir=None):
+ if template_dir is not None:
+ template_dir = os.path.abspath(os.path.expanduser(template_dir))
+
+ self.template_dict = {
+##
+ 'style.css':
+"""body {
+ font-family: "lucida grande", "sans serif";
+ font-size: 14px;
+ color: #333;
+ width: auto;
+ margin: auto;
+}
+
+div.main {
+ padding: 20px;
+ margin: auto;
+ padding-top: 0;
+ margin-top: 1em;
+ background-color: #fcfcfc;
+ border-radius: 10px;
+
+}
+
+div.footer {
+ font-size: small;
+ padding-left: 20px;
+ padding-right: 20px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin: auto;
+ background: #305275;
+ color: #fffee7;
+ border-radius: 10px;
+}
+
+div.header {
+ font-size: xx-large;
+ padding-left: 20px;
+ padding-right: 20px;
+ padding-top: 10px;
+ font-weight:bold;
+ padding-bottom: 10px;
+ background: #305275;
+ color: #fffee7;
+ border-radius: 10px;
+}
+
+th.target_name {
+ text-align:left;
+ border: 1px solid;
+ border-color: #305275;
+ background-color: #305275;
+ color: #fff;
+ width: auto;
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+table {
+ border-style: solid;
+ border: 1px #c3d9ff;
+ border-spacing: 0px 0px;
+ width: auto;
+ padding: 0px;
+
+ }
+
+tb { border: 1px; }
+
+tr {
+ vertical-align: top;
+ border: 1px #c3d9ff;
+ border-style: dotted;
+ width: auto;
+ padding: 0px;
+}
+
+th {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #c3d9ff;
+ border-collapse: collapse;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+
+td {
+ border-width: 1px;
+ border-color: #c3d9ff;
+ border-collapse: collapse;
+ padding-left: 5px;
+ padding-right: 5px;
+ width: auto;
+}
+
+img { border-style: none; }
+
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+p { width: auto; }
+
+p.backlink {
+ width: auto;
+ font-weight: bold;
+}
+
+a {
+ background: inherit;
+ text-decoration: none;
+}
+
+a { color: #553d41; }
+a:hover { color: #003d41; }
+a:visited { color: #305275; }
+.footer a { color: #508d91; }
+
+/* bug index pages */
+
+td.tab {
+ padding-right: 1em;
+ padding-left: 1em;
+}
+
+td.sel.tab {
+ background-color: #c3d9ff ;
+ border: 1px solid #c3d9ff;
+ font-weight:bold;
+ border-top-left-radius: 15px;
+ border-top-right-radius: 15px;
+}
+
+td.nsel.tab {
+ border: 1px solid #c3d9ff;
+ font-weight:bold;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+}
+
+table.bug_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #c3d9ff;
+ padding: 0px;
+ width: 100%;
+ border: 1px solid #c3d9ff;
+}
+
+table.target_list {
+ padding: 0px;
+ width: 100%;
+ margin-bottom: 10px;
+}
+
+table.target_list.td {
+ border-width: 1px;
+}
+
+tr.wishlist { background-color: #DCFAFF;}
+tr.wishlist:hover { background-color: #C2DCE1; }
+
+tr.minor { background-color: #FFFFA6; }
+tr.minor:hover { background-color: #E6E696; }
+
+tr.serious { background-color: #FF9077;}
+tr.serious:hover { background-color: #E6826B; }
+
+tr.critical { background-color: #FF752A; }
+tr.critical:hover { background-color: #D63905;}
+
+tr.fatal { background-color: #FF3300;}
+tr.fatal:hover { background-color: #D60000;}
+
+td.uuid { width: 5%; border-style: dotted;}
+td.status { width: 5%; border-style: dotted;}
+td.severity { width: 5%; border-style: dotted;}
+td.summary { border-style: dotted;}
+td.date { width: 25%; border-style: dotted;}
+
+/* bug detail pages */
+
+td.bug_detail_label { text-align: right; border: none;}
+td.bug_detail { border: none;}
+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;
+}
+""",
+##
+ 'base.html':
+"""<!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 }}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset={{ charset }}" />
+ <link rel="stylesheet" href="{{ stylesheet }}" type="text/css" />
+ </head>
+ <body>
+ <div class="header">{{ header }}</div>
+ <div class="main">
+ {% block content %}{% endblock %}
+ </div>
+ <div class="footer">
+ <p>Generated by <a href="http://www.bugseverywhere.org/">
+ Bugs Everywhere</a> on {{ generation_time }}</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>
+""",
+##
+ 'bugdirs.html':
+"""{% extends "base.html" %}
+
+{% block content %}
+{% if bugdirss %}
+{% block bugdir_table %}{% endblock %}
+{% else %}
+<p>No bugdirs.</p>
+{% endif %}
+{% endblock %}
+""",
+##
+ 'index.html':
+"""{% extends "base.html" %}
+
+{% block content %}
+<table>
+ <tbody>
+ <tr>
+ <td class="{{ active_class }}"><a href="{% if index_file %}{{ index_file }}{% else %}.{% endif %}">Active Bugs</a></td>
+ <td class="{{ inactive_class }}"><a href="{{ index_file }}?type=inactive">Inactive Bugs</a></td>
+ <td class="{{ target_class }}"><a href="{{ index_file }}?type=target">Divided by target</a></td>
+ </tr>
+ </tbody>
+</table>
+{% if bugs %}
+{% block bug_table %}{% endblock %}
+{% else %}
+<p>No bugs.</p>
+{% endif %}
+{% endblock %}
+""",
+##
+ 'standard_index.html':
+"""{% extends "index.html" %}
+
+{% block bug_table %}
+<table class="bug_list">
+ <thead>
+ <tr>
+ <th>UUID</th>
+ <th>Status</th>
+ <th>Severity</th>
+ <th>Summary</th>
+ <th>Date</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for bug in bugs %}
+ {{ bug_entry.render({'bug':bug, 'dir':bug_dir(bug), 'index_file':index_file}) }}
+ {% endfor %}
+ </tbody>
+</table>
+{% endblock %}
+""",
+##
+ 'target_index.html':
+"""{% extends "index.html" %}
+
+{% block bug_table %}
+{% for target,bugs in targets %}
+<table class="target_list">
+ <thead>
+ <tr>
+ <th class="target_name" colspan="5">
+ Target: {{ target.summary|e }} ({{ target.status|e }})
+ </th>
+ </tr>
+ <tr>
+ <th>UUID</th>
+ <th>Status</th>
+ <th>Severity</th>
+ <th>Summary</th>
+ <th>Date</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for bug in bugs %}
+ {{ bug_entry.render({'bug':bug, 'dir':bug_dir(bug), 'index_file':index_file}) }}
+ {% endfor %}
+ </tbody>
+</table>
+{% endfor %}
+{% endblock %}
+""",
+##
+ 'index_bug_entry.html':
+"""<tr class="{{ bug.severity }}">
+ <td class="uuid"><a href="{{ dir }}/{{ index_file }}">{{ bug.id.user()|e }}</a></td>
+ <td class="status"><a href="{{ dir }}/{{ index_file }}">{{ bug.status|e }}</a></td>
+ <td class="severity"><a href="{{ dir }}/{{ index_file }}">{{ bug.severity|e }}</a></td>
+ <td class="summary"><a href="{{ dir }}/{{ index_file }}">{{ bug.summary|e }}</a></td>
+ <td class="date"><a href="{{ dir }}/{{ index_file }}">{{ (bug.time_string or '')|e }}</a></td>
+</tr>
+""",
+##
+ 'bug.html':
+"""{% extends "base.html" %}
+
+{% block content %}
+{{ backlinks.render({'up_link': up_link, 'index_type':index_type, 'index_file':index_file}) }}
+<h1>Bug: {{ bug.id.user()|e }}</h1>
+
+<table>
+ <tbody>
+ <tr><td class="bug_detail_label">ID :</td>
+ <td class="bug_detail">{{ bug.uuid|e }}</td></tr>
+ <tr><td class="bug_detail_label">Short name :</td>
+ <td class="bug_detail">{{ bug.id.user()|e }}</td></tr>
+ <tr><td class="bug_detail_label">Status :</td>
+ <td class="bug_detail">{{ bug.status|e }}</td></tr>
+ <tr><td class="bug_detail_label">Severity :</td>
+ <td class="bug_detail">{{ bug.severity|e }}</td></tr>
+ <tr><td class="bug_detail_label">Assigned :</td>
+ <td class="bug_detail">{{ strip_email(bug.assigned or '')|e }}</td></tr>
+ <tr><td class="bug_detail_label">Reporter :</td>
+ <td class="bug_detail">{{ strip_email(bug.reporter or '')|e }}</td></tr>
+ <tr><td class="bug_detail_label">Creator :</td>
+ <td class="bug_detail">{{ strip_email(bug.creator or '')|e }}</td></tr>
+ <tr><td class="bug_detail_label">Created :</td>
+ <td class="bug_detail">{{ (bug.time_string or '')|e }}</td></tr>
+{% if target %}
+ <tr><td class="bug_detail_label">Target :</td>
+ <td class="bug_detail"><a href="../../{{ bug_dir(target) }}/{{ index_file }}">{{ target.summary }}</a></td></tr>
+{% endif %}
+ <tr><td class="bug_detail_label">Summary :</td>
+ <td class="bug_detail">{{ bug.summary|e }}</td></tr>
+ </tbody>
+</table>
+
+<hr/>
+
+{% if comments %}
+{% for depth,comment in comments %}
+{% if depth == 0 %}
+<div class="comment root" id="C{{ comment_dir(comment) }}">
+{% else %}
+<div class="comment" id="C{{ comment_dir(comment) }}">
+{% endif %}
+{{ comment_entry.render({
+ 'depth':depth, 'bug': bug, 'comment':comment, 'comment_dir':comment_dir,
+ 'format_body': format_body, 'div_close': div_close,
+ 'strip_email': strip_email}) }}
+{{ div_close(depth) }}
+{% endfor %}
+{% if comments[-1][0] > 0 %}
+{{ div_close(0) }}
+{% endif %}
+{% else %}
+<p>No comments.</p>
+{% endif %}
+{{ backlinks.render({'up_link': up_link, 'index_type': index_type}) }}
+{% endblock %}
+""",
+##
+ 'bug_backlinks.html':
+"""<p class="backlink"><a href="{{ up_link }}">Back to {{ index_type }} Index</a></p>
+<p class="backlink"><a href="../../{{ index_file }}?type=target">Back to Target Index</a></p>
+""",
+##
+ 'bug_comment_entry.html':
+"""<table>
+ <tbody>
+ <tr>
+ <td class="bug_comment_label">Comment:</td>
+ <td class="bug_comment">
+ --------- Comment ---------<br/>
+ ID: {{ comment.uuid }}<br/>
+ Short name: {{ comment.id.user() }}<br/>
+ From: {{ strip_email(comment.author or '')|e }}<br/>
+ Date: {{ (comment.date or '')|e }}<br/>
+ <br/>
+ {{ format_body(bug, comment) }}
+ </td>
+ </tr>
+ </tbody>
+</table>
+""",